docs(01-01): cite D-05 in must_haves per coverage gate .planning/phases/01-stabilize-video-pipeline/01-01-PLAN.md

This commit is contained in:
2026-05-15 17:07:49 +02:00
parent 576280f6aa
commit 0811c6a292
5 changed files with 41 additions and 29 deletions

View File

@@ -18,12 +18,14 @@ requirements_addressed:
must_haves:
truths:
- "`src/offscreen/recorder.ts` exists and exports the symbols Plan 02 tests against: addChunk, trimAged, getBuffer, resetBuffer, assertCodecSupported, VIDEO_BUFFER_DURATION_MS"
- "`src/offscreen/recorder.ts` exists at the canonical source path as a real TypeScript module — strict type-check, source maps, IDE support (D-06)"
- "`src/offscreen/recorder.ts` exports the symbols Plan 02 tests against: addChunk, trimAged, getBuffer, resetBuffer, assertCodecSupported, VIDEO_BUFFER_DURATION_MS"
- "Running `npx vitest run tests/offscreen/ring-buffer.test.ts tests/offscreen/codec-check.test.ts` exits 0 with all tests green"
- "Buffer holds at most: 1 header chunk + every chunk with arrival timestamp newer than now-30_000ms"
- "Codec strictly bound to `video/webm;codecs=vp9` at 400 000 bps with `MediaRecorder.isTypeSupported` gate; no fallback chain (D-20)"
- "`MediaRecorder.start(2000)` is called on session start (timeslice = 2000 ms per SPEC §4.1)"
- "On `MediaStreamTrack.onended`, the buffer is cleared and a `RECORDING_ERROR` of `'user-stopped-sharing'` is emitted to SW"
- "Capture is `navigator.mediaDevices.getDisplayMedia()` invoked from inside the offscreen document (D-01); SW-side `chrome.tabCapture.getMediaStreamId` is removed in Plan 05"
- "Single continuous `MediaRecorder` runs for the whole session with `mediaRecorder.start(2000)` so chunks land on cluster boundaries per SPEC §4.1 (D-09)"
- "One-time source picker fires on session start (operator picks screen/window once); on `MediaStreamTrack.onended` the buffer is cleared and a `RECORDING_ERROR` of `'user-stopped-sharing'` is emitted to SW so the popup can re-prompt next interaction (D-03)"
- "Restart-segments fallback (D-13) is pre-staged as a commented-out skeleton at the bottom of recorder.ts so Plan 07's fallback path doesn't require a re-plan"
- "`src/offscreen/index.html` exists at the source path and references `./recorder.ts`"
- "`src/shared/logger.ts` has an `OffscreenLogger` class with `[OS:...]` prefix"
@@ -72,8 +74,10 @@ handshake — that is Plan 04. To keep this plan inside its context budget, the
port-side code in `recorder.ts` is left as a small import-time placeholder
that Plan 04 fills in. The Plan-02 port + handshake tests therefore remain
RED until Plan 04 lands, which is the intended choreography per the
wave-1 dependency graph (Plans 03 and 04 run in parallel; Plan 02 wrote both
sets of RED tests in advance).
sequential wave structure (Plan 03 lands in wave 2; Plan 04 follows in wave
3 and refactors the bootstrap section of the same file). Plan 02 wrote both
sets of RED tests in advance so Plan 03 and Plan 04 each have a discrete
RED→GREEN cycle to flip.
Purpose: REQ-video-ring-buffer's load-bearing logic lives here. The ring
buffer is a pure function — exactly the TDD sweet spot. The remaining
@@ -155,7 +159,7 @@ export interface VideoChunk {
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-1-01 | Tampering — codec downgrade | `MediaRecorder` constructor | mitigate | `assertCodecSupported()` calls `MediaRecorder.isTypeSupported('video/webm;codecs=vp9')` BEFORE constructing the recorder; if it returns false, throws an Error and emits `RECORDING_ERROR` to SW. No vp8 / h264 / default fallback chain. The strict mode covers `MediaRecorder` itself being absent (the codec-check test mocks both cases). Grep gate: `grep -v '^#' src/offscreen/recorder.ts \| grep -cE 'codecs=(vp8\|h264)'` returns 0 (no fallback codec strings in the module). |
| T-1-03 | Information Disclosure — captured stream contains other apps' content | `getDisplayMedia` stream | accept | This is the documented residual risk per CONTEXT.md D-04. The Chrome "Sharing your screen" indicator is the user-facing signal; the operator chose to share. No code-level mitigation is possible (the API is supposed to capture the screen). Documented as accepted risk in 01-RESEARCH.md §"Security Domain". |
| T-1-03 | Information Disclosure — captured stream contains other apps' content | `getDisplayMedia` stream | accept | This is the documented residual risk per CONTEXT.md D-04 — the operator opted into the "Sharing your screen" indicator as the cost of the broader capture coverage. The Chrome "Sharing your screen" indicator is the user-facing signal; the operator chose to share. No code-level mitigation is possible (the API is supposed to capture the screen). Documented as accepted risk in 01-RESEARCH.md §"Security Domain". |
| T-1-NEW-03-01 | DoS — unbounded buffer growth on a stuck timestamp | `addChunk` + `trimAged` | mitigate | The trim function uses arrival timestamp; if a clock anomaly produces a negative or stuck `now`, the buffer is bounded above by SPEC §10 #9 (50 MB RAM ceiling) anyway. Defensive belt: the recorder also exposes `getBuffer().length` to SW so a healthchecker can observe growth. No active rate-limit needed for Phase 1. |
</threat_model>