6.0 KiB
Phase 1: Stabilize Video Pipeline — Discussion Log
Audit trail only. Do not use as input to planning, research, or execution agents. Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
Date: 2026-05-15 Phase: 01-stabilize-video-pipeline Areas discussed: Offscreen source-of-truth location, Ring-buffer mechanism, Tab-switch behavior, State survival across SW unload
Offscreen source-of-truth location
| Option | Description | Selected |
|---|---|---|
src/offscreen/recorder.ts as crxjs offscreen entry |
Real TS module bundled by crxjs; HTML references the bundle; type-check + source maps; deletes both offscreen/index.ts and the inline copy-offscreen plugin |
✓ |
src/offscreen/recorder.ts via web_accessible_resources |
Same source file, declared as WAR; reachable from chrome.runtime.getURL(); more boilerplate |
|
| Keep vite.config.ts inline string | Status quo — hard NO per audit; no type-check, no source maps, easy to lose |
User's choice: crxjs offscreen entry (Recommended)
Notes: Aligns with the audit's primary structural recommendation. The inline plugin and dead offscreen/index.ts are both deleted in the same phase.
Ring-buffer mechanism
| Option | Description | Selected |
|---|---|---|
Single continuous MediaRecorder + age-based trim (start(2000)) |
Spec-aligned timeslice; retain header forever, drop later chunks by age; verify with ffprobe; pivot to restart-segments if verification fails | ✓ |
| Restart-segments every ~10 s | Each segment self-contained valid WebM; bulletproof playability; ~200–300 ms gap per restart (~2–3% loss); higher RAM (3 segments in flight) | (fallback) |
| Cluster-aware EBML trim via ts-ebml | Theoretically optimal; ~80 KB dep + more test surface | (third fallback, in deferred) |
| Chunk-arrival-time trim, 200 ms timeslice (status quo) | Current broken approach |
User's choice: "It seems that 2 is the most stable? But if you think 1 is better it is up to you." After follow-up: locked Option 1 with Option 2 as documented fallback. Notes: Decision deferred to Claude after weighing the trade-offs; rationale captured in CONTEXT.md D-09..D-13. Plan-checker enforces ffprobe verification as the Phase 1 success gate; if it fails, the plan revision pivots to restart-segments without re-asking the user.
Tab-switch behavior
| Option | Description | Selected |
|---|---|---|
| Hard restart (stop old recorder, start fresh on new tab) | SPEC §8-implied; spec-aligned | (rendered N/A by capture-API change) |
| Cross-tab carry-over | Preserve prior tab's buffer until new fills; ~2× RAM during overlap | |
| Per-tab buffers (one per recently-visited tab) | High RAM; risks CON-ram-ceiling | |
| (emergent option from user: capture whole browser / whole screen) | Switch capture API to getDisplayMedia() / chrome.desktopCapture; tab-switch handling becomes N/A |
✓ |
User's choice: "May we just record the whole browser? or the whole screen?" → confirmed in the follow-up question as switch to chrome.desktopCapture / getDisplayMedia.
Notes: This is a project-level amendment to DEC-003 (capture API), not just a Phase 1 implementation detail. Trade-off was made explicit to the user before confirming: Chrome shows a permanent "Sharing your screen" banner (loses SPEC §1 "silent for operator" property), picker dialog required on session start. User accepted. Phase 1's plan therefore includes the doc-amendment cascade (D-A1..D-A6) BEFORE any code work, and removes all chrome.tabs.onActivated/onUpdated reattachment logic.
State survival across SW unload
| Option | Description | Selected |
|---|---|---|
| Move buffer ownership to offscreen + long-lived port keepalive | Offscreen survives SW; port resets idle timer (only mechanism that does); buffer in plain offscreen memory; honors CON-buffer-storage | ✓ |
| Same as above + chrome.storage.session belt-and-suspenders | Cold-start recovery on offscreen crash; borderline CON-buffer-storage compliance | (deferred to Phase 5) |
| Keep SW-owned buffer, add proper keepalive port | Smallest diff; SW can still die under memory pressure | |
| Accept buffer loss on SW unload | Don't fight lifecycle; violates "continuous" intent |
User's choice: "one seems no problem." — locked Option 1.
Notes: chrome.alarms keepalive deleted (DEC-010 / CON-service-worker-keepalive amended). The chrome.storage.session belt-and-suspenders variant moved to Phase 5 deferred ideas. The SW-side IndexedDB code path in vite.config.ts:43-104 and src/background/index.ts:445-475 is deleted alongside.
Claude's Discretion
- Internal protocol choice between
chrome.runtime.connectport andchrome.runtime.sendMessagefor offscreen↔SW messaging (decision: port for keepalive, sendMessage for one-shot requests — captured in CONTEXT.md D-19). - Codec strictness — enforce vp9 only via
MediaRecorder.isTypeSupported, fail loud if unavailable. No fallback chain (current code's vp9→vp8→h264→default is removed). - Internal module naming for the offscreen recorder.
- Exact error-UX copy for "user stopped sharing" — deferred to Phase 3 (popup state machine territory).
- Code-style choices within
src/offscreen/(already constrained bytsconfigstrict mode).
Deferred Ideas
- Audio capture.
getDisplayMedia({ audio: true })makes this trivial — but SPEC §9 excludes audio from Phase 1. Phase 2 work. - Per-tab silent capture mode (re-introducing
tabCaptureas an opt-in viaconfig.json) for installations that prioritize silent operation over broad coverage. - Cluster-aware EBML trim (ts-ebml) — third-line fallback under D-13 if even restart-segments don't produce playable output. Currently parked.
chrome.storage.sessioncold-start buffer-pointer recovery — Phase 5 (Harden + clean up).- Error UX for "user stops sharing" — popup state extension, Phase 3.