Plan 02-01 Wave 0 RED gate closed. Three failing test files (16 it()
blocks total: 11 RED + 5 GREEN regression guards) pin the locked
decisions for Phase 2 ahead of Plans 02-02 + 02-03 implementation:
- blob-url-download.test.ts (3 RED) — D-P2-01 offscreen Blob URL
pipeline (closes audit P0-6: base64 data: URL → blob: URL).
- meta-json-urls-schema.test.ts (5 RED) — D-P2-02 meta.url → meta.urls
migration + F2 empty-tracker → urls:[] resolution.
- strict-meta-json-validation.test.ts (3 RED + 5 GREEN) — D-P2-03
strict 8-field schema validation with EXPECTED_KEYS pin including
planner-suggested `schemaVersion` 8th field.
Test count delta: 155 GREEN → 159 GREEN + 11 RED (+4 GREEN regression
guards, +11 RED test contracts). Vitest reporter:
Test Files 4 failed | 27 passed (31)
Tests 12 failed | 159 passed (171)
(12 failed = 3 + 5 + 3 RED from this plan + 1 pre-existing flaky
ffprobe test in webm-remux.test.ts — out of scope; documented in
SUMMARY.md Deferred Issues.)
Tier-1 grep gate: 13/13 GREEN preserved (this plan touches no
production code).
Planner-resolved tensions carried forward in SUMMARY.md:
- D-P2-03 'non-empty urls[]' vs CONTEXT.md permissive empty-array →
F2 resolved in favor of permissive (Test 3 of Task 3 relaxed).
- 8th field name `schemaVersion` → tentative planner pick;
Plan 02-03 implementer commits to schemaVersion: '2' const.
- tab-url-tracker module seam → planner-suggested name
`src/background/tab-url-tracker.ts` with getTabUrlsSeen() export.
- Plan claim 'ALL 8 fail' reconciled honestly: 3 RED + 5 GREEN
regression guards (timestamp/semver/totalEvents/buffer-seconds/
duration-minutes already match current 7-field shape).
Plan suggestions reconciled with reality:
- vitest env: 'node' not 'jsdom' (Node 24 has URL/Blob/performance
globals; jsdom not in devDeps). FileReader polyfill inline.
- Task 2 Test 1 source-text scan instead of tsc-compile-failure
(vitest.config.ts typecheck:{enabled:false}).
Per worktree-mode constraint: STATE.md, ROADMAP.md, REQUIREMENTS.md
NOT modified. The orchestrator owns those writes after all worktree
agents in Wave 0 complete.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
19 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-stabilize-export-pipeline | 01 | testing |
|
|
|
|
|
|
|
|
~20min | 2026-05-20 |
Phase 02 Plan 01: Wave-0 RED tests for export pipeline migration
Three failing test files (16 it() blocks) pin D-P2-01 Blob URL download contract + D-P2-02 meta.json urls[] migration + D-P2-03 strict 8-field schema validation + F2 empty-tracker resolution, ahead of Plan 02-02 + 02-03 implementation.
Performance
- Duration: ~20 min
- Started: 2026-05-20T13:13:59Z (baseline test capture)
- Completed: 2026-05-20T13:33:38Z (final commit + SUMMARY)
- Tasks: 3/3 completed
- Files created: 3 test files (1 917 lines total)
- Files modified: 0 production-code files (this is the RED gate ONLY)
Accomplishments
Task 1: tests/background/blob-url-download.test.ts (3 RED)
Pins D-P2-01 (offscreen-minted Blob URL pipeline; closes audit P0-6).
- Test 1 (wire-format polarity guard):
chrome.downloads.downloadis called with aurlthat starts withblob:and does NOT start withdata:application/zip;base64,. RED today; concrete assertion error:chrome.downloads.download was called with url='data:application/zip;base64,UEsDBAoAAAAAAL1qtFw...' — D-P2-01 forbids data:application/zip;base64, prefix. - Test 2 (latency + wire-format at 6 MB scale): A 6 MB archive completes in under 5 000 ms AND emits a
blob:URL. The vi.doMock on remuxSegments short-circuits the muxer to inject a synthetic 6 MB Blob. RED today via the wire-format guard (data: URL prefix); GREEN after Plan 02-02. Elapsed ~1 266 ms (well under budget). - Test 3 (revoke lifecycle):
URL.revokeObjectURLis scheduled with the same URL passed tochrome.downloads.downloadoncechrome.downloads.onChangedreportsstate.current === 'complete'. RED today; concrete error:chrome.downloads.onChanged._callbacks.length === 0 at probe time(current downloadArchive does not register an onChanged listener).
Commit: 748a81f test(02-01): RED — pin Blob URL download contract (D-P2-01)
Task 2: tests/background/meta-json-urls-schema.test.ts (5 RED)
Pins D-P2-02 (meta.json url→urls migration) + F2 empty-tracker resolution.
- Test 1 (SessionMetadata type-shape via source-text scan):
src/shared/types.tsMUST containurls: string[]AND MUST NOT containurl:(singular) inside the SessionMetadata interface body. Workaround for vitest typecheck:{enabled:false}. RED today; concrete error:'urls: string[]' field not present. - Test 2 (createArchive meta.json shape): Real createArchive output has
Array.isArray(meta.urls) === trueANDmeta.url === undefined. RED today; concrete error:meta.urls is not an Array. Got: undefined. - Tests 3+4+5 (tab-url-tracker module seam): Each test dynamically imports
src/background/tab-url-tracker(the planner-suggested module name from Plan 02-03) andexpect.fails with a precise GREEN-gate contract message when the module doesn't exist. RED today; module missing.- Test 3 pins dedup + first-seen-first order: ['A', 'B', 'A'] → ['A', 'B'].
- Test 4 pins URL filter: include https + chrome-extension://; exclude chrome:// + about:.
- Test 5 pins F2 empty-tracker contract: no observations →
getTabUrlsSeen() === [](NOT undefined, NOT [], NOT null).
Commit: 9e45d33 test(02-01): RED — pin meta.json urls[] schema + dedup/filter + empty-tracker (D-P2-02 + F2)
Task 3: tests/build/strict-meta-json-validation.test.ts (3 RED + 5 GREEN regression guards)
Pins D-P2-03 strict 8-field meta.json schema validation.
- Test 1 (RED): exactly 8 fields. Current shape has 7. Concrete error:
meta.json has 7 fields; D-P2-03 requires exactly 8. Current keys: [timestamp, url, userAgent, extensionVersion, videoBufferSeconds, logDurationMinutes, totalEvents]. - Test 2 (GREEN regression guard): ISO-8601 Z-suffix timestamp. Current
new Date().toISOString()already matches; catches future locale-formatter regressions. - Test 3 (RED): urls Array of valid URLs (empty permitted per F2). Concrete error:
meta.urls is not an Array. Got: undefined. - Test 4 (GREEN regression guard): semver extensionVersion. Current manifest.version '1.0.0' matches; catches build-hash regressions.
- Test 5 (GREEN regression guard): non-negative integer totalEvents. Current sum-of-event-counts matches; catches negative/float regressions.
- Test 6 (GREEN regression guard): videoBufferSeconds === 30 literal. CON-video-window pin.
- Test 7 (GREEN regression guard): logDurationMinutes === 10 literal. CON-event-log-window pin.
- Test 8 (RED): no extra fields beyond EXPECTED_KEYS. Concrete error:
meta.json contains extra (unexpected) fields: ["url"]. EXPECTED_KEYS = [timestamp, urls, userAgent, extensionVersion, videoBufferSeconds, logDurationMinutes, totalEvents, schemaVersion].
Commit: 94e0346 test(02-01): RED — pin strict 8-field meta.json schema validation (D-P2-03)
Test count delta
- Before: 155 GREEN (baseline including sw-bundle-import.test.ts after npm run build).
- After: 159 GREEN + 11 RED (11 new RED from this plan + the 5 Task-3 GREEN-today regression guards count as +4 net new GREEN since Test 5 was added per F2; see "Plan vs reality" below).
- Net delta: +11 RED, +4 GREEN tests (was 155, now 170 vitest-discovered + 1 ffprobe-flaky out-of-scope; see Deferred Issues).
Vitest reporter final state:
Test Files 4 failed | 27 passed (31)
Tests 12 failed | 159 passed (171)
(12 failed = 3 [Task 1] + 5 [Task 2] + 3 [Task 3 RED] + 1 [pre-existing ffprobe-flaky webm-remux test — out of scope; see Deferred Issues].)
Tier-1 grep gate
tests/background/no-test-hooks-in-prod-bundle.test.ts continues to pass with 13/13 GREEN (FORBIDDEN_HOOK_STRINGS at the existing inventory; this plan touches no production code).
Plan-vs-reality reconciliation
Plan claim: "ALL 8 [strict-validation] tests MUST fail under current HEAD"
Reality: 3 of 8 are RED-today (Tests 1, 3, 8 — the migration-driven assertions). The other 5 are GREEN-today regression guards (timestamp regex, semver, totalEvents, videoBufferSeconds, logDurationMinutes — all of which already match the current 7-field shape).
Resolution: Honored the plan's spirit (8 strict-validation tests pinning the post-migration schema) while being honest about the test states. The 5 GREEN-today tests are not test pollution — they're regression guards that prevent Plan 02-03 from accidentally regressing the unchanged fields when adding urls + schemaVersion.
Plan suggestion: "Use jsdom env (default in vitest.config.ts)"
Reality: vitest.config.ts explicitly sets environment: 'node' and jsdom is not in devDependencies. Node 24 ships URL.createObjectURL + URL.revokeObjectURL + performance + Blob as globals (verified at land time), so jsdom is not required. FileReader is the only missing global; a minimal polyfill inline in each test file delegates to Blob.arrayBuffer().
Plan suggestion: "Test 1 fails at the TypeScript-compile layer"
Reality: vitest.config.ts has typecheck: { enabled: false }. A tsc-failure-based pin would be a no-op. Replaced with a source-text regex scan of src/shared/types.ts that asserts both presence of urls: string[] AND absence of url: inside the SessionMetadata interface body.
Planner-resolved tensions (carried forward into Plans 02-02 + 02-03)
- D-P2-03 "non-empty urls[]" vs CONTEXT.md
<specifics>permissive empty-array clause: resolved in favor of the permissive clause (F2 — empty IS PERMITTED for whole-desktop-no-tab sessions). Test 3 of Task 3 enforces the relaxed contract. - 8th field name
schemaVersion: TENTATIVE PLANNER PICK to mark the D-P2-02 url→urls breaking-change cutover. EXPECTED_KEYS constant intests/build/strict-meta-json-validation.test.tspins this choice; Plan 02-03's implementer COMMITS to addingschemaVersion: '2'as a constant insrc/background/index.ts. If a stronger argument for a different 8th-field name surfaces, this test file is the canonical place to amend. - tab-url-tracker module name
src/background/tab-url-tracker.ts: PLANNER-SUGGESTED. Plan 02-03's implementer commits to this name; if renaming is desired, all three Task 2 dynamic-import strings need to update lockstep. _resetForTesting/_observeForTestingergonomic hooks on tab-url-tracker: OPTIONAL contract the test file documents. Plan 02-03 implementer SHOULD expose these for Tests 3+4+5 ergonomic wiring; if absent, the tests need to wire chrome.tabs.onUpdated callbacks directly.
Forward links
- Plan 02-02 (Wave 1 GREEN): flips Task 1's 3 RED tests by minting the Blob URL in the offscreen document via
URL.createObjectURL, transferring it to the SW via a new port-bridge message, and registering thechrome.downloads.onChangedlistener forURL.revokeObjectURLlifecycle. - Plan 02-03 (Wave 1 GREEN): flips Task 2's 5 RED tests + Task 3's 3 RED tests by (a) amending
src/shared/types.tsto swapurl: stringforurls: string[], (b) implementingsrc/background/tab-url-tracker.tswithgetTabUrlsSeen()fed by chrome.tabs.onUpdated + chrome.tabs.onActivated listeners (DEC-011 Amendment 1tabspermission), (c) amendingcreateArchiveinsrc/background/index.tsto callsnapshotOpenTabs() + getTabUrlsSeen()and writeurls+schemaVersion: '2'to meta.json (removing the oldurl:field). - Plan 02-04 (Wave 2 UAT extensions): harness A24+ assertions read the GREEN-flipped meta.json. A28 (added per F1) pins REQ-archive-layout strict zip-layout (5 entries exact).
Deferred Issues
Pre-existing flaky test in tests/background/webm-remux.test.ts
The ffprobe -count_frames reports between 905 and 912 frames inclusive test (Plan 01-08 Task 2) fails when run as part of the full-suite parallel runner but passes when run in isolation. The failure surface is intermittent; the input fixture (raw-3ebml-concat.webm) and remuxSegments output are deterministic. Suspected cause: ffprobe spawnSync timing interaction with parallel vitest workers, or transient tmpdir contention.
- Out of scope for this plan per Plan 02-01 §verification: this plan touches no production code and no Phase 1 test file.
- Scope boundary: not directly caused by current plan changes (verified by running the test in isolation post-commit).
- Recommended owner: Phase 5 hardening OR a Phase 2 closure debug session if the flake recurs during Plans 02-02 / 02-03 execution. If the flake blocks Phase 2 plan-checker or operator empirical UAT, escalate to a /gsd-debug session.
Self-Check: PASSED
Files created (verified via stat at SUMMARY commit time)
- FOUND: tests/background/blob-url-download.test.ts (~625 lines)
- FOUND: tests/background/meta-json-urls-schema.test.ts (~671 lines)
- FOUND: tests/build/strict-meta-json-validation.test.ts (~621 lines)
Commits exist (verified via git log)
- FOUND:
748a81ftest(02-01): RED — pin Blob URL download contract (D-P2-01) - FOUND:
9e45d33test(02-01): RED — pin meta.json urls[] schema + dedup/filter + empty-tracker (D-P2-02 + F2) - FOUND:
94e0346test(02-01): RED — pin strict 8-field meta.json schema validation (D-P2-03)
Plan §verification gates
- Task 1:
npx vitest run tests/background/blob-url-download.test.ts→ 3 failed (3) ✓ - Task 2:
npx vitest run tests/background/meta-json-urls-schema.test.ts→ 5 failed (5) ✓ - Task 3:
npx vitest run tests/build/strict-meta-json-validation.test.ts→ 3 failed | 5 passed (8) ✓ (5 GREEN = regression guards per plan-vs-reality reconciliation above) - Skip discipline:
grep -cE "\.skip\(|\.skip$|\.skip[, ]"returns 0 for all three files ✓ - Tier-1 grep gate:
npx vitest run tests/background/no-test-hooks-in-prod-bundle.test.ts→ 13/13 GREEN preserved ✓
Success criteria reconciliation
- Three RED test files exist with concrete failure messages under current HEAD (3 + 5 + 3 = 11 RED).
meta-json-urls-schema.test.tsTest 1 fails (source-text scan; the planner's tsc-compile-failure path was a no-op due to typecheck:{enabled:false}, but the source-text scan is a stronger structural pin that survives the disabled-typecheck config).- Tier-1 grep gate (FORBIDDEN_HOOK_STRINGS inventory) remains GREEN (13/13).
- UAT harness baseline UNCHANGED (test-only changes; this plan touches no production-source code).
- Commits land with
test(02-01): RED — ...prefix matching Plan 01-02 / 01-13 RED-test precedent.