Files
Mark ba5474c54f docs(01-11): close as spike-pivot — SUMMARY landed, AMENDMENT-A deleted, pivots to 01-13
Closes Plan 01-11 honestly per GSD spike-pivot pattern. Original
Approach A (Puppeteer sw.evaluate per RESEARCH §1+6) empirically
falsified across Wave 3 execution + feasibility research. Approach B
(extension-internal-page harness + offscreen synthetic stream) proven
via c647f61 prototype; full implementation moves to Plan 01-13.

What this commit does:
- ADDS 01-11-SUMMARY.md (spike-then-pivot framing per GSD artifact-
  types.md PLAN→SUMMARY lifecycle; captures retained infrastructure,
  falsified hypotheses, working prototype, bridge to 01-13)
- REVERTS frontmatter amendment block in 01-11-PLAN.md; replaces with
  closed_as/pivoted_in/closure_note pointing at SUMMARY + 01-13
- DELETES 01-11-PLAN-AMENDMENT-A.md (improvised artifact type — not
  recognized in GSD artifact-types.md; content folded into SUMMARY)

Lesson for orchestrator (captured in SUMMARY §Architectural Notes):
when a plan attempts an approach that proves infeasible, the right
move is honest SUMMARY + new plan, NOT in-place rewrite + AMENDMENT
artifact. The project's own pattern (01-08, 01-09, 01-10, 01-11
added mid-phase as new work surfaced) confirms add-new-plan-when-
scope-shifts is the established pattern.

Plan 01-09 closure via harness PASS NOT achieved by 01-11; still
requires operator UAT pending Plan 01-13 landing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 14:02:38 +02:00

14 KiB

phase: 01-stabilize-video-pipeline plan: 11 subsystem: test-infrastructure tags: - puppeteer - uat - harness - e2e - mv3-extension - spike-then-pivot - architectural-falsification - test-bundle-separation requires: - 01-08 (Plan 01-08 SUMMARY + Tier-1 SW-bundle-import gate GREEN) - 01-09 (Bug A + Bug B fixes landed; the regression-catch targets) provides: - test-build infrastructure (vite.test.config.ts + dist-test/ + npm scripts build:test/test:uat) - Tier-1 hook-leak grep gate (no-test-hooks-in-prod-bundle.test.ts enforces __mokoshTest absent from production dist/) - empirical falsification of original RESEARCH §6 (MV3 SW blocks dynamic import — verified) - working prototype validating Approach B architecture (commit c647f61; A6 5/5 GREEN; Bug-B regression rewind verified) - offscreen-side test hooks scaffolding (src/test-hooks/offscreen-hooks.ts with installFakeDisplayMedia) - input for Plan 01-13 (carries the proven architecture forward) affects: - package.json (puppeteer + tsx + @types/node devDeps) - vite.test.config.ts (NEW — extends production config with mode='test', outDir='dist-test') - tsconfig.json (test path inclusion) - src/test-hooks/sw-hooks.ts (NEW — BROKEN architecturally; deleted in 01-13) - src/test-hooks/offscreen-hooks.ts (NEW — works; carried to 01-13) - src/test-hooks/types.ts (NEW — type contracts; carried to 01-13) - src/background/index.ts (Wave 1 added dynamic import — silently kills SW per MV3 limit; reverted in 01-13) - src/offscreen/recorder.ts (Wave 1 added dynamic import — works because offscreen IS a DOM document) - tests/uat/lib/.ts (Wave 2 popup-bridge scaffolding — wrong architecture; replaced in 01-13) - tests/uat/harness.test.ts (Wave 2 skeleton with A0 GREEN — rewired in 01-13) - tests/uat/prototype/.ts (NEW — proves Approach B; promoted to production paths in 01-13) - tests/background/no-test-hooks-in-prod-bundle.test.ts (NEW Tier-1 gate; permanent) tech-stack: added: - puppeteer 25.x (extension automation driver) - tsx (TS runner for harness scripts) - "@types/node" patterns: - Two-bundle separation (production dist/ vs test dist-test/ via Vite mode flag) - MOKOSH_UAT Vite define-token gating (replaces import.meta.env.MODE='test' which collides with vitest) - Tier-1 grep gate as production-bundle hygiene contract - Synthetic MediaStream via Canvas.captureStream() (bypasses getDisplayMedia picker in test) - track.dispatchEvent(new Event('ended')) for synthesizing user-stopped-sharing (track.stop() does NOT fire ended per W3C spec) falsified: - "MV3 SW supports dynamic import gated by import.meta.env.MODE" — empirically wrong; await import() in SW never resolves; SW silently dies - "Puppeteer sw.evaluate exposes extension chrome." — empirically wrong; only chrome.{loadTimes,csi} exposed (CDP treats MV3 SW as generic worker) - "Popup-bridge architecture sufficient for SW state queries" — empirically wrong; setPopup state changes don't propagate to popup chrome.action.getPopup queries reliably - "Headless --auto-select-desktop-capture-source triggers getDisplayMedia resolution" — empirically wrong in --headless=new key-files: created: - vite.test.config.ts - src/test-hooks/sw-hooks.ts (deleted in 01-13) - src/test-hooks/offscreen-hooks.ts (carried to 01-13) - src/test-hooks/types.ts (carried to 01-13) - tests/uat/lib/launch.ts + extension.ts + sw.ts + offscreen.ts + assertions.ts + zip.ts (Wave 2; popup-bridge — replaced in 01-13) - tests/uat/harness.test.ts (Wave 2 skeleton; rewired in 01-13) - tests/uat/prototype/extension-page-harness.html + .ts (PROVEN ARCHITECTURE; promoted in 01-13) - tests/uat/prototype/a6.test.ts (Puppeteer driver template; promoted in 01-13) - tests/background/no-test-hooks-in-prod-bundle.test.ts (Tier-1 grep gate; permanent) modified: - package.json (puppeteer + tsx + @types/node devDeps; build:test + test:uat scripts) - tsconfig.json (test paths) - src/background/index.ts (Wave 1 dynamic import — reverted in 01-13) - src/offscreen/recorder.ts (Wave 1 dynamic import — preserved in 01-13) decisions: - Approach A (Puppeteer sw.evaluate per RESEARCH §1+6) attempted across Waves 1-3; empirically falsified by both Wave 3 execution (commit f44ca3a) + dedicated feasibility research subagent (Verdict-A research, prototype c647f61). - Approach B (extension-internal-page harness + offscreen-side synthetic MediaStream + chrome.runtime.sendMessage bridge) validated via prototype; industry-standard pattern per MetaMask, eyeo, Chrome MV3 official testing docs convergence. - Wave 0 infrastructure retained as foundation for 01-13. Waves 1-2 partially retained — offscreen-hooks + types + harness.test.ts skeleton + Tier-1 gate kept; sw-hooks + popup-bridge lib deleted/replaced. - Spike-then-pivot framing: 01-11 reads retroactively as a SPIKE that delivered useful infrastructure + falsified the original architectural hypothesis + produced a working prototype. Full 14-assertion harness implementation defers to Plan 01-13. - MOKOSH_UAT Vite define-token chosen over import.meta.env.MODE='test' (per original RESEARCH §6 sketch) because vitest defaults MODE='test' and would have activated hooks during unit tests, clobbering 8 existing chrome. vi.fn() mocks. - Tier-1 grep gate (no-test-hooks-in-prod-bundle.test.ts) is permanent baseline — verifies __mokoshTest absent from production dist/ on every test run. metrics: duration: "~8h cumulative (planning + research + 2 executor attempts + feasibility research + prototype + pivot)" completed: "2026-05-18 (closed as spike-then-pivot; full harness deferred to 01-13)" task_count: "Wave 0 (T1) complete; Waves 1-4 partial or not delivered as originally planned" files_modified: "18 created + 4 modified (some files will be deleted in 01-13 baseline)" net_loc: "~1500+ (test infrastructure + prototype + Tier-1 gate)" pivot: "yes — full harness implementation deferred to Plan 01-13"

Phase 1 Plan 11: UAT Harness — Approach A Spike (Pivoted to 01-13)

Attempted to retire operator UAT for functional gates via Puppeteer-driven MV3 extension automation. Approach A (Puppeteer sw.evaluate per original RESEARCH §1+6) empirically falsified across Wave 3 + dedicated feasibility research. Approach B (extension-internal-page harness + offscreen-side synthetic stream) validated via working prototype. Full harness implementation deferred to Plan 01-13.

One-Liner

Approach A spike: shipped two-bundle infrastructure + Tier-1 grep gate + offscreen-hooks scaffolding + empirical falsification of "MV3 SW supports dynamic import" hypothesis + working prototype of Approach B (extension-internal-page harness). Full 14-assertion implementation pivots to Plan 01-13.

What Landed

Infrastructure (Wave 0, commit 96fa8e8) — Retained for 01-13

  • vite.test.config.ts — extends production config with mode='test' + outDir='dist-test'
  • package.json scripts build:test + test:uat (orchestrate build + harness)
  • tests/background/no-test-hooks-in-prod-bundle.test.ts — Tier-1 grep gate (A0 assertion); permanent in test suite
  • Bonus: 0cd50fd — imported Bug B recovery-flow debug record from prior session (cleanup that was overdue)

Hooks scaffolding (Wave 1, commit cb1a729) — Partially Retained

File Status Reason
src/test-hooks/offscreen-hooks.ts RETAINED + EXTENDED in 01-13 Offscreen IS a DOM document; dynamic import works there
src/test-hooks/types.ts RETAINED in 01-13 Type contracts valid regardless of arch
src/test-hooks/sw-hooks.ts DELETED in 01-13 MV3 SW blocks dynamic import — silently kills SW
src/background/index.ts Wave 1 import block REVERTED in 01-13 Same MV3 SW limit

Harness scaffolding (Wave 2, commit dbd977c) — Mostly Replaced

File Status Reason
tests/uat/lib/*.ts (popup-bridge) DELETED/REPLACED in 01-13 Wrong architecture; setPopup state changes don't propagate reliably
tests/uat/harness.test.ts skeleton REWIRED in 01-13 Top-level structure stays; assertions point at new extension-page architecture
A0 Tier-1 gate (GREEN here) RETAINED — permanent Grep gate is architecture-agnostic

Working prototype (commit c647f61) — PROVES Approach B; Promoted in 01-13

  • tests/uat/prototype/extension-page-harness.html + .ts — extension-internal page (privileged chrome.* access) drives assertions via direct chrome.* calls + chrome.runtime.sendMessage bridge
  • tests/uat/prototype/a6.test.ts — Puppeteer driver template
  • A6 assertion (Bug B regression catch): 5/5 GREEN
  • Bug-B regression rewind verified: if (false) patch on src/background/index.ts:759 turns 4/5 RED; restoring turns 5/5 GREEN
  • Total prototype runtime: ~7s end-to-end

Empirical Falsifications (Research Output)

These were the original RESEARCH §1 + §6 hypotheses. All falsified by execution + feasibility research:

  1. MV3 SW blocks dynamic import. await import(...) in src/background/index.ts silently never resolves; SW dies. Verified across two independent investigations. Cited: Chromium es_modules.md + w3c/webextensions#212.
  2. Puppeteer WebWorker.evaluate against MV3 SW only exposes chrome.{loadTimes, csi} — NOT extension chrome.* API. CDP-level abstraction treats MV3 SW as a generic worker.
  3. Popup-bridge chrome.action.setPopup state changes don't propagate to popup-side chrome.action.getPopup queries within waitable timeframes.
  4. Headless --auto-select-desktop-capture-source doesn't trigger getDisplayMedia resolution in --headless=new for screen-capture sources.

Test Counts

  • Before plan: 83/83 vitest GREEN
  • After Wave 0 (96fa8e8): 84/84 GREEN (+1 Tier-1 grep gate)
  • After Wave 1 (cb1a729): 89/89 GREEN (+5 hook contract tests)
  • After Wave 2 (dbd977c): 89/89 GREEN (popup-bridge tests stubbed RED-by-design per plan; A0 GREEN)
  • After prototype (c647f61): 89/89 vitest unchanged; prototype runs out-of-band via npx tsx tests/uat/prototype/a6.test.ts (5/5 GREEN, ~7s)
  • End of plan: 89/89 vitest GREEN; production bundle hook-free (Tier-1 gate enforces)

Deviations from Plan

The plan as written assumed Approach A architecture would work. Across Wave 3 attempted execution + dedicated feasibility research, Approach A architecturally infeasible. Pivoted to Approach B per prototype.

Plan 01-09 closure via harness PASS (the original Task 5 redirect target) NOT achieved — Plan 01-09 still requires operator UAT pending Plan 01-13 landing.

Architectural Notes Worth Carrying Forward

  • Drive Chrome FROM INSIDE the extension, not FROM OUTSIDE. MetaMask/eyeo/Chrome-docs convergence: extension-internal pages have FULL chrome.* API access as privileged contexts. Puppeteer drives the page; page drives chrome.*. SW state queried indirectly via chrome.runtime.sendMessage.
  • track.dispatchEvent(new Event('ended')) for synthesizing user-stopped-sharing, NOT track.stop() (which does NOT fire ended per W3C spec + empirical Chrome 148 verification).
  • __MOKOSH_UAT__ Vite define-token over import.meta.env.MODE='test' — vitest defaults MODE='test', which would activate hooks during unit tests and clobber chrome.* mocks.
  • MV3 SW blocks dynamic import — never use await import(...) in src/background/index.ts. Test-mode SW augmentation must be done via build-time inclusion (Vite plugin) or eager static import, not runtime import gate.
  • Pages CAN call chrome.offscreen.createDocument — confirmed empirically. Chrome docs don't say this explicitly. Architectural unlock for the prototype.
  • Spike-then-pivot framing is a legitimate plan outcome — when a plan attempts an approach that proves infeasible, the right move is honest SUMMARY + new plan, not in-place rewrite + AMENDMENT artifact. (Lesson for the orchestrator.)

Self-Check: PARTIAL — Closed as Spike-Pivot

  • FOUND: vite.test.config.ts (Wave 0)
  • FOUND: tests/background/no-test-hooks-in-prod-bundle.test.ts (Tier-1 gate, A0 GREEN)
  • FOUND: src/test-hooks/offscreen-hooks.ts (carries to 01-13)
  • FOUND: src/test-hooks/types.ts (carries to 01-13)
  • FOUND: tests/uat/prototype/extension-page-harness.{html,ts}
  • FOUND: tests/uat/prototype/a6.test.ts (5/5 GREEN)
  • FOUND: tests/uat/lib/*.ts (wrong arch — to delete/replace in 01-13)
  • FOUND: src/test-hooks/sw-hooks.ts (broken — to delete in 01-13)
  • FOUND: 96fa8e8 (Wave 0 infrastructure)
  • FOUND: 0cd50fd (bonus debug record import)
  • FOUND: cb1a729 (Wave 1 — partial retained; sw-hooks broken)
  • FOUND: dbd977c (Wave 2 — popup-bridge wrong arch)
  • FOUND: f44ca3a (Wave 3 partial — abandoned)
  • FOUND: c647f61 (prototype — proves Approach B; PROVENANCE for 01-13)
  • NOT-DELIVERED: A1-A13 functional assertions (deferred to 01-13)
  • NOT-DELIVERED: Plan 01-09 closure via harness PASS (still requires operator UAT pending 01-13)

Known Stubs (To Be Resolved in 01-13)

  • tests/uat/lib/*.ts — popup-bridge architecture stubs from Wave 2 dbd977c. Wrong architecture; 01-13 deletes or rewrites.
  • src/test-hooks/sw-hooks.ts + dynamic-import block in src/background/index.ts — BROKEN (MV3 SW limit). 01-13 deletes both.
  • tests/uat/prototype/* — proof-of-concept code, not production-quality. 01-13 promotes to production paths under tests/uat/ proper.

Bridge to Plan 01-13

Plan 01-13 implements full UAT harness via Approach B (extension-internal-page architecture). Inputs:

  • This SUMMARY (pivot rationale + retained infrastructure + falsified hypotheses)
  • Plan 01-11-RESEARCH.md (original research; partially historical now)
  • Feasibility research findings (Verdict-A architecture details, embedded in c647f61 commit body + this SUMMARY)
  • Prototype files (tests/uat/prototype/*, src/test-hooks/offscreen-hooks.ts, vite.test.config.ts)
  • The 14-assertion scope from original Plan 01-11 PLAN.md

01-13 will land all 14 assertions reliably, close Plan 01-09 functional contract via harness PASS, and become the standard closure gate for future phases.