Files
mokosh/.planning/phases/01-stabilize-video-pipeline/01-DISCUSSION-LOG.md

6.0 KiB
Raw Permalink Blame History

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; ~200300 ms gap per restart (~23% 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.connect port and chrome.runtime.sendMessage for 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 by tsconfig strict 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 tabCapture as an opt-in via config.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.session cold-start buffer-pointer recovery — Phase 5 (Harden + clean up).
  • Error UX for "user stops sharing" — popup state extension, Phase 3.