Files
mokosh/.planning/ROADMAP.md
Mark d1ef77a7d1 docs(01-10): state + roadmap + requirements — Plan 01-10 closure
State markers sync after Plan 01-10 closure:

STATE.md:
- progress.completed_plans: 13 → 14
- progress.percent: 93 → 100 (all 14 Phase 1 functional plans complete)
- status: executing (Phase 1 final-closure marker flip pending)
- stopped_at + Last session timestamps refreshed
- Current Position bumped to 14/14 plans complete
- Outstanding Phase 1 gates: Plan 01-10 row marked CLOSED;
  Phase 1 final-closure marker flip listed as remaining work
- Plan 01-10 closure section added mirroring existing 01-12/01-13/01-14
  patterns: 4 wave commits + 5 inter-cycle debug commits + cycle-2 ack
- Performance Metrics: Phase 01 P10 row added (5h, 5 tasks, 14 files)
- Decisions: 3 Plan 01-10 architectural decisions added
  (first-install activation; D-16-toolbar charter preservation;
  three-pipeline DOM population pattern; startVideoCapture D-01
  cleanup gap closure)

ROADMAP.md:
- Plan 01-10 row flipped to [x] with full closure annotations
  (commit chain + harness counts + operator ack date)
- Plan 01-09 row annotated with closure-cycle follow-up debug
  commits (a2dfc8c startVideoCapture no-tab + 4bba679 notifStartup
  text split — both landed during Plan 01-10 closure cycle)
- Phase 1 plans-count narrative updated: "all 14 functional plans
  complete; Phase 1 final-closure marker flip pending"
- Progress table: Phase 1 plans complete 13/14 → 14/14;
  status row updated to "Final-closure marker flip pending"

REQUIREMENTS.md:
- Footer timestamp + closure note updated: Plan 01-10 introduced
  no new functional REQs (it consumed REQ-video-ring-buffer
  already-Complete via Plan 01-07) by adding the first-install
  operator-facing activation surface that complements the always-on
  capture pipeline.
- Phase 1 final functional plan delivered; final-closure marker
  flip pending (REQUIREMENTS / ROADMAP / STATE markers + optional
  /gsd-verify-work 1).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 12:10:56 +02:00

250 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Roadmap: Mokosh
## Overview
The Mokosh codebase is a partially-broken first attempt at SPEC `Тз расширение
фаза1.md`. An external audit identified 7 P0 defects that prevent SPEC §10
acceptance. This roadmap therefore frames Phase 1 as **stabilization to spec**,
not greenfield: phases 13 each remediate a tightly-grouped subset of the P0
defects along sensible commit boundaries; phase 4 runs the SPEC §10 smoke pass
end-to-end. An optional phase 5 absorbs the P1/P2 follow-ups (SW state
persistence, `fetch` interception fix, `meta.json` field hardening,
`generate-icons.js` ESM/CJS, dead-code cleanup).
The journey: **broken-but-installable → playable video → masked DOM + log →
working export → green §10 smoke → harden + clean up**.
## Phases
**Phase Numbering:**
- Integer phases (1, 2, 3): Planned milestone work.
- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED).
Decimal phases appear between their surrounding integers in numeric order.
- [ ] **Phase 1: Stabilize video pipeline** — Collapse offscreen duality, fix MediaRecorder shadow, fix WebM ring buffer playability, replace `chrome.tabCapture` with offscreen `getDisplayMedia` (AMENDED from original DEC-003). **Closed 2026-05-15 then REOPENED 2026-05-16**: the 2026-05-15 closure was based on insufficient operator playback verification; D-13's concat-of-self-contained-segments architecture produces a multi-EBML WebM that plays only ~9 s instead of ~30 s in standards-compliant parsers (mpv, ffmpeg, Chrome HTMLMediaElement). UAT Test 3 retest on 2026-05-16 confirmed via byte-level EBML probe. SPEC §10 #7 not actually satisfied. Plan 01-08 (WebM remux via ts-ebml + webm-muxer) replaces `mergeVideoSegments`'s file-concat with a real single-EBML remux. See `.planning/debug/d13-multi-ebml-concat-unplayable.md`. Option C port-lifecycle refactor (debug session `empty-archive-port-race`) DID land cleanly and is retained. Phase 1 will additionally absorb whole-desktop + auto-start UX work (Plans 01-09/01-10) per the 2026-05-16 amended charter.
- [ ] **Phase 2: Stabilize DOM + event capture privacy** — Migrate rrweb to v2 `maskInputFn`, plug `content/index.ts setupInputLogging` password leak
- [ ] **Phase 3: Stabilize export pipeline** — Restore user-activation gesture in popup, delete dead `permissions.request`, replace base64 `data:` URL with Blob URL minted in offscreen
- [ ] **Phase 4: SPEC §10 smoke verification** — End-to-end install-and-record-and-export pass against all 9 acceptance criteria
- [ ] **Phase 5: Harden + clean up** _(optional)_ — P1/P2 follow-ups: SW state persistence, fetch interception, `meta.json` fields, `generate-icons.js` ESM/CJS, dead-code
## Phase Details
### Phase 1: Stabilize video pipeline
**Goal**: The video ring buffer captures the most recent 30 s of the active
tab's video continuously across tab switches, with a playable WebM header
retained — so that on export the assembled `last_30sec.webm` will play.
**Depends on**: Nothing (first phase). Operates on the existing `offscreen/`
directory + `vite.config.ts` inline string + `src/background/`.
**Requirements**: REQ-video-ring-buffer
**P0 defects addressed**:
- P0-1: Collapse the offscreen duality (`offscreen/index.ts` + inline string in
`vite.config.ts`) into a single source of truth; fix the `mediaRecorder`
shadow that breaks `stopRecording`.
- P0-2: Fix WebM ring buffer playability — single continuous `MediaRecorder`,
2000 ms timeslice per spec (CON-video-codec), cluster-timestamp-based rolling
window honouring the WebM header retention rule (DEC-009).
- P0-3: Make capture always-on with `chrome.tabs.onActivated` /
`chrome.tabs.onUpdated` re-attachment; start on `onInstalled` / `onStartup`,
not on popup open (CON-tab-capture-binding, REQ-video-ring-buffer).
**Success Criteria** (what must be TRUE):
1. [x] There is exactly one source of truth for the offscreen document; rebuilding
`vite.config.ts` does not regenerate a divergent inline duplicate, and
`stopRecording` runs without `mediaRecorder is undefined` shadow errors.
2. [x] With the extension loaded and an operator session active, a `MediaRecorder`
is running on the operator-selected screen/window source. AMENDED 2026-05-15
(D-13 fix-a3 activation): the recorder cycles in 10 s self-contained segments
(stop+restart on the same `MediaStream`) instead of a single continuous
recorder — replaces D-09..D-11 to fix VP9 keyframe orphan-P-frame freezes.
Recording continues unchanged across tab switches (no tab re-attach logic;
AMENDED from the original wording).
3. [x] The in-memory video ring buffer at any instant contains at most 3 of the
most recent 10 s WebM segments (3 × 10 s = 30 s window, no more, no less);
concatenating segments sequentially yields a multi-EBML-header WebM that
Chrome plays end-to-end (SPEC §10 #7 — operator confirmed clean playback
2026-05-15; ffmpeg `-v warning -i fixture -f null -` exit 0 with zero
decoder errors, only expected muxer DTS-monotonicity warnings at segment
join boundaries).
**Plans**: 14 plans (01-01 through 01-14). All 14 functional plans complete; Phase 1 final-closure marker flip pending.
- [x] 01-01-PLAN.md — Doc cascade: amend DEC-003 / DEC-010 / RETIRE constraints / swap manifest permissions (D-A1..D-A6)
- [x] 01-02-PLAN.md — Wave-0 test infrastructure: Vitest install + 4 RED test files + fixtures placeholder
- [x] 01-03-PLAN.md — Offscreen recorder TDD: ring buffer + codec strict-mode + getDisplayMedia + track-ended cleanup; D-13 fallback skeleton pre-staged
- [x] 01-04-PLAN.md — Port keepalive + OFFSCREEN_READY handshake (TDD): replaces alarms keepalive on offscreen side
- [x] 01-05-PLAN.md — SW shrink: delete legacy buffer + alarms + IndexedDB + tabCapture paths; wire SW-side onConnect host
- [x] 01-06-PLAN.md — Build pipeline collapse: delete vite.config.ts inline plugin + top-level offscreen/ dir; declare rollupOptions.input
- [x] 01-07-PLAN.md — Manual smoke + ffprobe D-12 acceptance gate + A3 empirical-playback gate; D-12 + A3 debug sessions resolved mid-execution via pre-staged base64 wire format + D-13 restart-segments; regression fixture committed; SPEC §10 #2/#3/#7 functionally green (Closed 2026-05-15)
- [x] 01-08-PLAN.md — WebM remux via ts-ebml + webm-muxer (replaces D-13 file-concat; closes SPEC §10 #7 playability per debug d13-multi-ebml-concat-unplayable.md)
- [x] 01-09-PLAN.md — Toolbar onClicked direct flow + monitor-only picker + onStartup notification + 3-state badge state machine; closure-by-harness Amendment 2 (Plan 01-13 PASS substitutes for operator UAT). Closure-cycle follow-up debug `a2dfc8c` (startVideoCapture no-tab cleanup; D-01 dead-code removal) + `4bba679` (notifStartup text split into notifStartupCta + notifRecordingStarted) landed during Plan 01-10 closure 2026-05-20.
- [x] 01-10-PLAN.md — Welcome tab (Hero + Loom dial per D-02; first-install onboarding; chrome.runtime.onInstalled + chrome.storage.local flag-gating + chrome.tabs.create + canonical mokosh-mark.svg via Vite ?url import + canonical src/shared/tokens.css @import + chrome.i18n.getMessage for welcomeHeroRu + welcomeHeroEn; harness A15-A17 with A17.7 --mks-rec probe + A17.8 mark-bundling invariant; D-16-toolbar charter preserved). Closure 2026-05-20 via cycle-2 operator ack "All good" + 5 inter-cycle debug fixes (89e1e09 → 49f087f → 8f329d8 → b112cb7 → 4bba679 → d48a715 → 0854baf → a2dfc8c → d21ed17) + brand-rename follow-up "AI Call Recorder" → "Mokosh"; 153/153 vitest + 24/24 UAT GREEN.
- [x] 01-11-PLAN.md — UAT harness Approach-A spike (PIVOTED to 01-13; carries forward Wave 0 infrastructure + Tier-1 grep gate; falsified hypotheses recorded)
- [x] 01-12-PLAN.md — Design integration (R2 Lora self-host, src/shared/tokens.css canonical, 16 i18n keys across en+ru, branded Loom icons replacing Bug A placeholders, manifest i18n + default_locale='en', BADGE_REC_COLOR madder #b2543d, chrome.i18n.getMessage with `|| <const>` fallback, harness A18-A22; operator brand-fit ack 2026-05-20 'all good')
- [x] 01-13-PLAN.md — UAT harness via Approach B (extension-internal-page driver + offscreen synthetic stream; 15/15 GREEN; Plan 01-09 functional closure)
- [x] 01-14-PLAN.md — Picker narrowing via monitorTypeSurfaces:'include' (Chrome 119+ picker enhancement; A23 harness regression)
### Phase 2: Stabilize DOM + event capture privacy
**Goal**: rrweb captures DOM events on typical pages and the user-event log
captures clicks/navigation/network errors — and in neither stream do password
values appear.
**Depends on**: Phase 1 (no functional dependency, but Phase 1 establishes the
"capture is always-on" baseline that this phase plugs into).
**Requirements**: REQ-rrweb-dom-buffer, REQ-user-event-log,
REQ-password-confidentiality
**P0 defects addressed**:
- P0-5: rrweb data-sensitive leak — migrate to rrweb v2 `maskInputFn` (the
legacy `maskInputSelector` is gone in v2.0.0-alpha.4 per `package.json`); fix
the parallel leak in `src/content/index.ts setupInputLogging` so password
field values are dropped at logger entry, not just at rrweb level.
**Success Criteria** (what must be TRUE):
1. On a page containing `<input type="password">` and elements with
`data-sensitive="true"`, rrweb snapshots for that page mask the value of
both kinds of fields (verified by inspecting exported `rrweb/session.json`).
2. On the same page, typing into a password field produces no `input` event
entry containing the typed value in the user-event log
(`logs/events.json`).
3. On a typical page with forms, tables, and a modal, rrweb records DOM
events without throwing in the Content Script console; the event log
captures clicks, navigations (`popstate`/`hashchange`), and network errors
(`fetch` / `XHR` >= 400).
**Plans**: TBD
**UI hint**: yes
### Phase 3: Stabilize export pipeline
**Goal**: A click on "Сохранить отчёт об ошибке" produces a SPEC-conformant ZIP
archive on disk in under 5 s, containing a screenshot taken at click time,
laid out per CON-archive-layout, with `meta.json` per CON-meta-json-schema, and
declared by a manifest carrying exactly the permission set in DEC-011.
**Depends on**: Phase 1, Phase 2 (export consumes the video + rrweb + event-log
buffers established by phases 1 and 2).
**Requirements**: REQ-popup-ui, REQ-screenshot-on-export, REQ-archive-layout,
REQ-meta-json-schema, REQ-archive-export-latency, REQ-manifest-permissions
**P0 defects addressed**:
- P0-4: Restore the user-activation gesture for `getMediaStreamId` by moving
the call to the popup-click handler; delete the dead `permissions.request`
dance that was masking the missing gesture (REQ-popup-ui,
CON-tab-capture-binding).
- P0-6: Replace the base64 `data:` URL download with a Blob URL minted in the
offscreen document — the Service Worker lacks `URL.createObjectURL` (DEC-006,
REQ-archive-export-latency).
**Success Criteria** (what must be TRUE):
1. Opening the popup shows a button reading "Сохранить отчёт об ошибке" with
sub-label "Последние 30 сек видео + 10 мин лога"; clicking it transitions
idle → "Сохраняю..." → "Готово! ✓" → idle (with 3 s revert) and triggers
a `chrome.downloads` download.
2. The downloaded file lands in the user's Downloads folder, named
`session_report_YYYY-MM-DD_HH-MM-SS.zip`, in under 5 seconds from click;
opening it reveals exactly the layout in REQ-archive-layout
(`video/last_30sec.webm`, `rrweb/session.json`, `logs/events.json`,
`screenshot.png`, `meta.json` at the root) with no extra entries.
3. `meta.json` validates against the verbatim CON-meta-json-schema (all 7
fields present, types correct, `timestamp` is ISO-8601 with `Z`).
4. `manifest.json` in `dist/` after `npm run build` declares exactly the
permission set in DEC-011 with no additional or missing entries; loading
unpacked into Chrome produces no permission-related warnings or errors in
`chrome://extensions/`.
**Plans**: TBD
**UI hint**: yes
### Phase 4: SPEC §10 smoke verification
**Goal**: All 9 SPEC §10 acceptance criteria pass against an unpacked load of
the build into a real Chrome instance.
**Depends on**: Phase 1, Phase 2, Phase 3.
**Requirements**: REQ-install-clean (and end-to-end verification of all
preceding REQs)
**P0 defects addressed**:
- P0-7: End-to-end smoke verification against §10. This is a verification
phase, not a new implementation — it confirms the cumulative output of
phases 13 actually satisfies the SPEC.
**Success Criteria** (what must be TRUE):
1. The extension installs into Chrome via "Load unpacked" against `dist/`
with no errors or warnings in `chrome://extensions/`.
2. With the extension loaded and a normal browsing session under way, the
video buffer runs continuously across tab switches and never holds more
than 30 s of footage (confirmed by inspecting the SW console / a debug
export).
3. On a typical page (form + table + modal) rrweb records without throwing,
the event log captures clicks/navigation/network errors, and passwords
are absent from both streams.
4. A click on the popup button produces a ZIP in Downloads in under 5 s; the
ZIP opens; `video/last_30sec.webm` plays in a browser.
5. Background RAM consumption (measured via Chrome Task Manager) does not
exceed 50 MB during a sustained recording session (CON-ram-ceiling).
**Plans**: TBD
### Phase 5: Harden + clean up _(optional)_
**Goal**: Eliminate the P1/P2 follow-ups identified in the audit so that the
codebase is not just spec-conformant but maintainable. This phase has no new
v1 requirements — it improves robustness and removes technical debt around
already-shipped behaviour.
**Depends on**: Phase 4 (do not harden until §10 is green).
**Requirements**: none (no new v1 REQs; all v1 REQs are covered by phases 14)
**P1/P2 items addressed** (informative list from the audit, exact scope
finalized at plan time):
- SW state persistence around the 30 s idle unload edge cases.
- `fetch` interception fix in the network-error path of REQ-user-event-log.
- `meta.json` field hardening (timestamp source, version source, totalEvents
derivation).
- `generate-icons.js` ESM/CJS compatibility with the rest of the toolchain.
- Dead-code cleanup (the `permissions.request` dance removed in Phase 3 may
have stranded helpers; the offscreen duality removed in Phase 1 may have
stranded shims).
- `getDisplayMedia` cursor visibility constraint (`video: { cursor: 'always' }`)
— refines capture quality for diagnostic UX; surfaced during Phase 1 smoke
(2026-05-15) as a user observation. Operator's screen cursor was absent
from captured frames despite being the highest-signal cue when reproducing
pointer-driven bugs. Constraint is opt-in per the `getDisplayMedia` spec
and Chrome implements it via the `CursorCaptureConstraint` enum (`always`
/ `motion` / `never`).
**Success Criteria** (what must be TRUE):
1. After running the extension idle for >5 minutes, then exporting, the
archive still contains a non-empty video buffer (proves SW state
persistence works across one or more SW unload/reload cycles).
2. A page that issues a failing `fetch` (response code >= 400) produces a
`network_error` entry in `events.json`; a failing `XMLHttpRequest` does
too.
3. `npm run build` and `node generate-icons.js` both succeed under the
project's module setting (`"type": "module"` in `package.json`) with no
`require is not defined` or `Cannot use import statement outside a
module` errors.
4. A repo grep for the symbols deleted in phases 1 and 3
(`permissions.request`, the duplicate offscreen inline string) returns no
live references.
**Plans**: TBD
## Progress
**Execution Order:**
Phases execute in numeric order: 1 → 2 → 3 → 4 → 5.
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. Stabilize video pipeline | 14/14 | Final-closure marker flip pending (all functional plans complete; awaiting REQUIREMENTS / ROADMAP / STATE markers + optional /gsd-verify-work 1) | Functional contract closed 2026-05-19 via Plan 01-13 harness PASS; design/brand contract closed 2026-05-20 via Plan 01-12 brand-fit ack; welcome-tab contract closed 2026-05-20 via Plan 01-10 cycle-2 operator ack "All good" + 5 inter-cycle debug fixes |
| 2. Stabilize DOM + event capture privacy | 0/TBD | Not started | - |
| 3. Stabilize export pipeline | 0/TBD | Not started | - |
| 4. SPEC §10 smoke verification | 0/TBD | Not started | - |
| 5. Harden + clean up (optional) | 0/TBD | Not started | - |