20 KiB
Phase 4: Harden + clean up (optional) - Context
Gathered: 2026-05-20 Status: Ready for planning
## Phase BoundaryFinal 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):
- SW state persistence across 30s idle unload/reload (5+ min idle export test produces non-empty video buffer)
- fetch + XHR network_error capture in events.json (response code >= 400 verification)
- generate-icons.js ESM/CJS compatibility (
npm run build+node generate-icons.jsboth succeed under"type": "module") - 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
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-polyfillsand 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_refs>
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.mdor 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 introducedtests/uat/lib/harness-page-driver.ts— findLatestZip at ~line 1395; A29 race-condition origintests/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.jsat 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.tsgetDisplayMedia call site — addvideo: { cursor: 'always' }per ChromeCursorCaptureConstraintenum (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 Functionsource 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)
</canonical_refs>
<code_context>
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_modifiedoverlap 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-sessionflips 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
</code_context>
## 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; usepreviousUrlin the emit instead ofhistory.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; assertvideo/last_30sec.webmexists 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.jsfiles are ESM; generate-icons.js usesrequire('fs')(CJS); rename togenerate-icons.cjsOR 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 '<inline-offscreen-string>' src/ vite.config.tsshould 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: darkmedia 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.
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