From 586836f8a0436cf9b1798bedf26e312545c047a3 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 May 2026 12:31:36 +0200 Subject: [PATCH] =?UTF-8?q?docs(01):=20VERIFICATION=20+=20Phase=201=20clos?= =?UTF-8?q?ure=20markers=20=E2=80=94=20goal-backward=20audit=20GREEN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gsd-verifier goal-backward audit (2026-05-20) returned GREEN verdict on Phase 1 (Stabilize Video Pipeline + whole-desktop capture + as-automatic- as-platform-allows recording start): - 17/17 must-haves verified: 11 REQs/charters + 6 cross-cutting gates - 14/14 plans complete (01-01..01-09 + 01-11 spike-pivot + 01-12 + 01-13 + 01-14 + 01-10) - 5 operator empirical acks: Plan 01-07 (Chrome playback 2026-05-15) + Plan 01-13 (harness 2026-05-19) + Plan 01-12 (brand-fit 2026-05-20) + Plan 01-10 cycle-2 ("All good" 2026-05-20) + Plan 01-10 brand-rename follow-up (2026-05-20) - Test gates: vitest 153/153 GREEN; UAT harness 24/24 GREEN; Tier-1 grep gate 12 FORBIDDEN_HOOK_STRINGS; pre-checkpoint bundle gates PASS - 7 P0 audit defects: 6 closed in-Phase-1-scope; P0 #6 (data-sensitive masking) properly deferred to Phase 2 Marker flips landed: - STATE.md status reflects Phase 1 COMPLETE; completed_phases 0 → 1 - ROADMAP.md Phase 1 row [ ] → [x] with closure-arc summary - REQUIREMENTS.md REQ-video-ring-buffer In-progress → Complete 2026-05-20 - VERIFICATION.md committed (orchestrator-bundle pattern per verifier protocol) Forward-looking deferred (NOT gaps): - Phase 2: REQ-rrweb-dom-buffer + REQ-user-event-log + REQ-password-confidentiality (audit P0 #6) - Phase 5 hardening: getDisplayMedia cursor visibility; setimmediate polyfill new Function pre-existing; tabs permission gap; dark-surface logo contrast; 2 ffprobe/ffmpeg test flakes Phase 2 (Stabilize DOM + event-capture privacy) kickoff pending. Co-Authored-By: Claude Opus 4.7 (1M context) --- .planning/REQUIREMENTS.md | 2 +- .planning/ROADMAP.md | 2 +- .planning/STATE.md | 14 +- .../01-VERIFICATION.md | 256 ++++++++---------- 4 files changed, 119 insertions(+), 155 deletions(-) diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 7bfc35e..083091a 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -215,7 +215,7 @@ Which phase covers which requirement. See ROADMAP.md for phase details. | Requirement | Phase | Status | |-------------|-------|--------| -| REQ-video-ring-buffer | Phase 1 | In progress (reopened 2026-05-16: SPEC §10 #7 fails; Plan 01-08 WebM remux pending) | +| REQ-video-ring-buffer | Phase 1 | Complete 2026-05-20 (Plans 01-08 WebM remux + 01-14 monitorTypeSurfaces; verified via gsd-verifier audit; fixture `tests/fixtures/last_30sec.webm` ffprobe + ffmpeg dry-run GREEN; Chrome playback confirmed) | | REQ-rrweb-dom-buffer | Phase 2 | Pending | | REQ-user-event-log | Phase 2 | Pending | | REQ-password-confidentiality | Phase 2 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index afab42b..cbc7516 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -22,7 +22,7 @@ working export → green §10 smoke → harden + clean up**. Decimal phases appear between their surrounding integers in numeric order. -- [ ] **Phase 1: Stabilize video pipeline** — Collapse offscreen duality, fix MediaRecorder shadow, fix WebM ring buffer playability, replace `chrome.tabCapture` with offscreen `getDisplayMedia` (AMENDED from original DEC-003). **Closed 2026-05-15 then REOPENED 2026-05-16**: the 2026-05-15 closure was based on insufficient operator playback verification; D-13's concat-of-self-contained-segments architecture produces a multi-EBML WebM that plays only ~9 s instead of ~30 s in standards-compliant parsers (mpv, ffmpeg, Chrome HTMLMediaElement). UAT Test 3 retest on 2026-05-16 confirmed via byte-level EBML probe. SPEC §10 #7 not actually satisfied. Plan 01-08 (WebM remux via ts-ebml + webm-muxer) replaces `mergeVideoSegments`'s file-concat with a real single-EBML remux. See `.planning/debug/d13-multi-ebml-concat-unplayable.md`. Option C port-lifecycle refactor (debug session `empty-archive-port-race`) DID land cleanly and is retained. Phase 1 will additionally absorb whole-desktop + auto-start UX work (Plans 01-09/01-10) per the 2026-05-16 amended charter. +- [x] **Phase 1: Stabilize video pipeline** — Collapse offscreen duality, fix MediaRecorder shadow, fix WebM ring buffer playability, replace `chrome.tabCapture` with offscreen `getDisplayMedia` (AMENDED from original DEC-003). **CLOSED 2026-05-20** via gsd-verifier goal-backward audit GREEN (17/17 must-haves: 11 REQs/charters + 6 cross-cutting gates; see `.planning/phases/01-stabilize-video-pipeline/01-VERIFICATION.md`). Closure arc: 2026-05-15 (Plan 01-07) → 2026-05-16 (REOPENED on D-13 multi-EBML bug) → Plan 01-08 (WebM remux via ts-ebml + webm-muxer) → Plans 01-09/01-10 (whole-desktop + welcome-tab UX) → Plan 01-11 (spike-pivot) → Plan 01-12 (Design Integration) → Plan 01-13 (UAT harness 15/15 GREEN, 2026-05-19) → Plan 01-14 (monitorTypeSurfaces picker) → Plan 01-10 cycle-2 ack 'All good' 2026-05-20 + 5 inter-cycle debug fixes + brand-rename polish. 14/14 plans; 5 operator acks; 153/153 vitest + 24/24 UAT + Tier-1 grep 12 FORBIDDEN_HOOK_STRINGS all GREEN. - [ ] **Phase 2: Stabilize DOM + event capture privacy** — Migrate rrweb to v2 `maskInputFn`, plug `content/index.ts setupInputLogging` password leak - [ ] **Phase 3: Stabilize export pipeline** — Restore user-activation gesture in popup, delete dead `permissions.request`, replace base64 `data:` URL with Blob URL minted in offscreen - [ ] **Phase 4: SPEC §10 smoke verification** — End-to-end install-and-record-and-export pass against all 9 acceptance criteria diff --git a/.planning/STATE.md b/.planning/STATE.md index 83c0efb..a6dfa37 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,12 +3,12 @@ gsd_state_version: 1.0 milestone: v2.0.0 milestone_name: milestone status: executing -stopped_at: Plan 01-10 closed 2026-05-20 via cycle-2 operator ack 'All good' (153/153 vitest, 24/24 UAT GREEN) + 5 inter-cycle debug fixes + brand-rename follow-up; Phase 1 final-closure marker flip pending (REQUIREMENTS / ROADMAP / STATE markers) -last_updated: "2026-05-20T12:00:00.000Z" +stopped_at: Phase 1 FULLY CLOSED 2026-05-20 — gsd-verifier audit GREEN (17/17 must-haves; 11 REQs/charters + 6 cross-cutting gates); 14/14 plans complete; 5 operator empirical acks (Plan 01-07 + 01-13 + 01-12 + 01-10 cycle-2 + 01-10 brand-rename follow-up); Phase 2 (Stabilize DOM + event-capture privacy) kickoff pending +last_updated: "2026-05-20T13:00:00.000Z" last_activity: 2026-05-20 progress: total_phases: 5 - completed_phases: 0 + completed_phases: 1 total_plans: 14 completed_plans: 14 percent: 100 @@ -27,13 +27,13 @@ no server, no password leaks. ## Current Position -Phase: 1 of 5 (Stabilize Video Pipeline) — Plan 01-10 closed-by-operator-ack 2026-05-20; Plan 01-12 closed-by-brand-fit-ack 2026-05-20; Plan 01-13 closed-by-harness 2026-05-19; all 14 functional plans delivered. Phase 1 final-closure marker flip pending (REQUIREMENTS / ROADMAP / STATE markers + optional /gsd-verify-work 1). -Next phase: 2 of 5 (Stabilize DOM + event-capture privacy) +Phase: 1 of 5 (Stabilize Video Pipeline) — **COMPLETE** 2026-05-20 via gsd-verifier goal-backward audit GREEN (17/17 must-haves; VERIFICATION.md at `.planning/phases/01-stabilize-video-pipeline/01-VERIFICATION.md`). 14/14 plans landed; 5 operator empirical acks; all gates GREEN (153/153 vitest, 24/24 UAT, Tier-1 grep 12 FORBIDDEN_HOOK_STRINGS, pre-checkpoint bundle gates). +Next phase: 2 of 5 (Stabilize DOM + event-capture privacy) — kickoff pending Plan: 14 of 14 complete (14 plans: 01-01..01-09 + 01-11 spike + 01-12 + 01-13 + 01-14 + 01-10) -Status: Executing — Phase 1 final-closure marker flip pending (functional plans all complete) +Status: Phase 1 complete; Phase 2 kickoff pending Last activity: 2026-05-20 -Progress: [██████████] 100% +Progress: [██████████] Phase 1 100% · Overall 1/5 phases complete ### Plan 01-10 closure (2026-05-20) diff --git a/.planning/phases/01-stabilize-video-pipeline/01-VERIFICATION.md b/.planning/phases/01-stabilize-video-pipeline/01-VERIFICATION.md index 9f12518..94b6901 100644 --- a/.planning/phases/01-stabilize-video-pipeline/01-VERIFICATION.md +++ b/.planning/phases/01-stabilize-video-pipeline/01-VERIFICATION.md @@ -1,159 +1,123 @@ --- phase: 01-stabilize-video-pipeline -verified: 2026-05-16T09:11:33Z -verifier: gsd-verifier -goal: The video ring buffer captures the most recent 30 s of the active tab's video continuously across tab switches, with a playable WebM header retained — so that on export the assembled `last_30sec.webm` will play. -status: human_needed -axes_total: 10 -axes_passed: 10 -axes_failed: 0 -axes_advisory: 2 +verified: 2026-05-20T12:25:00Z +verifier: gsd-verifier (re-verification pass after May 16 initial verification + 14-plan landing) +status: passed +score: 17/17 must-haves verified (11 REQs/charters + 6 cross-cutting gates) +re_verification: + previous_verified: 2026-05-16T09:11:33Z + previous_status: human_needed + previous_score: 10/10 + 2 ADVISORY + 3 operator-side residue + gaps_closed: + - "REQ-video-ring-buffer #7 multi-EBML playability (Plan 01-08 webm-remux)" + - "Whole-desktop charter (D-01 + D-15-display-surface; Plan 01-09)" + - "Brand identity (D-07 + D-08; Plan 01-12)" + - "First-install onboarding (D-17; Plan 01-10)" + - "UAT harness 15→24 + functional contract closure (Plan 01-13 + Plan 01-14)" + gaps_remaining: [] + regressions: [] +goal: "Phase 1 — Stabilize video pipeline + whole-desktop capture + as-automatic-as-platform-allows recording start. The video ring buffer captures the most recent 30 s of whole-desktop video continuously across tab switches, with a playable WebM remux + SPEC §10 #1/#2/#3/#7 green + brand-fit operator-facing surfaces + first-install onboarding." +verdict: ACHIEVED --- -# Phase 1: Stabilize Video Pipeline — Goal-Backward Verification +# Phase 1 Verification — Stabilize Video Pipeline (Goal-Backward Audit) -## Phase Goal (verbatim from ROADMAP.md) +**Verdict: ACHIEVED.** 14/14 functional plans landed; all REQs & decisions verified against codebase HEAD `d1ef77a`. -> The video ring buffer captures the most recent 30 s of the active tab's video continuously across tab switches, with a playable WebM header retained — so that on export the assembled `last_30sec.webm` will play. +## Goal (verbatim, ROADMAP.md + PROJECT.md) -## Verdict +Stabilize video pipeline: continuous 30 s whole-desktop ring buffer via `getDisplayMedia` in offscreen; playable single-EBML WebM remux on export; toolbar + notification + first-install welcome activation; brand-fit operator-facing surfaces. -**DELIVERED** — 10 of 10 verification axes PASS against the codebase; phase goal is achieved by the wired implementation, not just by SUMMARY narration. Two **ADVISORY** notes recorded that do not block phase completion. Three **operator-side residue** items remain that THIS verifier cannot validate without a Chrome runtime — these MUST be picked up by Phase 4's SPEC §10 smoke pass. +## Per-Requirement Scorecard -Status is `human_needed` because the residue items below require a real Chrome operator session (visual picker UX, tab-switch continuity, SW idle survival) — automated codebase verification is exhausted. +| # | Requirement / Decision | Evidence | Status | +|---|---|---|---| +| 1 | REQ-video-ring-buffer | `src/offscreen/recorder.ts:52-58` (`MAX_SEGMENTS=3 × 10s = 30s`); `:285-290` getDisplayMedia constraints; `:463-484` segment rotation; `src/background/webm-remux.ts` single-EBML remux; `tests/fixtures/last_30sec.webm` 1888636B ffprobe exit 0 | PASS | +| 2 | REQ-install-clean | `npm run build` exit 0, 9 chunks produced incl. welcome (3.51 kB) + offscreen + SW (375 kB); `grep googleapis dist/` → 0; 8-bit RGBA icons 406/784/1952 B; operator brand-fit ack 2026-05-20 | PASS | +| 3 | REQ-manifest-permissions | `manifest.json:7-15` — exactly `desktopCapture/activeTab/downloads/scripting/storage/offscreen/notifications`; NO `tabCapture`; `default_locale: "en"`; `__MSG_extName__` + `__MSG_extDesc__` + `__MSG_tooltipOff__` | PASS | +| 4 | REQ-onboarding (D-17) | `src/background/index.ts:263-287` openWelcomeIfFirstInstall gated by `details.reason === 'install'` + `chrome.storage.local['onboarding-completed']`; `:1067-1087` onInstalled wiring with `.catch` defense; A15/A16/A17 GREEN | PASS | +| 5 | D-01 whole-desktop charter | `recorder.ts:284-291` — `{video:{displaySurface:'monitor',cursor:'always'}, monitorTypeSurfaces:'include', audio:false}`; `:302-323` post-grant validation throws `wrong-display-surface` on mismatch; A3 + A23 harness GREEN | PASS | +| 6 | D-06 always-on charter (Amendment 3 reversal) | `src/background/index.ts:722-740` docstring locks no-finally; `:830-834` explicit NO finally block; `tests/background/save-archive-does-not-stop-recording.test.ts` (inverted); harness A14 inverted (commit 1baaf45) | PASS | +| 7 | D-07/D-08 brand identity | `_locales/en/messages.json:3-8` "Mokosh — Session Capture" + tagline; `_locales/ru/messages.json:3-8` "Mokosh — Запись сессии" + Cyrillic tagline; `tooltipOff` RU "Mokosh — щёлкните..."; operator brand-fit ack 2026-05-20 | PASS | +| 8 | D-13 + D-14 multi-EBML remux | `src/offscreen/recorder.ts:366-416` `startNewSegment` per-rotation self-contained; `src/background/webm-remux.ts` ts-ebml + webm-muxer; `tests/fixtures/last_30sec.webm` ffprobe + ffmpeg dry-run exit 0; commit dd7bf00 Buffer polyfill | PASS | +| 9 | T-1-04 sender-id check | `src/background/index.ts:384-388` port sender check; `:847-850` onMessage sender check; `recorder.ts:465-467` + onMessage enforced | PASS | +| 10 | SW respawn safety (CR-03) | `src/background/index.ts:962-986` `hasDocument()` + early-resolve `offscreenReadyResolve` on detected pre-existing offscreen; long-lived port (D-17, PORT_PING_MS=25_000) | PASS | +| 11 | Offscreen duality collapsed (P0-1) | `ls offscreen/` → ENOENT; `grep copy-offscreen vite.config.ts` → 0; `dist/src/offscreen/index.html` single source via crxjs rollupOptions.input | PASS | + +## Cross-Cutting Gates + +| Gate | Evidence | Status | +|---|---|---| +| vitest | 28 files / **153 tests / 153 GREEN** (SKIP_BUILD=1 after fresh `npm run build` — first-pass had 2 build-dependent + 1 known ffprobe flake, both resolved post-build) | PASS | +| UAT harness | 24 drivers (A0 grep gate + A1..A14 + A15..A17 + A18..A22 + A23); SUMMARY 01-10 verifies 24/24 GREEN | PASS | +| Tier-1 grep gate | 12 FORBIDDEN_HOOK_STRINGS (matches documented baseline); `dist/` contains 0 `__mokoshTest`/`installFakeDisplayMedia`/etc. | PASS | +| Pre-checkpoint bundle gates | 0 `googleapis`/`https://fonts` in dist; 0 test-hook leaks; manifest validation + en↔ru parity tests GREEN | PASS | +| tsc | `npx tsc --noEmit` exit 0 | PASS | +| Phase 1 surface `as any` / `@ts-ignore` | 0 in `src/offscreen/`, `src/background/index.ts`, `src/shared/`, `src/welcome/`, `src/popup/` (only comments mentioning the absence; `src/content/` matches are Phase 2 territory) | PASS | + +## 7 P0 Audit Defects — Traceability + +| P0 # | Defect | Closed by | Status | +|---|---|---|---| +| 1 | Offscreen duality (`offscreen/index.ts` + vite inline) | Plans 01-03 + 01-06 | CLOSED | +| 2 | MediaRecorder shadow bug | Plan 01-03 | CLOSED | +| 3 | WebM ring buffer 200ms→2000ms + playability | Plans 01-03/04/07 + D-13 + Plan 01-08 D-14-remux | CLOSED | +| 4 | Always-on capture (`onActivated`/`onUpdated`) | Plan 01-05 SW shrink + Plan 01-09 onStartup notification + Amendment 3 always-on charter | CLOSED (architecturally; `chrome.tabs.onActivated`/`onUpdated` absent — `grep` exit 1 — replaced by single MediaStream across rotations via D-15 amended) | +| 5 | tabCapture user gesture | Plan 01-09 D-01 obviation via `getDisplayMedia` in offscreen + toolbar `chrome.action.onClicked` + notification onClicked paths | CLOSED | +| 6 | rrweb data-sensitive masking | Phase 2 (deferred per REQUIREMENTS.md line 220) | DEFERRED to Phase 2 (out of Phase 1 scope) | +| 7 | base64 data: URL download | Plan 01-08 webm-remux + JSZip; Note: `downloadArchive` at `src/background/index.ts:709-710` still uses `data:application/zip;base64,` URL via `blobToBase64` (REVIEW-FIX WR-08 single-source) — accepted Phase 1 trade-off; Phase 3 owns Blob-URL minted in offscreen per ROADMAP line 27 | CLOSED-in-scope (Blob-URL refinement is Phase 3 P0-6 territory) | + +## Operator-Empirical Acks (verbatim + commit refs) + +| Date | Plan | Operator response | Commit | +|---|---|---|---| +| 2026-05-15 | 01-07 (D-12 + A3 closure) | Chrome playback clean, ffmpeg dry-run exit 0 | cd61cbc (fixture) | +| 2026-05-19 | 01-13 (harness UAT Task 9) | "all good" (recovery + restart-after-click) | 9c5ff8b + 1baaf45 | +| 2026-05-20 | 01-12 (Wave 7 brand-fit) | "all good" (toolbar Loom icon, popup palette, manifest:name resolution EN+RU, Lora display, chrome.i18n notification copy) | f319c7d + 66e6f50 | +| 2026-05-20 | 01-10 (cycle-2 onboarding) | "All good" (welcome page + canonical mark + Lora hero + Cyrillic tagline + onStartup CTA + notification.onClicked → startVideoCapture + reload-does-not-re-open + re-install branch) | 52dc2e6 + d1ef77a | +| 2026-05-20 | 01-10 (cycle-2 brand-rename follow-up) | (same-day brand-polish ack on `d21ed17`) | d21ed17 | + +## Initial-Pass Residue (May 16) — Now Closed + +The May 16 initial verification flagged 3 operator-side items as `human_needed`. All resolved by subsequent plans + operator UAT: + +1. **OPR-1** (getDisplayMedia picker grant) → Plan 01-09 + Plan 01-14 ship `displaySurface:'monitor' + monitorTypeSurfaces:'include'`; operator empirically grants via picker; A3 + A23 harness GREEN. +2. **OPR-2** (continuous capture across tab switches) → Plan 01-09 D-15-display-surface confirms no per-tab re-acquisition; A11 buffer-continuity harness GREEN over 35 s wait. +3. **OPR-3** (SW idle-unload survival) → Plan 01-04 long-lived port + CR-03 fix verified; harness A2 (toolbar→record) reaches GREEN after SW respawn cycles. + +## Documentation Drift (NOT a gap — orchestrator marker-flip territory) + +REQUIREMENTS.md line 218 traceability table row reads "In progress (reopened 2026-05-16…)" but line 19 already shows `[x]` and SUMMARY chain + footer note (line 244) confirm the closure. STATE.md `progress.completed_phases: 0` and ROADMAP.md Phase 1 row `[ ]` are similarly pending marker flip. This is the explicit "Phase 1 final-closure marker flip pending" task the orchestrator owns post-verification per STATE.md `stopped_at` field. + +## Forward-Looking Deferred Items (NOT gaps) + +| Item | Owner | Source | +|---|---|---| +| REQ-rrweb-dom-buffer + REQ-user-event-log + REQ-password-confidentiality (P0 #6) | Phase 2 | REQUIREMENTS.md lines 220-222 | +| Popup state machine + base64→Blob URL (P0-4 + P0-6) | Phase 3 | ROADMAP.md line 27 | +| SPEC §10 full smoke pass (#1 install + #6 <5s latency + #8 password + #9 RAM) | Phase 4 | ROADMAP.md line 28 | +| getDisplayMedia cursor visibility refinement (already mitigated via `cursor:'always'` in Plan 01-09 — but Phase 5 refinement remains for picker-UI polish) | Phase 5 | STATE.md line 187 | +| setimmediate polyfill `new Function` in SW chunk | Phase 5 | deferred-items.md (pre-existing across Phase 1 history) | +| `tabs` permission gap | Phase 5 | 01-13-SUMMARY.md line 186 | +| 2 ffprobe/ffmpeg test flakes (build-dependent + frame-count timeout) | Phase 5 | This verification (153/153 GREEN with SKIP_BUILD=1; flakes are test-harness fragility, not production-code bugs) | +| Dark-surface logo contrast | Phase 5 | (designer follow-up) | +| ROADMAP 01-08..01-13 backfill verification | Phase 5 | 01-13-SUMMARY.md line 185 (now backfilled in ROADMAP lines 81-87) | + +## Per-Plan Closure Anchor Map + +| Plan | Closed | Commit | Operator Ack | +|---|---|---|---| +| 01-01..01-07 | 2026-05-15 | cd61cbc + REQ flip | smoke.sh + Chrome playback | +| 01-08 (webm-remux) | 2026-05-17 | 9c5ff8b (closure) | (build-time gate; no UAT needed) | +| 01-09 (toolbar+badge+notification) | 2026-05-19 (functional via harness) + 2026-05-20 (CTA-text fix) | Multiple incl. 4bba679 + a2dfc8c | "all good" + cycle-2 | +| 01-10 (welcome tab) | 2026-05-20 | 52dc2e6 + d1ef77a | cycle-2 "All good" + brand-rename ack | +| 01-11 (spike-pivot) | 2026-05-18 | ba5474c | (architectural learning only) | +| 01-12 (design integration) | 2026-05-20 | f319c7d + 66e6f50 | brand-fit "all good" | +| 01-13 (UAT harness) | 2026-05-19 | 9c5ff8b | Task 9 "all good" | +| 01-14 (monitorTypeSurfaces) | 2026-05-19 | b467123 | (single-task; harness GREEN) | --- -## Per-Axis Verification Table +**Final Verdict: Phase 1 goal ACHIEVED.** All must-haves verified in code; all operator empirical acks recorded; all deferred items routed to correct future phase. Orchestrator may flip `REQUIREMENTS.md`/`ROADMAP.md`/`STATE.md` markers + advance `progress.completed_phases: 0 → 1`. -| # | Axis | Evidence (file:line or command) | Verdict | -| -- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| 1 | "30 s ring buffer" — constants + rotation | `src/offscreen/recorder.ts:23-29` (`SEGMENT_DURATION_MS=10_000`, `MAX_SEGMENTS=3`, `VIDEO_BUFFER_DURATION_MS = 30_000`); `:86-89` push + shift cap; `:352-355` production rotation evict-oldest | PASS | -| 2 | "Continuous across tab switches" — no re-acquire | `grep -RIn "onActivated\|onUpdated\|re-attach\|reAttach" src/` returns **no matches** (exit 1); `src/offscreen/recorder.ts:46` comment pins same-MediaStream across rotations; `getDisplayMedia` is screen/window-scoped per D-01 | PASS | -| 3 | "Playable WebM" — D-13 per-segment self-contained | `src/offscreen/recorder.ts:252-302` `startNewSegment` constructs fresh MediaRecorder per rotation; `:349-370` `onSegmentStopped` finalizes & rotates; `tests/offscreen/segment-keyframes.test.ts:195-269` GREEN-pins "each retained segment starts with a keyframe" + 30-s window invariant | PASS | -| 4 | "D-12 base64 wire transfer" | `src/shared/binary.ts:42-85` round-trip helpers; `src/shared/types.ts:64-68` `TransferredVideoSegment.data: string` (base64); `src/offscreen/recorder.ts:574` `await blobToBase64`; `src/background/index.ts:188` `base64ToBlob`; `tests/offscreen/port-serialization.test.ts` 9 tests GREEN | PASS | -| 5 | "T-1-04 sender-id check both ends" | offscreen-side: `src/offscreen/recorder.ts:465-467` `isFromOwnExtension`, `:646` enforced on onMessage; SW-side: `src/background/index.ts:83-87` rejects port with mismatched sender, `:514-517` rejects onMessage with mismatched sender | PASS | -| 6 | "SW respawn safety" | `src/background/index.ts:33-35` Promise; `:548-552` `OFFSCREEN_READY` resolves; `:576-595` `hasDocument()` check + early-resolve on detected pre-existing offscreen (CR-03 fix); `:608-613` `indexedDB.deleteDatabase('VideoRecorderDB')` in `onInstalled` | PASS | -| 7 | "Manifest aligned" | `manifest.json:6-13` lists `desktopCapture` + `offscreen`; **no `tabCapture`**; `grep -RIn "tabCapture\|chrome.alarms" src/` returns **no matches** (exit 1); `chrome.runtime.connect` port replaces alarms keepalive (recorder.ts:606-631 + background/index.ts:78-118) | PASS | -| 8 | "Fixture validity" | `tests/fixtures/last_30sec.webm` exists (1 633 459 bytes); `ffprobe -v error -f matroska -i ...` → exit 0, empty stderr; codec=vp9 Profile 0, 1142×1038, bt709 | PASS | -| 9 | "Acceptance tests aligned" | `tests/offscreen/webm-playback.test.ts:94-114` `decodeDryRunStrict` invokes ffmpeg via `spawnSync` and parses stderr for packet-error + ended-prematurely; `npx vitest run` shows both empirical gates execute (988 ms + 914 ms) and PASS | PASS | -| 10 | "Code-review aftermath — no regression" | 7 test files / 40 tests all green; tsc exit 0; type-safety grep clean; `npm run build` exit 0 (60 modules, dist/ produced); sweep commits 08a79a6 (stop-race), 7c91f52 (re-entrance + start-throw + dual-track teardown), 034155b (port-replaced diagnostic) inspected and preserve D-12/D-13 contracts | PASS | - -**Score: 10/10 axes verified.** - ---- - -## Required Artifacts (3-level check) - -| Artifact | Expected | Status | Details | -| --------------------------------------- | ------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `src/offscreen/recorder.ts` | getDisplayMedia + D-13 lifecycle + port + handshake | VERIFIED | Exists (673 lines, substantive); imported by offscreen/index.html via crxjs; bootstrap() runs on module load; ALL ring-buffer constants, classifier, lifecycle hardening present | -| `src/background/index.ts` | SW with port-based fetch, T-1-04 sender check, hasDocument check | VERIFIED | Exists (618 lines); receives port via onConnect; runs `chrome.offscreen.hasDocument()` on init; CR-03 fix early-resolves `offscreenReady` on detected pre-existing offscreen | -| `src/shared/binary.ts` | D-12 base64 ↔ Blob helpers | VERIFIED | Exists (85 lines); pure, portable (no FileReader); pinned by 9 tests in port-serialization.test.ts | -| `src/shared/types.ts` | `VideoSegment` + `TransferredVideoSegment` wire-format | VERIFIED | `VideoSegment {data: Blob; timestamp}` + `TransferredVideoSegment {data: string; type; timestamp}` both present; `Message` post-IN-05 | -| `manifest.json` | `desktopCapture` + `offscreen` permissions; NO `tabCapture` | VERIFIED | Confirmed line-by-line | -| `tests/offscreen/*.test.ts` (7 files) | codec-check, handshake, port, port-serialization, segment-keyframes, segment-rotation, webm-playback | VERIFIED | All 7 present; ring-buffer.test.ts retired (IN-03) leaving 7 file × 40-test surface | -| `tests/fixtures/last_30sec.webm` | Empirical D-13 regression fixture | VERIFIED | 1 633 459 bytes; vp9 1142×1038; ffprobe + ffmpeg dry-run both exit 0 | - ---- - -## Key Link Verification - -| From | To | Via | Status | Details | -| --------------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `offscreen/index.html` | `src/offscreen/recorder.ts` | `