# Architectural Decisions (synthesized from SPEC) The SPEC `Тз расширение фаза1.md` does not formally label itself as ADR-set, but sections 2 ("Стек"), 7 ("Разрешения в manifest.json"), and 8 ("Важные ограничения и решения") encode binding technical choices that act as architectural decisions for Phase 1. Each decision below is reproduced from the SPEC verbatim where possible and tagged with its origin. Status legend (synthesized, since SPEC has no formal ADR status field): - `Accepted` — SPEC §2/§7/§8 prescribed, no contradicting source - `locked: false` everywhere — SPEC is `locked: false` in classification, so these decisions can still be revised by a future ADR; today they are the authoritative baseline. --- ## DEC-001: Chrome Extension Manifest V3 - Source: `Тз расширение фаза1.md` §2 (Стек), §7 (Разрешения) - Status: Accepted - Locked: false - Scope: Extension platform / packaging - Decision: The extension MUST be implemented as a Chrome Extension targeting Manifest V3 (Service Worker background, no persistent background page). - Rationale (from SPEC): Required for modern Chrome compatibility; implied by every API used (`chrome.tabCapture`, `chrome.downloads`, `chrome.tabs`, `chrome.alarms`). - Confirming source: `README.md` §"Технический стек" — "Chrome Extension, Manifest V3". --- ## DEC-002: Service Worker as Background Coordinator - Source: `Тз расширение фаза1.md` §2, §3 (Структура расширения), §8 - Status: Accepted - Locked: false - Scope: Background execution model - Decision: Background logic runs in a Manifest V3 Service Worker (`background/service-worker.js`) that coordinates the video buffer and archive packaging. - Constraint binding: Service Worker is unloaded after ~30 s idle; keepalive MUST be maintained via `chrome.alarms` firing every 20 seconds (SPEC §8). - Confirming source: `README.md` §"Технический стек". --- ## DEC-003: Tab Video Capture via `chrome.tabCapture` - Source: `Тз расширение фаза1.md` §2, §4.1, §7 - Status: Accepted - Locked: false - Scope: Video acquisition - Decision: Active-tab video is captured via `chrome.tabCapture.capture()`. - Codec/bitrate binding: `video/webm; codecs=vp9` at `400 000 bps`. - `MediaRecorder` chunk cadence: 2000 ms. - Activation constraint: `tabCapture` is tied to the active tab and requires a user gesture on first invocation; on tab switch the capture re-attaches. - Confirming source: `README.md` §"Технический стек". ## Amendment (Phase 01-stabilize-video-pipeline, 2026-05-15) - AMENDED-BY: Phase 01 CONTEXT.md D-01..D-05 - Replace `chrome.tabCapture.capture()` with `navigator.mediaDevices.getDisplayMedia()` called from the offscreen document. - Offscreen document is created with `chrome.offscreen.Reason.DISPLAY_MEDIA` (replaces `USER_MEDIA`). - Codec/bitrate/timeslice binding unchanged: `video/webm; codecs=vp9` @ 400 000 bps, MediaRecorder timeslice 2000 ms. - Trade-off accepted: SPEC §1 "silent operation" is given up — Chrome's permanent "Sharing your screen" indicator is shown while recording. Phase 1 accepts this in exchange for broader capture coverage and elimination of `tabCapture` user-gesture juggling. - Tab-switch re-attachment clause is REMOVED — `getDisplayMedia` captures a screen/window, not a tab. There is nothing to re-attach. - Manifest permission `tabCapture` is REPLACED with `desktopCapture` (the latter is harmless: `getDisplayMedia` is a web standard API and does NOT actually require `desktopCapture`, but we declare it for clarity per CONTEXT.md D-05). --- ## DEC-004: DOM Capture via rrweb - Source: `Тз расширение фаза1.md` §2, §4.2 - Status: Accepted - Locked: false - Scope: DOM recording - Decision: DOM events are captured via the `rrweb` npm library (`rrweb.record()`), running in the Content Script, with events forwarded to the Service Worker on archive-export request. - Sensitive-data binding: rrweb is configured with `maskInputSelector` to mask `input[type=password]` and `[data-sensitive="true"]`. - Buffer cap binding: 5 000 rrweb events maximum; oldest dropped on overflow (SPEC §8). - Confirming source: `README.md` §"Технический стек", §"Особенности". --- ## DEC-005: Archive Packaging via JSZip - Source: `Тз расширение фаза1.md` §2, §3, §6 - Status: Accepted - Locked: false - Scope: Archive assembly - Decision: ZIP packaging uses the `jszip` npm library bundled as `libs/jszip.min.js` (or the Vite-bundled equivalent). - Archive layout binding: see REQ-archive-layout in `requirements.md`. - Confirming source: `README.md` §"Технический стек". --- ## DEC-006: File Download via `chrome.downloads` - Source: `Тз расширение фаза1.md` §2, §5, §7 - Status: Accepted - Locked: false - Scope: File delivery - Decision: The assembled ZIP is delivered to the user's "Downloads" folder via `chrome.downloads.download()`. No server upload in Phase 1. - Confirming source: `README.md` §"Технический стек". --- ## DEC-007: In-Memory Buffers in Service Worker + Content Script - Source: `Тз расширение фаза1.md` §2, §4.1, §4.2, §4.3 - Status: Accepted - Locked: false - Scope: Storage strategy - Decision: All recorded data (video chunks, rrweb events, user-event log) is held in memory only — video chunks in the Service Worker, rrweb events and user-event log in the Content Script. No `chrome.storage` / IndexedDB persistence for the rolling buffers in Phase 1. - Confirming source: `README.md` §"Технический стек" — "In-memory (Service Worker + Content Script)". --- ## DEC-008: Screenshot via `chrome.tabs.captureVisibleTab` - Source: `Тз расширение фаза1.md` §4.4, §5 - Status: Accepted - Locked: false - Scope: Screenshot capture - Decision: A single PNG screenshot is captured via `chrome.tabs.captureVisibleTab()` at the moment the user clicks "Save archive". The screenshot is bundled as `screenshot.png` inside the ZIP. --- ## DEC-009: WebM Header Retention - Source: `Тз расширение фаза1.md` §4.1, §8 - Status: Accepted - Locked: false - Scope: Video container integrity - Decision: The first chunk emitted by `MediaRecorder` (which contains the WebM container header) MUST be retained in the ring buffer indefinitely; only subsequent chunks rotate out by the 30-second TTL rule. - Rationale (from SPEC §8): WebM without its header is not playable. --- ## DEC-010: Service Worker Keepalive via `chrome.alarms` - Source: `Тз расширение фаза1.md` §8 - Status: Accepted - Locked: false - Scope: Service Worker lifecycle - Decision: To prevent the 30 s idle unload of MV3 Service Workers, a `chrome.alarms` alarm fires every 20 seconds to keep the worker alive. ## Amendment (Phase 01-stabilize-video-pipeline, 2026-05-15) - AMENDED-BY: Phase 01 CONTEXT.md D-17..D-18 - Replace `chrome.alarms`-driven 20 s keepalive with a long-lived `chrome.runtime.connect` port opened from the offscreen document to the Service Worker. The port emits a `PING` message every 25 s; both directions of traffic reset the SW's 30 s idle timer per Chrome 110+ semantics (developer.chrome.com/blog/longer-esw-lifetimes). - The `alarms` permission is removed from `manifest.json` (it is no longer used by Phase 1; Phase 2 / 3 may re-add if needed). - Port lifetime cap (~5 minutes per Chromium-extensions community gist sunnyguan/f94058f66fab89e59e75b1ac1bf1a06e) is mitigated by reconnecting on `onDisconnect` and pre-emptively at ~290 s. - See `.planning/phases/01-stabilize-video-pipeline/01-RESEARCH.md` Pattern 5 for the canonical implementation. --- ## DEC-011: Manifest Permissions Set - Source: `Тз расширение фаза1.md` §7 - Status: Accepted - Locked: false - Scope: Manifest configuration - Decision: `manifest.json` requests exactly: - `permissions`: `tabCapture`, `activeTab`, `downloads`, `scripting`, `storage` - `host_permissions`: `` - Note: `tabCapture` requires explicit user gesture on first activation. --- ## DEC-012: Vite + crxjs + TypeScript Build Toolchain - Source: `README.md` §"Технический стек" - Status: Accepted (DOC-level — not asserted by SPEC) - Locked: false - Scope: Build tooling - Decision: The extension is built with Vite + the `crxjs` plugin in TypeScript. The SPEC does not prescribe a build tool, so this is a DOC-level decision not contradicted by any higher-precedence source. - Provenance caveat: If a future ADR/SPEC selects a different toolchain, this DEC is auto-overridden.