` and ``; planner-side confirms the head + tokens.css link remain untouched. |
| A6 | Phase 3 plans can stay at FORBIDDEN_HOOK_STRINGS = 12 entries (no new __MOKOSH_UAT__ symbol required) | Pitfall 5 / Standard Stack | MEDIUM — most likely true (rrweb + chrome.tabs + chrome.downloads are already production surfaces), but if Plan 03-04 scaffolding requires a new bridge op (e.g., `get-page-metrics` from offscreen → harness), that would add 1-2 entries. Surface in plan-checker pass. |
| A7 | `chrome://memory-internals` is available on Chrome stable 124+ (operator-facing path) | RAM operator instructions / D-P3-04 | LOW — chrome://memory-internals is a stable internal URL since Chrome 80+. The `chrome://extensions/` service-worker memory display (Inspect views > service worker > Memory) is the canonical alternative. |
| A8 | The harness host can read the assembled zip's `rrweb/session.json` and `logs/events.json` via existing `findLatestZip` + JSZip pattern (Plan 02-04 precedent) | Don't Hand-Roll / Pattern 1 | VERIFIED — Plan 02-04 driveA26 + driveA28 already do exactly this for meta.json + zip-layout; rrweb/session.json + logs/events.json are sibling entries in the canonical 5-entry layout. |
**If the table is mostly LOW risk:** confirmed. The only MEDIUM is A6; planner-side flag.
## Open Questions (RESOLVED)
1. **Wave structure: parallel or sequential for Plans 03-01..04?**
- What we know: Plan 02-04 ran A24-A28 sequentially within a single plan (one author). Phase 3's 5-plan structure is finer-grained per D-P3-01.
- What's unclear: Do Plans 03-01, 03-02, 03-03 modify the SAME `tests/uat/extension-page-harness.ts` + `tests/uat/lib/harness-page-driver.ts` files in ways that would conflict on parallel execution? CONTEXT.md "Claude's Discretion" defers to plan-time `files_modified` overlap audit.
- Recommendation: Default sequential (03-01 → 03-02 → 03-03 → 03-04 → 03-05) unless plan-checker confirms zero overlap. Phase 2 lesson cited in CONTEXT.md is "plan-checker should catch overlaps"; honor it.
2. **A29 chaining: own SAVE vs. chain off A28?**
- What we know: A26 + A28 chain off A25's already-completed SAVE per Plan 02-04 (no new SAVE). A27 owns its SAVE because the tab tracker needs the multi-tab onActivated events to fire BEFORE dispatch.
- What's unclear: Does the probe-page DOM injection happen before the existing harness page's recorded session, or before a separate session? rrweb starts on `start()` (content-script init) — by the time the harness page is loaded, rrweb is already recording.
- Recommendation: A29 chains off A28's already-completed SAVE if the DOM mutations happen during the rolling 30s recording window before A25's SAVE. If timing-fragile, A29 owns its SAVE with `setupFreshRecording` + segment-settle, mirroring A27. Plan 03-01 planner picks based on smoke-test.
3. **Plan 03-04 Page.metrics scaffolding: include or skip?**
- What we know: D-P3-04 says "if practical without research budget". This research budget is exhausted; Page.metrics IS practical (single API call); scaffolding is ~30 lines.
- What's unclear: Is the resulting page-realm-only measurement value worth the diagnostic copy overhead? Operator-facing chrome://memory-internals is unaffected.
- Recommendation: SHIP the scaffolding (low cost). Output is informational only; explicit diagnostic copy ("page-realm only; SW context excluded") avoids over-interpretation. Tier-1 inventory unaffected (Page.metrics is host-side; not bundled).
## Environment Availability
| Dependency | Required By | Available | Version | Fallback |
|------------|------------|-----------|---------|----------|
| Node.js + npm | All harness tooling | ✓ | (project pin) | — |
| Puppeteer ^25 | UAT harness driver | ✓ | 25.0.2 (package.json) | — |
| Chrome / Chromium (Puppeteer-bundled) | Real-Chrome target for harness | ✓ | Puppeteer-bundled stable | — |
| rrweb 2.0.0-alpha.4 + @rrweb/types | DOM event verification (Plan 03-01) | ✓ | Pinned (package.json:11) | — |
| JSZip | Archive parsing in driveA29+ | ✓ | 3.10.1 | — |
| tsx | TS runner | ✓ | 4.22.1 | — |
| vitest | Unit-test layer (Tier-1 grep gate) | ✓ | ^4 | — |
| chrome://memory-internals (operator) | §10 #9 RAM check | ✓ (Chrome stable 80+) | — | chrome://extensions service-worker Memory tab |
| ffprobe / ffmpeg | §10 #7 video playback re-verification (already covered in Phase 1 VERIFICATION) | Already verified Phase 1 closure | — | — |
**Missing dependencies with no fallback:** None. Phase 3 introduces zero new dependencies.
**Missing dependencies with fallback:** None.
## Validation Architecture
> Required per `.planning/config.json:workflow.tdd_mode = true` (workflow.nyquist_validation absent — treat as enabled).
### Test Framework
| Property | Value |
|----------|-------|
| Framework | vitest ^4 (171/171 GREEN post Phase 2) + Puppeteer ^25 UAT harness (29/29 GREEN) |
| Config file | `vite.config.ts` + `vite.test.config.ts` (defines `__MOKOSH_UAT__`); `vitest.config.ts` if present (project pins via package.json scripts) |
| Quick run command | `npm test` (vitest) for unit tier; `SKIP_PROD_REBUILD=1 HEADLESS=1 npm run test:uat` for harness tier |
| Full suite command | `npm test && npm run test:uat` (sequential — UAT depends on `npm run build:test` artifact) |
### Phase Requirements → Test Map
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|--------|----------|-----------|-------------------|-------------|
| REQ-install-clean | §10 #1 fresh build loads unpacked w/o errors | aggregator (existing tests) | `npm test` covers manifest + i18n + no-remote-fonts | ✅ tests/i18n/manifest-i18n.test.ts + tests/build/no-remote-fonts.test.ts |
| REQ-rrweb-dom-buffer | §10 #4 rrweb records meta+full+incremental on probe page | UAT harness (Plan 03-01 A29) | `npm run test:uat` (post-Plan 03-01) | ❌ Wave 0 (A29 to be created) |
| REQ-user-event-log | §10 #5 events.json contains 5 UserEvent types | UAT harness (Plan 03-02 A30) | `npm run test:uat` (post-Plan 03-02) | ❌ Wave 0 (A30 to be created) |
| (§10 #8 PARTIAL per D-P3-02) | Password sentinel absent from events.json | UAT harness (Plan 03-03 A31 negative-assert) | `npm run test:uat` (post-Plan 03-03) | ❌ Wave 0 (A31 to be created) |
| (§10 #9 best-effort per D-P3-04) | RAM scaffolding via Page.metrics OR operator chrome://memory-internals | UAT harness (Plan 03-04 A3X, optional) + manual-only operator step | `npm run test:uat` (post-Plan 03-04) + operator | Optional Wave 0 (A3X may or may not be created) |
### Sampling Rate
- **Per task commit:** `npm test` (~30s for full vitest suite)
- **Per wave merge:** `npm run test:uat` (~95s; Plan 02-04 baseline)
- **Phase gate:** `npm test && npm run test:uat` both GREEN before `/gsd-verify-work 3`
### Wave 0 Gaps
- [ ] `tests/uat/extension-page-harness.ts` — append probe HTML injection helper + assertA29..A31 (+ optional A3X) — Plan 03-01..04
- [ ] `tests/uat/lib/harness-page-driver.ts` — append driveA29..A31 (+ optional A3X) + EventType import — Plan 03-01..04
- [ ] `tests/uat/harness.test.ts` — import + drivers[] push + banner — Plan 03-01..04
- [ ] `tests/uat/extension-page-harness.html` — append probe HTML below existing `` + ``; preserve tokens.css link — Plan 03-01
*(No new test framework install required. No new vitest test files anticipated — all new assertions live in the UAT harness tier.)*
## Security Domain
> `security_enforcement` is not explicitly set in `.planning/config.json` — treating as default-enabled.
### Applicable ASVS Categories
| ASVS Category | Applies | Standard Control |
|---------------|---------|-----------------|
| V2 Authentication | no | Extension is local-only; no auth surface |
| V3 Session Management | no | Local-only; no remote sessions |
| V4 Access Control | partial | MV3 permissions baseline (DEC-011 + Amendment 1) — verified via existing `manifest-i18n.test.ts` |
| V5 Input Validation | yes | `URL_SCHEME_ALLOW` regex at `src/background/tab-url-tracker.ts:79` validates URL schemes; probe page inputs are not user input from a security boundary perspective |
| V6 Cryptography | no | No cryptography in Phase 1 v1 surface |
| V7 Error Handling and Logging | partial | Logging via `ContentLogger`; no PII expected (passwords filtered) |
| V8 Data Protection | partial | Password filter at `src/content/index.ts:82` is the v1 minimum per D-P3-02; full masking deferred to Phase 4+ if charter reverses |
### Known Threat Patterns for Phase 3
| Pattern | STRIDE | Standard Mitigation |
|---------|--------|---------------------|
| Test-only hook surface leaking to production bundle | Information Disclosure | Tier-1 FORBIDDEN_HOOK_STRINGS gate (12 entries; unit-mirror + UAT A0 mirror); `__MOKOSH_UAT__` Vite define-token tree-shake; verified per Plan 02-04 SUMMARY |
| Probe page sentinel value (e.g., "secret-do-not-log-123") leaking via grep mismatch | Information Disclosure | Sentinel is a fixed test constant, not a real secret; absence-assertion proves filter fires; logged to event log triggers explicit RED |
| New chrome.* permission grant for probe-page composition | Elevation of Privilege | Phase 3 plans explicitly stay at DEC-011 + Amendment 1 baseline (8 permissions); no new manifest changes |
| rrweb data buffer growth past 5000 events / 10min cap during probe | Denial of Service | `cleanupOldEvents` at `src/content/index.ts:27-50` already enforces (oldest-dropped); Plan 03-01 probe HTML interaction is brief (~500ms) and far below the cap |
## Sources
### Primary (HIGH confidence)
- npm registry verified 2026-05-20 via `npm view rrweb dist-tags`: `latest=2.0.0-alpha.4, alpha=2.0.0-alpha.20, beta=1.0.0-beta.2`
- @rrweb/types EventType enum verified via `node_modules/@rrweb/types/dist/index.d.ts`: `{DomContentLoaded:0, Load:1, FullSnapshot:2, IncrementalSnapshot:3, Meta:4, Custom:5, Plugin:6}`
- Plan 02-04 SUMMARY (`.planning/phases/02-stabilize-export-pipeline/02-04-SUMMARY.md`) — most recent Approach B harness precedent; 29/29 UAT GREEN
- Plan 01-13 SUMMARY — Approach B foundation; FORBIDDEN_HOOK_STRINGS pattern
- Plan 01-14 SUMMARY — single-assertion plan precedent (A23); 7-file atomic-commit shape
- `src/content/index.ts` — production wiring being verified (read-only for Phase 3)
- `src/shared/types.ts:124-131` — UserEvent type contract
- `tests/uat/extension-page-harness.ts:2851-3413` — assertA24-A28 implementations (latest pattern)
- `tests/uat/lib/harness-page-driver.ts` — driveA* + findLatestZip + JSZip pattern
- `tests/background/no-test-hooks-in-prod-bundle.test.ts:108-126` — Tier-1 inventory (12 entries)
- 03-CONTEXT.md (D-P3-01..04 locked decisions + canonical_refs)
- 03-UI-SPEC.md (null-spec; probe-page conventions; FORBIDDEN_HOOK_STRINGS lockstep guidance)
### Secondary (MEDIUM confidence)
- Puppeteer official docs: https://pptr.dev/api/puppeteer.page.metrics (Page.metrics + JSHeapUsedSize semantics)
- Puppeteer official docs: https://pptr.dev/guides/chrome-extensions (MV3 SW target acquisition pattern)
- MDN: https://developer.mozilla.org/en-US/docs/Web/API/Performance/measureUserAgentSpecificMemory (SW memory measurement requires COOP+COEP — not viable for MV3 extensions)
- rrweb master/test/utils.ts (reference for assertDomSnapshot pattern — explicitly rejected for charter mismatch)
- W3C Screen Capture spec §6.1 (already cited in Plan 01-14; relevant for §10 #9 if RAM scaffolding research expands)
### Tertiary (LOW confidence — flagged for plan-time validation)
- chrome://memory-internals stability across Chrome stable channel (cited at A7 in Assumptions Log; common operator path but not formally guaranteed)
- WebSearch findings on rrweb 2.0 stable release status — cross-verified with npm registry (HIGH); separate WebSearch hit on "rrweb 2.0 production ready" returned an unrelated commercial-fork article, not the canonical rrweb-io repo
## Metadata
**Confidence breakdown:**
- Standard stack: HIGH — all dependencies present in package.json; versions verified via `npm view`
- Architecture: HIGH — Approach B template from Plan 02-04 is direct precedent; 3 chained-assertion patterns proven
- Pitfalls: HIGH for Pitfalls 1-3 (verified against existing harness + rrweb registry); MEDIUM for Pitfalls 4-6 (research-derived; not yet exercised in this codebase)
- RAM ceiling (§10 #9): MEDIUM — Page.metrics scope verified; SW heap measurement caveat verified; chrome://memory-internals operator instructions are standard but not version-stamped
- rrweb v2 upgrade safety: HIGH — alpha.4 pin status verified directly via npm dist-tags
**Research date:** 2026-05-20
**Valid until:** 2026-06-20 (30 days; stable dependencies; if rrweb 2.0 stable ships in the interim, re-check D-P3-03 deferral)
## RESEARCH COMPLETE