# Phase 4: Harden + clean up (optional) - Context **Gathered:** 2026-05-20 **Status:** Ready for planning ## Phase Boundary Final v1 milestone phase: eliminate accumulated P1/P2 follow-ups, audit residuals, flake instability, and operator-perceptible polish so the v1 release is not just spec-conformant but maintainable + operator-friendly. Per ROADMAP, this phase has NO new v1 requirements — all functional REQs are covered by Phases 1-3. **Charter:** User explicitly chose "Full Phase 4" (most rigorous option). All 4 ROADMAP success criteria + meaningful subset of 12 deferred items from 03-VERIFICATION.md are IN scope. **In scope:** ROADMAP success criteria (4 originally defined): 1. SW state persistence across 30s idle unload/reload (5+ min idle export test produces non-empty video buffer) 2. fetch + XHR network_error capture in events.json (response code >= 400 verification) 3. generate-icons.js ESM/CJS compatibility (`npm run build` + `node generate-icons.js` both succeed under `"type": "module"`) 4. Dead-code grep returns no live references for permissions.request + duplicate offscreen inline string Audit P1 polish (all three — D-P4-02): 5. P1 #11: fetch interception URL extraction fix — `args[0]?.toString()` becomes `[object Request]` when args[0] is a Request object; replace with `args[0] instanceof Request ? args[0].url : String(args[0])` at src/content/index.ts:147 6. P1 #14: navigation URL tracking — replace `history.state?.url` (always "unknown" in apps that don't populate it) with module-level previousUrl tracking; src/content/index.ts:99-109 7. P1 #15: rrweb timestamps semantics — rrweb timestamps are page-load-relative not Unix epoch; normalize against record start time or use `Date.now()` at emit; src/content/index.ts:36-42 Flake stabilization: 8. A29 zip-mtime race fix via cs-injection-world re-target (Plan 03-02 introduced the pattern; ~1/3 runs flake) 9. Pre-existing parallel-vitest Tier-1-build-step race (~1/5 full-suite runs) 10. 2 pre-existing ffprobe/ffmpeg vitest flakes (pre-date Phase 3) Visual / operator-perceptible polish (D-P4-03): 11. getDisplayMedia cursor visibility constraint `video: { cursor: 'always' }` — operator-perceptible UX win (Plan 01-07 obs 2026-05-15); enables pointer-driven bug reproduction 12. Dark-surface logo contrast — Plan 01-10 obs 2026-05-20 CSP / build hygiene: 13. setimmediate polyfill `new Function` in SW chunk via `vite-plugin-node-polyfills` — pre-existing CSP-question; replace with manual polyfill or strip dependency Harness extension polish: 14. A31 extended: grep rrweb/session.json for sentinel absence (one-line ext.) — folds into existing driveA31 Docs hygiene (D-P4-05): 15. ROADMAP backfill for Plans 01-08..01-13 (5 plans inline-tracked but not row-added per Plan 01-13 plan-checker flag #4) Closure: 16. 04-VERIFICATION.md aggregator + alpha re-distribution + milestone v1 close prep **Out of scope (deferred to v2 / v1.1 follow-up milestone):** - rrweb 2.0.0-alpha.4 → stable v2 upgrade research + implementation (D-P3-03 + D-P4-01 charter): alpha-pin stable across 13 plans + 33/33 UAT GREEN; risk vs reward favors v1.1+ deferral - Programmatic SW-realm RAM measurement via chrome.devtools Memory API (D-P3-04 + D-P4-01): research-heavy; A32 best-effort scaffolding + chrome://memory-internals operator path + alpha distribution coverage already accepted for §10 #9 - REQ-password-confidentiality v2 candidate (full rrweb v2 maskInputFn + data-sensitive HTML attribute guards): out of scope per charter shift "we don't care about privacy hardening" 2026-05-20; only revisit if charter reverses ## Implementation Decisions ### Phase 4 scope (D-P4-01 — full Phase 4) - **D-P4-01:** Full Phase 4 scope — all 4 ROADMAP success criteria + meaningful subset of 12 deferred items from 03-VERIFICATION.md. Exclusions per user choice: rrweb v2 upgrade + programmatic SW-RAM measurement. Estimated plan count: 6-8 plans across 2-3 waves. - **Rationale:** User picked "Full Phase 4 (Recommended for v1 quality)" 2026-05-20. Honors no-unilateral-scope-reduction memory + canonical ceremony preference. Phase 4 is the last chance to harden before v1 release; the audit P1/P2 items are real correctness issues (P1 #11 fetch URL is wrong; P1 #14 nav URL always "unknown"; P1 #15 rrweb timestamps wrong unit). Visual polish (cursor + dark-logo) is operator-perceptible quality. ### Audit P1 polish (D-P4-02 — all three) - **D-P4-02:** Address all three audit P1 correctness items (#11 + #14 + #15) in a single dedicated plan or two cohesive plans. - **Rationale:** All three are correctness fixes in src/content/index.ts; surgical scopes per item; high ratio of fix-value to fix-cost. User chose "All three (Recommended)" 2026-05-20. ### Visual / operator-perceptible polish (D-P4-03 — both items) - **D-P4-03:** Include both cursor visibility + dark-surface logo contrast. - **Rationale:** Cursor visibility is functional (pointer-driven bug reproduction is the highest-signal cue when operator reproduces a click-based bug; current captures lack the cursor entirely per Plan 01-07 obs 2026-05-15). Dark-logo is brand polish (Plan 01-10 obs 2026-05-20). Both are small surgical fixes that improve operator-facing quality before alpha re-distribution. User chose "Both (Recommended)" 2026-05-20. ### Alpha tester feedback integration (D-P4-04 — user-handled out-of-band) - **D-P4-04:** Phase 4 execution proceeds independently of alpha tester feedback. User explicitly: "no no, if something i'll tell you" 2026-05-20 — alpha tester findings, if any, are user-routed via separate channels. Phase 4 does NOT wait for alpha confirmation. After Phase 4 closes, fresh build is re-packaged for testers as the v1-final candidate. - **Rationale:** Faster to v1 close. Honors user's operating mode (they curate operator-side signal themselves). Alpha distribution becomes a post-v1 quality validator + maintenance-window input, not a v1 gate. ### Docs hygiene (D-P4-05 — ROADMAP backfill in) - **D-P4-05:** Include ROADMAP backfill for Plans 01-08..01-13 (5 plans inline-tracked but not row-added per Plan 01-13 plan-checker flag #4). Small surgical docs polish; ~15 min. - **Rationale:** ROADMAP rows are the canonical reference document; keeping it accurate is hygiene. User chose "Include" 2026-05-20. ### Claude's Discretion - **Plan organization:** Planner picks 6-8 plan split. Suggested grouping: - 04-01: Bug / flake stabilization wave (A29 race + parallel-vitest race + 2 ffprobe flakes — 4 atomic tasks) - 04-02: Audit P1 polish (#11 fetch + #14 nav URL + #15 rrweb timestamps — cohesive content-script polish) - 04-03: SW state persistence (5min idle test — ROADMAP SC #1; harness extension) - 04-04: fetch + XHR network_error harness extension (ROADMAP SC #2; extends driveA30 or new driveA33) - 04-05: generate-icons ESM/CJS + dead-code grep + setimmediate polyfill (ROADMAP SC #3+#4 + CSP hygiene — build-side hardening) - 04-06: Visual polish — cursor visibility constraint + dark-logo contrast (operator-perceptible) - 04-07: A31 extension (one-line rrweb session.json sentinel grep) — could fold into 04-02 - 04-08: VERIFICATION.md aggregator + ROADMAP backfill + alpha re-distribution + milestone v1 close prep Planner may consolidate (e.g., fold 04-07 into 04-02; merge 04-05 + 04-06 if cohesive) for 6-7 plans target. - **Wave structure:** Likely 3-4 waves; sequential where files_modified overlap (Phase 2 + Phase 3 lessons cited). - **Harness assertion numbering:** A33+ for new Phase 4 harness assertions (continues A29-A32 sequence from Phase 3). - **Pre-checkpoint bundle gates:** Same 6/6 inventory per saved memory `feedback-pre-checkpoint-bundle-gates.md` (Tier-1 grep + SW CSP-safety + Node-globals + DOM-globals + manifest + en↔ru parity). - **Tier-1 FORBIDDEN_HOOK_STRINGS:** stays at 12 unless A33+ needs new __MOKOSH_UAT__-gated symbols (audit at plan time; flag if increment needed). - **CSP-safety mitigation for setimmediate polyfill:** options include (a) strip `vite-plugin-node-polyfills` and provide manual setimmediate=queueMicrotask polyfill, (b) configure plugin to use polyfills.builtins.setimmediate manually, (c) document acceptance with explicit CSP-allow rationale. Planner / researcher picks based on impact. - **CSV / data file modifications:** none expected; Phase 4 is mostly src/* tweaks + tests/* additions + docs. ## Canonical References **Downstream agents MUST read these before planning or implementing.** ### Phase scope + acceptance source - `.planning/ROADMAP.md` §"Phase 4: Harden + clean up (optional)" — 4 ROADMAP success criteria verbatim + originally-framed P1/P2 list - `.planning/REQUIREMENTS.md` (no new REQs in Phase 4; Phase 4 verifies tech-debt cleanup against already-shipped REQs) - `.planning/PROJECT.md` — Validated section (all v1 REQs from Phase 1+2+3) + DEC-* table + Active section showing Phase 4 backlog - `.planning/phases/03-spec-10-smoke-verification-dom-event-log-verification/03-VERIFICATION.md` "Forward-Looking Deferred Items" table — 12 items inventory; Phase 4 cherry-picks ~10 ### Audit residuals + bug-fix targets (read for P1 polish per D-P4-02) - `.planning/intel/audit-residuals.md` or original audit document — P1 #11, P1 #14, P1 #15 source descriptions (if absent, retrieve from prior session intel) - `src/content/index.ts:147` — fetch interception (P1 #11 target) - `src/content/index.ts:99-109` — navigation URL tracking (P1 #14 target; module-level previousUrl needed) - `src/content/index.ts:36-42` — rrweb buffer cleanup timestamps (P1 #15 target; epoch vs page-load-relative) ### Flake stabilization targets - `.planning/phases/03-spec-10-smoke-verification-dom-event-log-verification/03-02-SUMMARY.md` "Issues Encountered" — A29 zip-mtime race documented; cs-injection-world is natural fix candidate - `.planning/phases/03-spec-10-smoke-verification-dom-event-log-verification/03-03-SUMMARY.md` — A29 race confirmed pre-existing; not Plan 03-03 introduced - `tests/uat/lib/harness-page-driver.ts` — findLatestZip at ~line 1395; A29 race-condition origin - `tests/uat/extension-page-harness.ts` — assertA29 + assertA30 + assertA31 + (assertA32 absent — A32 is host-side only) - `tests/uat/harness.test.ts` — orchestrator; driveA29Wrapped + driveA30Wrapped + driveA31Wrapped + driveA32 entries ### ROADMAP SC targets - ROADMAP SC #1 — SW state persistence: src/background/index.ts (initialize + keepalivePort + chrome.runtime.onStartup); needs new harness for 5-min idle test - ROADMAP SC #2 — fetch + XHR network_error: src/content/index.ts (setupFetchInterception + setupXHRInterception if exists); existing A30 covers click+input+navigation+js_error+network_error from synthetic triggers — Phase 4 SC #2 verifies the real-page failing-fetch path empirically - ROADMAP SC #3 — generate-icons ESM/CJS: `generate-icons.js` at repo root; package.json `"type": "module"` setting - ROADMAP SC #4 — dead-code grep: ripgrep for `permissions.request` + duplicate offscreen inline string in src/ ### Visual polish targets - `src/offscreen/recorder.ts` getDisplayMedia call site — add `video: { cursor: 'always' }` per Chrome `CursorCaptureConstraint` enum (per Plan 01-07 obs 2026-05-15) - `src/shared/brand/mokosh-mark.svg` + `src/welcome/welcome.css` (or wherever logo renders on dark surfaces) — Plan 01-10 obs 2026-05-20 ### CSP / build hygiene - `vite.config.ts` — vite-plugin-node-polyfills config (setimmediate polyfill source) - `dist/assets/index.ts-*.js` (SW chunk) — `new Function` source confirmation - `.planning/phases/01-stabilize-video-pipeline/deferred-items.md` — pre-existing acceptance log ### Prior-phase pattern + closure precedents - `.planning/phases/03-spec-10-smoke-verification-dom-event-log-verification/03-PATTERNS.md` — Approach B harness extension pattern (still canonical for new harness work in Phase 4) - `.planning/phases/02-stabilize-export-pipeline/02-VERIFICATION.md` — T5 override template structure - `.planning/phases/03-spec-10-smoke-verification-dom-event-log-verification/03-VERIFICATION.md` — 4-override template structure - `.planning/phases/01-stabilize-video-pipeline/01-13-SUMMARY.md` — Approach B harness ESTABLISHED - `.planning/phases/02-stabilize-export-pipeline/02-04-SUMMARY.md` — A24-A28 extension precedent - `.planning/phases/03-spec-10-smoke-verification-dom-event-log-verification/03-02-SUMMARY.md` — cs-injection-world pattern (chrome.tabs.create + chrome.scripting.executeScript world:'ISOLATED'); content-script realm access pattern ### Decision / operating-principle references - `/home/parf/.claude/projects/-home-parf-projects-work-repremium/memory/feedback-trust-harness-over-manual-uat.md` — harness coverage canonical; operator empirical reserved for genuinely non-automatable cases (A32 RAM is the canonical example) - `/home/parf/.claude/projects/-home-parf-projects-work-repremium/memory/feedback-pre-checkpoint-bundle-gates.md` — 6/6 standard inventory before checkpoint - `/home/parf/.claude/projects/-home-parf-projects-work-repremium/memory/feedback-gsd-ceremony-for-fixes.md` — debug discoveries route through /gsd-debug; no hot-edits - `/home/parf/.claude/projects/-home-parf-projects-work-repremium/memory/feedback-no-unilateral-scope-reduction.md` — user validates every scope decision ### Charter / v1 closure - 2026-05-20 charter shift: "we don't care about privacy hardening" → REQ-password-confidentiality Out of Scope v1 - v1 close target: after Phase 4, fresh alpha re-distribution → tester confirmation → v1.0 tag + release notes (separate workstream; not Phase 4 plan) ## Existing Code Insights ### Reusable Assets - **Approach B harness pattern**: any new Phase 4 harness assertions (A33+) follow the established assertA*/driveA*/orchestrator-wiring pattern. cs-injection-world adaptation (Plan 03-02) is canonical for content-script realm access. - **findLatestZip helper** (`tests/uat/lib/harness-page-driver.ts:~1395`): mtime-sort archive selection; A29 race fix target. - **JSZip + jszip helpers** (`tests/uat/lib/zip.ts`): archive parsing for harness assertions. - **`__MOKOSH_UAT__` Vite-define-token**: gates test-only surfaces from production bundle (only needed if new FORBIDDEN_HOOK_STRINGS entries introduced). - **Pre-checkpoint bundle gates pattern** (Plan 03-05 Task 1): 6/7 gate run before VERIFICATION.md write; Plan 04-08 (or equivalent closure plan) follows this. ### Established Patterns - **Sequential wave decomposition for file-overlap** (Phase 2 lesson + Phase 3 lesson): plan-checker should catch `files_modified` overlap within a wave; if detected, decompose into sequential waves. - **VERIFICATION.md override pattern**: documented in Phase 2 (T5) + Phase 3 (4 overrides incl. §10 #9). For Phase 4, success-criteria verification may use overrides for items already-verified-in-Phase-3 (e.g., dead-code grep is mostly checking work already done). - **CON-event-log-schema** (REQUIREMENTS.md): 5 event types; Phase 4 P1 #11/#14/#15 fixes affect navigation + fetch fields without changing schema shape. - **STATE.md CLI bug**: `state.record-session` flips status to "completed" when total/completed plan counts match — recurring bug fixed via manual STATE.md edit each phase (Phase 2 + Phase 3 + Phase 3-discuss + Phase 3-UI). Phase 4 will hit this 1-2 more times. ### Integration Points - **src/content/index.ts**: locus of P1 #11/#14/#15 fixes; existing event-log emit + fetch interception + navigation tracking - **src/background/index.ts**: locus of SW state persistence work (initialize + chrome.runtime.onStartup + keepalivePort) - **src/offscreen/recorder.ts**: locus of getDisplayMedia cursor:'always' constraint - **vite.config.ts** + dist/ build output: locus of setimmediate polyfill replacement - **generate-icons.js** + package.json `"type": "module"`: locus of ESM/CJS compat fix ## Specific Ideas - **A29 cs-injection-world fix:** the proven Plan 03-02 pattern — `chrome.tabs.create({url: 'https://example.com'})` + `chrome.scripting.executeScript({world: 'ISOLATED', target: {tabId}, func: probeDOMMutation})` — re-target the rrweb probe surface so the tab being captured is the harness probe page (eliminates iana.org leftover read race). - **P1 #11 fetch fix specific code change:** `args[0]?.toString()` → `args[0] instanceof Request ? args[0].url : String(args[0])`. Add unit test for both string-arg and Request-arg cases. - **P1 #14 nav URL fix:** add module-level `let previousUrl = location.href;` in src/content/index.ts; update before each navigation event capture; use `previousUrl` in the emit instead of `history.state?.url`. - **P1 #15 rrweb timestamps:** use `Date.now()` at emit time as the canonical epoch source; rrweb's internal page-load-relative timestamps stay rrweb-internal but the wrapper writes Unix epoch for events.json. - **5min idle test pattern (ROADMAP SC #1):** Puppeteer `await new Promise(r => setTimeout(r, 5 * 60 * 1000))` after recorder start; then SAVE; assert `video/last_30sec.webm` exists in archive + size > 0 bytes. Long-running test; consider parallel skip + dedicated CI lane. - **fetch+XHR network_error verification (ROADMAP SC #2):** harness fires `fetch('https://example.com/does-not-exist-404')` + `xhr.open('GET', '/404-also'); xhr.send();` from probe HTML; SAVE; assert events.json contains 2 network_error entries with response code >= 400 (extends driveA30 or adds A33). - **generate-icons ESM/CJS fix (SC #3):** package.json `"type": "module"` means `.js` files are ESM; generate-icons.js uses `require('fs')` (CJS); rename to `generate-icons.cjs` OR rewrite to ESM (`import fs from 'node:fs'`). Either works. - **Dead-code grep (SC #4):** `rg 'permissions\.request' src/` should return 0 hits (was removed in Phase 1 Plan 01-05); `rg '' src/ vite.config.ts` should return 0 hits (offscreen duality removed in Phase 1 Plan 01-06). - **Cursor visibility implementation:** `getDisplayMedia({video: {cursor: 'always'}, audio: ...})` at the getDisplayMedia call site in src/offscreen/recorder.ts (or wherever the constraint is built per D-13 restart-segments architecture). - **Dark-logo contrast:** options — (a) add `prefers-color-scheme: dark` media query in welcome.css with alt-color SVG, (b) swap to a contrast-tested neutral palette, (c) wrap logo in white-circle background. Designer-leaning; planner-side choice. - **setimmediate polyfill replacement:** `globalThis.setImmediate = globalThis.setImmediate || ((fn, ...args) => queueMicrotask(() => fn(...args)));` inline in SW entry — bypasses vite-plugin-node-polyfills entirely. ## Deferred Ideas ### To v1.1 / v2 maintenance milestone (post-v1 close) - **rrweb 2.0.0-alpha.4 → stable v2 upgrade** (D-P3-03 deferral; alpha-pin proven stable through 13 plans + 33/33 UAT GREEN; reaffirmed Phase 4 D-P4-01) - **Programmatic SW-realm RAM measurement** via chrome.devtools Memory API (D-P3-04 deferral; reaffirmed Phase 4 D-P4-01; A32 best-effort + chrome://memory-internals + alpha distribution coverage accepted as v1 closure) - **REQ-password-confidentiality v2 candidate**: full rrweb v2 maskInputFn + data-sensitive HTML attribute guards (D-P3-02 deferral; only revisit if charter reverses on "we don't care about privacy hardening") - **Per-plan ROADMAP rows for any Phase 2/3 plans inline-tracked but not row-added** (if any beyond Plans 01-08..01-13 catch-up) - **Alpha-tester findings integration**: any cycle-1-style debugs from current alpha (mokosh-build-2026-05-20-6dbed91.zip) or Phase 4 re-distributed build route through separate maintenance window — user explicitly handles operator-side signal out-of-band per D-P4-04 ### To v2 / SRV milestone (per SPEC §9 Out of Scope) - Server upload of captured archives (SRV-01) - AI-driven diagnostics (SRV-02) - Automatic ticket creation (SRV-03) - Analytics dashboard (SRV-04) - Audio recording (CAP-01) - DOM-event masking + privacy hardening reactivation (if v2 charter reverses) --- *Phase: 04-harden-clean-up-optional* *Context gathered: 2026-05-20*