--- phase: 01-stabilize-video-pipeline plan: 14 subsystem: offscreen-recorder tags: - mv3-extension - getDisplayMedia - monitorTypeSurfaces - picker-friction-reduction - uat - harness - w3c-screen-capture status: complete requires: [01-09, 01-10, 01-11, 01-13] provides: - monitorTypeSurfaces:'include' top-level constraint on production getDisplayMedia call - UAT harness A23 picker-narrowing regression gate - Tier-1 grep gate inventory extension (2 new forbidden strings; total 12) affects: - src/offscreen/recorder.ts (getDisplayMedia constraints object — picker UI behavior) - operator picker dialog UX (Chrome >= 119 only — graceful no-op on older Chrome) tech-stack: added: [] patterns: - "Top-level DisplayMediaStreamOptions member (W3C spec §6.1) — NOT nested in MediaTrackConstraints" - "Typed widening cast extended in lockstep with the new field (no `as any`)" - "Bridge-op route for harness inspection (mirrors get-display-surface / get-segment-count)" - "FORBIDDEN_HOOK_STRINGS lockstep across unit-gate + UAT A0 mirror" key-files: created: [] modified: - src/offscreen/recorder.ts - src/test-hooks/offscreen-hooks.ts - tests/offscreen/display-surface-constraint.test.ts - tests/uat/extension-page-harness.ts - tests/uat/lib/harness-page-driver.ts - tests/uat/harness.test.ts - tests/background/no-test-hooks-in-prod-bundle.test.ts decisions: - "src/test-hooks/types.ts NOT modified — the new lastGetDisplayMediaConstraints cell + get-last-getDisplayMedia-constraints op are module-internal to offscreen-hooks.ts; bridge dispatches on op string match, not surface method. The MokoshTestSurface cross-cast in offscreen-hooks.ts does not need to widen for this plan. Plan Step 3 explicitly delegated this read to the implementer." - "Test count drift accepted as positive deviation (98/98 → 100/100, +2 GREEN, 0 RED). The +2 are the 2 new parametrized FORBIDDEN_HOOK_STRINGS tests for the new symbols, both PASS. The plan's '98/98 preserved' clause was written before recognizing that the parametrized loop spawns one `it()` per new string." - "Key ordering across source + test expectation: { video: {...}, monitorTypeSurfaces: 'include', audio: false } — sibling between video: and audio:. Source change AND test-expectation update use the identical ordering." metrics: duration_minutes: 49 duration_seconds: 2957 tasks_completed: 1 files_modified: 7 files_created: 0 lines_added: 228 lines_deleted: 13 completed_date: 2026-05-19 commits: atomic_feat: b467123 revision_linkage: - "Plan revised once after plan-checker flagged B-01-14-01 (revision commit 433ee28; 2026-05-19). The original plan's must_haves truth #5 understated baseline regression risk by omitting Step 1b lockstep test-expectation update. The revision added Step 1b (display-surface-constraint.test.ts:223-226 update) and corresponding frontmatter/artifacts/key-link entries." ceremony_note: "Canonical GSD ceremony — plan (41c1f7e) → checker (B-01-14-01) → revision (433ee28) → executor (this commit b467123) → SUMMARY (this file). Replaces the retired AMENDMENT-A.md improvisation path per 01-11-SUMMARY.md Architectural Notes." architectural_delta: "ZERO new permissions; ZERO new manifest changes; ZERO new web_accessible_resources; new internal test-hook surface (lastGetDisplayMediaConstraints cell + get-last-getDisplayMedia-constraints op) gated identically to Plan 01-13's hook inventory via __MOKOSH_UAT__ Vite define-token dead-branch tree-shake." --- # Phase 01 Plan 14: monitorTypeSurfaces Picker Narrowing Summary **One-liner:** Shipped W3C Screen Capture `monitorTypeSurfaces: 'include'` as a top-level constraint on the production `getDisplayMedia` call site (Chrome ≥ 119 narrows the operator picker to monitor surfaces only — no Window/Chrome-Tab panes), with UAT harness A23 regression gate + Tier-1 grep-gate lockstep extension covering the new internal hook surface. ## What Shipped 1. **Source change** — `src/offscreen/recorder.ts:270-292` (commit `b467123`): added `monitorTypeSurfaces: 'include'` as a TOP-LEVEL `DisplayMediaStreamOptions` sibling of `video:` (NOT nested inside `video:` per W3C Screen Capture spec §6.1). The typed widening cast at lines 287-289 was extended in lockstep to include `monitorTypeSurfaces: 'include'` so the explicit-typing contract is preserved (no `as any`). The D-15 post-grant validation block (recorder.ts:294-307) is **unchanged** — belt-and-suspenders chain preserved. 2. **Lockstep test-expectation update** — `tests/offscreen/display-surface-constraint.test.ts:223-228`: Test 1's strict-deep-equality `toHaveBeenCalledWith` expectation grew from `{ video: {...}, audio: false }` to `{ video: {...}, monitorTypeSurfaces: 'include', audio: false }`. Same key ordering as the source change (`video:` → `monitorTypeSurfaces:` → `audio:`). The test author's "no objectContaining" discipline is honored (the comment at lines 221-225 was extended to mention Plan 01-14's addition; the strict deep-equality contract remains). Both source + test changes landed in the same atomic commit, so the 98/98 baseline never crossed a commit boundary in RED state. 3. **Harness wiring for A23** — `src/test-hooks/offscreen-hooks.ts`: - New module-scoped cell: `let lastGetDisplayMediaConstraints: DisplayMediaStreamOptions | null = null` (immediately after the existing fake-state cells, with docstring). - `installFakeDisplayMedia`'s `fakeGetDisplayMedia` closure: renamed `_constraints` → `constraints` and added `lastGetDisplayMediaConstraints = constraints ?? null;` as the first line. - New `__mokoshOffscreenQuery` dispatcher op: `'get-last-getDisplayMedia-constraints'` between `get-display-surface` and `get-segment-count`. Response: `{ constraints: lastGetDisplayMediaConstraints }`. Defensive try/catch mirroring the existing dispatcher pattern. 4. **Harness page assertion** — `tests/uat/extension-page-harness.ts:1906-1989`: new `assertA23()` mirroring `assertA3` (bridge query → 2-check `AssertionResult`): - A23.1: constraints object recorded by fakeGetDisplayMedia (non-null) → PASS - A23.2: `constraints.monitorTypeSurfaces === 'include'` (W3C spec §6.1; Chrome ≥ 119 picker narrowing) → PASS - `Window.__mokoshHarness` declaration + runtime export + status-bar text + console.log updated. 5. **Harness driver** — `tests/uat/lib/harness-page-driver.ts:996-1023`: exported `driveA23(page)` mirroring the `driveA14` page.evaluate wrapper shape. Read-only — no side effects, no new dispatch. 6. **Harness orchestrator** — `tests/uat/harness.test.ts`: - Header comment extended to describe A23 as the final functional assertion (post-A14; read-only inspection of A2-captured constraints). - `FORBIDDEN_HOOK_STRINGS` (line 85) extended with `lastGetDisplayMediaConstraints` and `get-last-getDisplayMedia-constraints`. - Import block extended with `driveA23` from `./lib/harness-page-driver`. - `drivers` array appended with `{ name: 'A23', drive: driveA23 }` after the A14 entry (with explanatory comment). - `Total = drivers.length + 1` comment updated to mention A23. - Orchestrator stdout updated: `Plan 01-13 + 01-14`, architecture line mentions `A1..A14, A23`. 7. **Unit-level grep gate** — `tests/background/no-test-hooks-in-prod-bundle.test.ts`: header comment extended with the 2 new strings (Total: 12 surface strings — was 10). `FORBIDDEN_HOOK_STRINGS` (line 105) extended in lockstep with the UAT A0 list. **Atomic commit hash:** `b467123` — single commit, 7 files, +228 / -13 lines. ## Verification Evidence | Gate | Pre-Plan Baseline | Post-Plan Result | Status | |------|-------------------|------------------|--------| | `npx tsc --noEmit` | exit 0 (clean) | exit 0 (clean) | GREEN | | `npm run build` | exit 0 | exit 0 | GREEN | | `npm run build:test` | exit 0 | exit 0 | GREEN | | `npm test` | 98/98 | **100/100** | GREEN (+2 via new parametrized FORBIDDEN_HOOK_STRINGS tests; both PASS) | | `npm run test:uat` | 15/15 (A1..A14 + A0) | **16/16** (+A23) | GREEN | | Production bundle hook-free (spot-check) | clean | clean | GREEN — zero occurrences of `lastGetDisplayMediaConstraints` or `get-last-getDisplayMedia-constraints` in `dist/` | | Production bundle ships `monitorTypeSurfaces` | n/a | present in `dist/assets/offscreen-*.js` | EXPECTED — operator-facing picker hint ships in prod | **A23 evidence (from UAT harness run output):** ``` --- A23 --- [harness-step] Step 1: bridge query 'get-last-getDisplayMedia-constraints' [harness-step] Step 1 result: {"constraints":{"video":{"displaySurface":"monitor","cursor":"always"},"monitorTypeSurfaces":"include","audio":false}} ======================================================================== A23 — getDisplayMedia called with monitorTypeSurfaces:'include' (Plan 01-14 picker narrowing): PASS Checks: [PASS] A23.1: constraints object recorded by fakeGetDisplayMedia (non-null) expected: "non-null DisplayMediaStreamOptions" actual: "{\"video\":{\"displaySurface\":\"monitor\",\"cursor\":\"always\"},\"monitorTypeSurfaces\":\"include\",\"audio\":false}" [PASS] A23.2: constraints.monitorTypeSurfaces === 'include' (W3C spec §6.1; Chrome ≥ 119 picker narrowing) expected: "include" actual: "include" ======================================================================== UAT harness: 16/16 assertions passed ``` The recorded constraints object shows the **exact production call shape** with the new sibling field — confirming the source change reached the call site through both the typed widening cast and the JavaScript runtime serialization across the production → offscreen-hooks fake → bridge dispatcher → harness assertion path. ## Research Linkage This plan ships the recommendation from Plan 01-10 RESEARCH §5 + §Pitfall-5 + §Environment-Availability: *"Add `monitorTypeSurfaces: 'include'` to the offscreen `getDisplayMedia` constraints. Single-line, zero-risk change that removes visual noise from the picker."* The Chrome ≥ 119 feature-gate per Plan 01-10 RESEARCH §Environment-Availability matches the target Chrome version for this extension. Authoritative references encoded into the commit body + source comments: - W3C Screen Capture §6.1 DisplayMediaStreamOptions: - Chrome screen-sharing-controls (Chrome 119+): ## Decision Linkage - **D-01** (whole-desktop only via getDisplayMedia; reject window/tab surfaces): `monitorTypeSurfaces: 'include'` is the picker-UI realization of D-01's intent. The operator's choice space is narrowed at the dialog level rather than catching wrong selections post-grant. - **D-15** (post-grant validation): unchanged. The recorder.ts:294-307 block remains the actual enforcement — `monitorTypeSurfaces` is a HINT-class constraint (per spec semantics; older Chrome / managed-policy / DevTools can override). Belt (picker-UI narrowing) + suspenders (post-grant tear-down with `wrong-display-surface` error code) chain preserved. - **D-13** (segment rotation 3 × 10 s = 30 s): unchanged. - **D-17** (long-lived port keepalive): unchanged. ## Ceremony Note This plan replaces the prior AMENDMENT-A.md improvisation path that was retired per `.planning/phases/01-stabilize-video-pipeline/01-11-SUMMARY.md` Architectural Notes. Canonical GSD ceremony (plan → checker → executor → SUMMARY) landed: 1. Plan creation: commit `41c1f7e` (2026-05-19). 2. Plan-checker review: BLOCKER B-01-14-01 raised — must_haves truth #5 understated baseline regression risk by omitting Step 1b lockstep test-expectation update. 3. Plan revision: commit `433ee28` (2026-05-19) — added Step 1b (display-surface-constraint.test.ts:223-226 update) + corresponding frontmatter/artifacts/key-link entries. 4. Executor (this commit `b467123`, 2026-05-19): single atomic commit for the entire 1-task plan. 5. SUMMARY (this file): canonical closure artifact. The lesson encoded by B-01-14-01: **strict-deep-equality test expectations are tightly coupled to the source-call shape**; any plan that mutates the source object MUST mutate the expectation in the same atomic commit, or the baseline crosses a RED boundary at commit-time. The checker caught this before any harm; the executor honored the same-commit guarantee. ## Revision Linkage The plan was revised once after the plan-checker flagged BLOCKER B-01-14-01 (commit `433ee28`, 2026-05-19). The revision added Step 1b (lockstep test-expectation update) + corresponding frontmatter/file/key-link entries to the plan. Without the revision, executing the original plan as-written would have dropped the vitest baseline from 98/98 GREEN to 97/98 RED on the strict-deep-equality assertion at `tests/offscreen/display-surface-constraint.test.ts:223-226`. The single-commit landing in this executor pass restored the same-commit guarantee. ## Architectural Delta - **ZERO** new permissions - **ZERO** new manifest changes - **ZERO** new web_accessible_resources entries - **ZERO** new dependencies (no new npm packages, no new types) - New internal test-hook surface (`lastGetDisplayMediaConstraints` cell + `get-last-getDisplayMedia-constraints` bridge op) gated identically to Plan 01-13's hook inventory via the `__MOKOSH_UAT__` Vite define-token dead-branch tree-shake. Tier-1 grep gate (unit-level + UAT A0) extended in lockstep to enforce. ## What Did Not Change - D-15 post-grant validation block (recorder.ts:294-307) — unchanged - Plan 01-09 toolbar `onClicked` chain — unchanged - Plan 01-10 welcome-page architecture — unchanged (scope reserved for the next-up plan) - Plan 01-12 design-integration scope — unchanged (scope reserved for the next-up plan) - Plan 01-13 harness architecture — the 14 existing assertions (A1..A14) are unmodified; A23 is purely additive (appended after the A14 driver in `drivers` array) - `src/test-hooks/types.ts` — unchanged (decision documented above; the new cell + op are module-internal to offscreen-hooks.ts) - `chrome.runtime` message contracts — unchanged (new bridge op uses the existing `__mokoshOffscreenQuery` envelope) - Vite build configuration — unchanged (the existing `__MOKOSH_UAT__` define-token tree-shake handles the new surface automatically) ## Deviations from Plan ### Auto-fixed Issues **None.** No bugs encountered. No CLAUDE.md violations. No missing critical functionality. No blocking issues. The plan's `` block + `` criteria mapped 1-to-1 to the actual execution path. ### Test count drift (benign positive deviation) - **Found during:** Step 9 (`npm test` post-commit) - **Issue:** Plan predicted "98/98 GREEN preserved"; actual count is 100/100. - **Root cause:** The unit-level grep gate at `tests/background/no-test-hooks-in-prod-bundle.test.ts` uses a parametrized `for (const needle of FORBIDDEN_HOOK_STRINGS) { it(...) }` loop (line 254-278) — each entry in the list spawns one independent `it()` test. Adding 2 strings to the inventory adds 2 tests. The plan's truth #5 wrote "98/98 GREEN" without noticing this arithmetic. - **Action taken:** None (no fix required). All 100 tests are GREEN; the 2 new tests assert the production bundle is hook-free for the new symbols (`lastGetDisplayMediaConstraints` and `get-last-getDisplayMedia-constraints`) — both PASS. The plan's spirit ("baseline preserved") is honored; the arithmetic just shifted up. - **Documented in:** decisions[] in this SUMMARY's frontmatter. ### Authentication Gates None encountered. The plan's `` chain (`npm run build`, `npx tsc --noEmit`, `npm test`, `npm run test:uat`) is fully autonomous (Chrome launches headlessly via puppeteer; no operator interaction). ## Threat Surface Scan No new threat surface introduced beyond what the plan's `` block already documents. The 6 STRIDE entries (T-01-14-01..T-01-14-06) cover the full delta. T-01-14-04 (Information Disclosure via leaked hook surface) is **mitigated** by the Tier-1 grep gate extension (verified GREEN). No threat flags to surface. ## Known Stubs None. No placeholder text, no hardcoded empty values, no TODO/FIXME markers introduced. Every new code path is fully wired to a real data source. ## Files Modified Summary | File | LoC delta | Purpose | |------|-----------|---------| | `src/offscreen/recorder.ts` | +16 / -0 | Source change + extended typed widening cast + explanatory comment | | `src/test-hooks/offscreen-hooks.ts` | +54 / -5 | Module cell + capture in fake + dispatcher op + dispatcher comment | | `tests/offscreen/display-surface-constraint.test.ts` | +5 / -1 | Strict-deep-equality expectation lockstep update | | `tests/uat/extension-page-harness.ts` | +77 / -4 | `assertA23` impl + global decl + runtime export + status/log | | `tests/uat/lib/harness-page-driver.ts` | +27 / -0 | `driveA23` page.evaluate wrapper | | `tests/uat/harness.test.ts` | +26 / -6 | FORBIDDEN inventory + import + drivers + comments | | `tests/background/no-test-hooks-in-prod-bundle.test.ts` | +18 / -2 | FORBIDDEN inventory lockstep + header comment | | **Total** | **+228 / -13** | **7 files, 1 atomic commit** | ## Self-Check: PASSED **File-existence verification:** - `src/offscreen/recorder.ts` — FOUND (modified per commit b467123) - `src/test-hooks/offscreen-hooks.ts` — FOUND (modified) - `tests/offscreen/display-surface-constraint.test.ts` — FOUND (modified) - `tests/uat/extension-page-harness.ts` — FOUND (modified) - `tests/uat/lib/harness-page-driver.ts` — FOUND (modified) - `tests/uat/harness.test.ts` — FOUND (modified) - `tests/background/no-test-hooks-in-prod-bundle.test.ts` — FOUND (modified) **Commit-existence verification:** - `b467123` — FOUND in `git log --oneline -3` (HEAD). **Gate evidence:** - `npm test` → 100/100 GREEN - `npm run test:uat` → 16/16 GREEN ("UAT harness: 16/16 assertions passed") - `npx tsc --noEmit` → exit 0 - `npm run build` → exit 0 - `npm run build:test` → exit 0 - Production bundle spot-check: zero occurrences of `lastGetDisplayMediaConstraints` and `get-last-getDisplayMedia-constraints` in `dist/`