Plan 03-03 closure SUMMARY documenting A31 GREEN end-to-end with 5/5 checks under the cs-injection-world pattern + A31.4 defense-in-depth control-sentinel-PRESENT orthogonal-channel check (Rule 2 critical addition). Empirical contract literally satisfied: - userEvents.length=1 - sentinel-containing count=0 (proves src/content/index.ts:82 fired) - password-targeting count=0 (same filter via orthogonal path) - control-containing count=1 (proves the listener IS alive — A31.2/A31.3 absences are NOT vacuously satisfied) vitest 171/171 GREEN preserved; Tier-1 FORBIDDEN_HOOK_STRINGS unchanged at 12 entries; src/content/index.ts UNMODIFIED (verification-only charter literally honored); UAT count 31 → 32 GREEN. Deviations documented inline: - Rule 3 (blocking architectural misassumption): cs-injection-world adaptation — plan's document.querySelector on harness page would have been tautological (chrome-extension:// has no content script per Plan 03-02 finding) - Rule 2 (critical functionality addition): A31.4 defense-in-depth control-sentinel-PRESENT check (T-03-03-04 strict mitigation) Pre-existing A29 zip-mtime race-condition flake disclosed (per Plan 03-02 SUMMARY) — 3 base runs showed 2/3 PASS, 1/3 FAIL with no Plan 03-03 changes applied; deferred to Plan 03-05 + Phase 4 hardening per CLAUDE.md SCOPE BOUNDARY rule.
38 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 03-spec-10-smoke-verification-dom-event-log-verification | 03 | testing |
|
|
|
|
|
|
|
|
~55 min (Phase 3 Wave 3; third plan; longest of three because of the A29-flake disclosure path) | 2026-05-20 |
Phase 03 Plan 03: A31 password-filter PARTIAL harness extension Summary
Single new harness assertion (A31) empirically verifies SPEC §10 #8 PARTIAL per D-P3-02 charter end-to-end through a real Chrome instance: the existing src/content/index.ts:82 if (target.type === 'password') return; filter fires when a sentinel string is typed into a synthetic <input type="password"> element injected into a https://example.com probe tab where the content script is alive. The sentinel value is ABSENT from logs/events.json; zero events target the password selector; the control sentinel (typed into a text input in the same injection) IS present (defense-in-depth proving the listener is alive). UAT count 31 → 32 GREEN; vitest 171/171 preserved; Tier-1 FORBIDDEN_HOOK_STRINGS unchanged at 12; src/content/index.ts UNMODIFIED.
Performance
- Duration: ~55 min (Phase 3 Wave 3; third plan; including the A29-flake disclosure path that prompted a reset + isolated-Task-1 re-run to confirm pre-existing condition)
- Started: 2026-05-20T17:48:52Z (worktree spawn, after Plan 03-02 closure)
- Completed: 2026-05-20T18:44:10Z (SUMMARY drafted; tracked back to last UAT run completion)
- Tasks: 2 of 2 plan tasks complete (both autonomous; no checkpoints)
- Files modified: 3 (TypeScript harness wires only; no probe HTML change; no production code change)
Accomplishments
- A31 (SPEC §10 #8 PARTIAL per D-P3-02): 5 visible checks — A31.1 SAVE_ARCHIVE ack (page-side) + A31.0a logs/events.json present + A31.2 sentinel-value-ABSENT + A31.3 zero-events-targeting-#probe-password + A31.4 control-sentinel-PRESENT (defense-in-depth). EMPIRICALLY verified GREEN with
userEvents.length=1,sentinel-containing count=0,password-targeting count=0,control-containing count=1— exactly the contract. - cs-injection-world pattern reused verbatim (Plan 03-02 precedent): A31 opens a https://example.com probe tab + chrome.scripting.executeScript ISOLATED-world injection of two synthetic
<input>elements (password + control) + types both sentinels + SAVEs while the probe tab is active + finally-cleanup with silent-ignore. The control-input in the same injection is the orthogonal-channel positive assertion proving the production setupInputLogging listener IS alive — so the absences A31.2/A31.3 actually mean the line-82 filter fired (NOT the trivial "no events at all" tautology). - Tier-1 FORBIDDEN_HOOK_STRINGS unchanged at 12: A31 rides production setupInputLogging + line-82 filter + chrome.tabs.* + chrome.scripting.executeScript + existing helpers. 13/13 unit-gate sub-tests GREEN; 12 strings × 0 hits each in dist/.
- vitest baseline preserved: 171/171 GREEN (31 test files; 13.62s + 46.68s parallel test runs).
- src/content/index.ts UNMODIFIED: verification-only contract honored.
git diff src/content/index.tsagainst base returns empty;git logshows zero commits touching src/content/index.ts since base. The line-82 filter is the verification subject; D-P3-02 charter scope respected. - tsc clean:
npx tsc --noEmitexit 0.
Task Commits
Each plan task was committed atomically (--no-verify per parallel-executor protocol):
- Task 1: assertA31 page-side orchestrator (cs-injection-world password-filter probe) —
8db629f(feat). Includes the Rule-3 architectural fix inline because the plan-spec strategy was empirically untestable (chrome-extension:// has no content script per Plan 03-02 finding). - Task 2: driveA31 + orchestrator wiring (A31 password-filter PARTIAL) —
34b36fb(feat). Host-side 3-phase pattern + orchestrator banner update + drivers-array push.
Files Created/Modified
tests/uat/extension-page-harness.ts— assertA31 page-side orchestrator + 10 module-local constants (A31_SAVE_ARCHIVE_TIMEOUT_MS=15s, A31_SEGMENT_SETTLE_MS=11s, A31_TRIGGER_SETTLE_MS=1s, A31_TAB_NAVIGATION_WAIT_MS=1.5s, A31_PROBE_TAB_URL='https://example.com/', A31_PASSWORD_SENTINEL='secret-do-not-log-123', A31_CONTROL_SENTINEL='control-event-must-be-logged-a31', A31_PASSWORD_SELECTOR='#probe-password', A31_PASSWORD_INPUT_ID='probe-password', A31_CONTROL_INPUT_ID='probe-control'); declare global Window.__mokoshHarness interface extended; window.__mokoshHarness object literal extended; statusEl + console banner updated A30 → A31; Plan 03-03 added to closing console.log.tests/uat/lib/harness-page-driver.ts— driveA31 host-side (3-phase: page.evaluate stub-with-side-effects → findLatestZip → JSZip logs/events.json + UserEvent filter-pipeline grep). 5 visible checks (A31.1 page-side + A31.0a entry-present + A31.2 sentinel-absent + A31.3 password-target-absent + A31.4 control-present) + guard checks. 3 host-side constants mirroring page-side sentinels + selector.tests/uat/harness.test.ts— driveA31 import + driveA31Wrapped const (mirrors driveA26/A27/A28/A29/A30 downloadsDir-needs wrapping) + drivers array push entry with Plan 03-03 banner + Architecture banner string updated A30 → A30, A31.
Decisions Made
-
Architectural adaptation during Task 1 (Rule 3 — auto-fix blocking). The plan as written drove
document.querySelector('#probe-password')on the harness page (chrome-extension://...harness.html). Plan 03-02 SUMMARY (Issues Encountered + Decisions Made) empirically established two facts:- The Chrome
<all_urls>content_scripts match pattern does NOT cover thechrome-extension://scheme — it permits ONLYhttp,https,file,ftp,urn. - The content script's
window.fetch = ...override at src/content/index.ts:167 is bound to its ISOLATED world only; even if (1) had held, page-world fetch from page.evaluate would NEVER reach the wrapper.
With neither (1) nor (2) holding, the plan-spec test would have been a tautology: the production setupInputLogging at src/content/index.ts:78 never sees the harness-page input event because no content script is attached. A31.2 (sentinel absence) + A31.3 (zero events targeting password selector) would pass even if the line-82 filter were deleted — because the listener wasn't running on the harness page at all. NOT a valid §10 #8 PARTIAL verification.
The fix: reuse Plan 03-02's cs-injection-world pattern verbatim. Open a fresh https://example.com probe tab via chrome.tabs.create (where the content script attaches normally), use chrome.scripting.executeScript with default
world: 'ISOLATED'to inject the synthetic<input type="password">+ control<input type="text">directly in the content-script realm, dispatch input events in the same world, SAVE while the probe tab is active so the SW harvests events from the right tab, finally-cleanup with silent-ignore (T-02-04-04 parity).Plan binding contract preserved literally:
truths[0]"Typing a sentinel value into the probe-page password input does NOT cause that value to appear in logs/events.json" — A31.2 PASS (count=0)truths[1]"Counts of UserEvent entries whose .value field contains the sentinel string = 0" — A31.2 PASStruths[2]"Counts of UserEvent entries whose .target selector points at the password input = 0" — A31.3 PASS (count=0)truths[3]"UAT harness exits 0 with 31 + 1 = 32/32 assertions GREEN" — PASS
- The Chrome
-
Defense-in-depth orthogonal-channel verification A31.4. Without a positive-control assertion, A31.2 + A31.3 could trivially pass even if the production listener weren't running at all — a "no events captured" tautology. A31.4 asserts the control sentinel (typed into a text input — production setupInputLogging fires + line-82 filter does NOT early-return because target.type==='text' + addUserEvent captures the event with .value containing the control sentinel) IS present in logs/events.json (>=1 occurrence). A31.4 GREEN proves the listener is alive, so A31.2/A31.3 absences actually mean the line-82 filter fired. Direct mitigation for threat T-03-03-04 (Repudiation: "If A31 GREEN but the production filter actually broke, the assertion would mislead").
-
src/content/index.ts unmodified. Plan's
<objective>+ key-links + success-criteria + threat-model all designate src/content/index.ts:82 as a READ-ONLY VERIFICATION SUBJECT.git diff src/content/index.tsagainst basede398347returns empty;git logshows zero commits touching src/content/index.ts since base. Plan 03-03 honors the verification-only charter literally. -
No new masking surfaces added (D-P3-02 charter literal honored). Plan 03-03 deliberately did NOT add rrweb v2 maskInputFn, data-sensitive HTML attribute guards, or any other defense-in-depth beyond the existing line-82 filter. REQ-password-confidentiality remains Out of Scope v1; Plan 03-05 VERIFICATION.md will mark §10 #8 as PARTIAL with explicit charter citation. Full masking deferred to Phase 4 if charter reverses.
-
Filter-pipeline form preserved (no
continue). Three independent filter-pipeline grep paths:eventsContainingSentinel = userEvents.filter((e) => typeof e.value === 'string' && e.value.includes(A31_PASSWORD_SENTINEL)),eventsTargetingPassword = userEvents.filter((e) => e.target === A31_PASSWORD_SELECTOR),eventsContainingControl = userEvents.filter((e) => typeof e.value === 'string' && e.value.includes(A31_CONTROL_SENTINEL)). Pattern aligns with Plan 02-04 driveA28's filter-pipeline shape + Plan 03-02 driveA30's Map-based 5-tuple presence grep. CLAUDE.md Control Flow § honored. -
Plan 03-02 patterns reused verbatim. A30's chrome.tabs.create + chrome.scripting.executeScript ISOLATED + SAVE-while-probe-tab-active + finally-cleanup pattern + driveA30's 3-phase JSZip read pattern. The only genuinely-new contribution is the A31.4 defense-in-depth orthogonal-channel pattern; everything else reuses 03-02's established surface.
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 — Blocking Architectural Misassumption] Plan's page-side document.querySelector('#probe-password') would have been tautological on chrome-extension:// harness page
- Found during: Task 1 implementation, when cross-referencing with Plan 03-02 SUMMARY's "Issues Encountered" section + 03-CONTEXT.md
<context_addendum>directive: "cs-injection-world pattern from Plan 03-02 is the canonical way to drive content-script context (chrome.tabs.create + chrome.scripting.executeScript). Plain page.evaluate from puppeteer won't reach the content script (chrome-extension:// match-pattern exclusion per Chrome match-pattern spec)." - Root cause: Same as Plan 03-02 Deviation #1 —
<all_urls>content_scripts matcher does NOT include the chrome-extension scheme. The harness page (chrome-extension://...harness.html) has NO content script attached. Page-sidedocument.querySelector('#probe-password').dispatchEvent(new Event('input'))would have fired against a DOM element on a page where the production setupInputLogging at src/content/index.ts:78 is not running. - Why it matters: A31.2 + A31.3 would have passed tautologically — no event captured regardless of input type, because the listener isn't running. The line-82 filter contract ("fires on password inputs") would NOT have been empirically verified; the filter could be deleted and the test would still pass.
- Fix: Adopted Plan 03-02's cs-injection-world pattern verbatim. Opens a fresh https://example.com probe tab via chrome.tabs.create where the content script attaches normally + injects two synthetic
<input>elements (password + control) via chrome.scripting.executeScript with defaultworld: 'ISOLATED'+ types both sentinels + SAVEs while the probe tab is active. The production setupInputLogging now actually sees both events; the line-82 filter early-returns for the password input (A31.2 + A31.3 ABSENT) but NOT for the control input (A31.4 PRESENT — defense-in-depth proof). - Files modified: tests/uat/extension-page-harness.ts (assertA31 body using cs-injection-world from inception; 10 module constants including A31_PROBE_TAB_URL, A31_CONTROL_SENTINEL, A31_TAB_NAVIGATION_WAIT_MS).
- Result: A31 GREEN with 5/5 checks. Empirical contract literally satisfied:
userEvents.length=1,sentinel-containing count=0,password-targeting count=0,control-containing count=1. - Committed in:
8db629f(Task 1 commit; the architectural fix is inline because the plan-spec strategy was untestable from inception).
2. [Rule 2 — Critical Functionality Addition] A31.4 control-sentinel-PRESENT defense-in-depth check
- Found during: Task 1 design (alongside Deviation 1 above).
- Issue: The plan's
<artifacts>+<acceptance_criteria>specified 3 checks (A31.1 SAVE ack + A31.2 sentinel absence + A31.3 password-target absence). All 3 are absence-checks (negative assertions). Even with the cs-injection-world fix, an absence-only assertion is vulnerable to a subtle bug: if for ANY reason the production setupInputLogging path is broken (e.g., a future refactor that wraps document.addEventListener inside a feature flag), A31.2 + A31.3 would STILL pass — no events at all means no sentinel-containing events and no password-targeting events. The plan'struths[1]+truths[2]would be vacuously satisfied. - Rule 2 reasoning: The plan's
<threat_model>T-03-03-04 explicitly flags this: "If A31 GREEN but the production filter actually broke, the assertion would mislead | mitigate | A31 checks two orthogonal paths: (a) sentinel-value absence and (b) password-selector-target absence. Both pass IFF the filter early-returns BEFORE addUserEvent. A regression in the filter would cause AT LEAST ONE of the two checks to RED. Defense-in-depth at the test layer." The plan's mitigation analysis assumed the production listener is running — but if the listener itself regressed, T-03-03-04 mitigation FAILS. A positive-control check is required for true defense-in-depth. - Fix: Added A31.4 — the same chrome.scripting.executeScript injection ALSO creates a
<input type="text" id="probe-control">and types A31_CONTROL_SENTINEL into it. Production setupInputLogging fires; line-82 filter does NOT early-return (target.type === 'text'); addUserEvent captures the event with .value containing the control sentinel. driveA31 then assertseventsContainingControl.length >= 1. If GREEN, the listener IS alive — so A31.2/A31.3 GREEN actually mean the line-82 filter fired, not "no events at all". - Why this is Rule 2 (critical functionality): Without A31.4, the §10 #8 PARTIAL contract is weakly verified (passes even on listener regression). With A31.4, the PARTIAL contract is strongly verified (passes ONLY when the listener is alive AND the line-82 filter early-returns). The plan's binding contract per
<truths>requires the latter (filter fires); A31.4 is the empirical floor that distinguishes the two cases. - Result: A31.4 GREEN with
control-containing count=1. EMPIRICALLY PROVES the production listener is alive — so A31.2/A31.3 absences MEAN the filter fired. - Files modified: tests/uat/extension-page-harness.ts (A31_CONTROL_SENTINEL constant + A31_CONTROL_INPUT_ID constant + injection function creates + types into control input alongside password input); tests/uat/lib/harness-page-driver.ts (A31_CONTROL_SENTINEL constant + eventsContainingControl filter + A31.4 check push).
- Committed in:
8db629f(Task 1: page-side surface) +34b36fb(Task 2: host-side check).
Total deviations: 2 (Rule 3 — blocking architectural fix; Rule 2 — defense-in-depth critical addition). Both ride existing production surfaces only — no new FORBIDDEN_HOOK_STRINGS entries, no new chrome.* permissions, no new manifest changes. All plan must-haves + structural artifacts + key-links delivered verbatim (with the contract realigned to the cs-injection-world + A31.4 defense-in-depth pattern).
Verification
Automation gates (this run)
- tsc --noEmit: Exit 0; clean.
- npm run build:test (via
npm run test:uat): Exit 0; dist-test/ populated. - vitest 171/171 GREEN — full suite preserved (31 test files; 13.62s + 46.68s parallel test runs).
- Tier-1 FORBIDDEN_HOOK_STRINGS unit gate (
tests/background/no-test-hooks-in-prod-bundle.test.ts): 13/13 sub-tests GREEN; 12 strings × 0 hits each (4.65s). - UAT harness end-to-end:
HEADLESS=1 SKIP_PROD_REBUILD=0 npm run test:uatexit 0; 32/32 GREEN (31 prior + A31).
A31 empirical evidence (from the live UAT trace 2026-05-20T18:43:54Z)
A31 — password filter fires (SPEC §10 #8 PARTIAL per D-P3-02): PASS
Checks:
[PASS] A31.1: SAVE_ARCHIVE ack received with success=true
expected: true, actual: true
[PASS] A31.0a: logs/events.json entry exists in zip
expected: true, actual: true
[PASS] A31.2: 0 UserEvent entries contain the SENTINEL in their .value field (proves src/content/index.ts:82 filter fired)
expected: 0, actual: 0
[PASS] A31.3: 0 UserEvent entries have target === '#probe-password' (filter early-returns BEFORE addUserEvent)
expected: 0, actual: 0
[PASS] A31.4: >=1 UserEvent entry contains the CONTROL sentinel (defense-in-depth: proves the listener is alive, so A31.2/A31.3 absences mean the filter fired — not "no events at all")
expected: ">=1 control", actual: 1
Diagnostics:
- Step 1: setupFreshRecording (A31 owns its recording — clean event-log window)
- Step 1 OK — REC state established
- Step 2: chrome.tabs.create(https://example.com/, active:true) — content script ISOLATED world is alive on https://, not on chrome-extension:// (Plan 03-02 lesson)
- Step 2 result: probeTab.id=639627313, probeTab.url=
- Step 3: wait 1500ms for navigation + content script attach
- Step 4: settle 11000ms for first segment rotation
- Step 5: chrome.scripting.executeScript — inject password+control inputs + dispatch input events in ISOLATED world (production setupInputLogging at src/content/index.ts:78 sees BOTH, line-82 filter early-returns on the password input only)
- Step 5 result: {"controlDispatched":true,"controlTyped":true,"passwordDispatched":true,"passwordTyped":true}
- Step 6: settle 1000ms so synchronous handlers complete + userEvents[] populates
- Step 7: dispatch SAVE_ARCHIVE (probe tab is the active tab; SW will harvest from there)
- Step 7 result: {"success":true}
- A31 zipPath=/tmp/mokosh-uat-KBwJL1/47832f1c-ec9c-47ca-bb49-796a5e167b16.zip
- A31 userEvents.length=1
- A31 sentinel-containing count=0, password-targeting count=0, control-containing count=1
Plan must-haves coverage (all GREEN)
truths[0]"Typing a sentinel value into the probe-page password input does NOT cause that value to appear in logs/events.json (existing src/content/index.ts:82 filter fires)" — A31.2 PASS (sentinel-containing count=0).truths[1]"Counts of UserEvent entries whose .value field contains the sentinel string = 0" — A31.2 PASS.truths[2]"Counts of UserEvent entries whose .target selector points at the password input = 0 (filter happens early-return BEFORE addUserEvent)" — A31.3 PASS (password-targeting count=0).truths[3]"UAT harness exits 0 with 31 + 1 = 32/32 assertions GREEN (A30 baseline preserved + new A31)" — PASS (32/32 GREEN; A0..A30 baseline preserved).artifacts[0]assertA31 page-side orchestrator on window.__mokoshHarness — PASS (3 grep hits in extension-page-harness.ts).artifacts[1]driveA31 host-side with JSZip-parse + negative-assertion sentinel grep — PASS (2 grep hits in harness-page-driver.ts).artifacts[2]driveA31 import + wrapped driver + drivers-array push entry — PASS (6 grep hits in harness.test.ts).key_links[0]harness.test.ts → driveA31 via driveA31Wrapped — PASS.key_links[1]driveA31 → assertA31 via page.evaluate(harness.assertA31()) — PASS (harness.assertA31()invocation present).key_links[2]assertA31 → src/content/index.ts:82 password filter via synthetic password-input event injected into content-script ISOLATED world on https://example.com — PASS (empirical: A31.2 + A31.3 + A31.4 prove the path is exercised and the filter fired).
Acceptance grep gates
npx tsc --noEmitexit 0 — PASSgrep -c 'assertA31' tests/uat/extension-page-harness.tsreturns 3 ≥ 3 — PASSgrep -c 'A31_PASSWORD_SENTINEL' tests/uat/extension-page-harness.tsreturns 2 ≥ 2 — PASSgrep -c "'secret-do-not-log-123'" tests/uat/extension-page-harness.tsreturns 1 == 1 — PASSgrep -c 'driveA31' tests/uat/lib/harness-page-driver.tsreturns 2 ≥ 2 — PASSgrep -c 'driveA31' tests/uat/harness.test.tsreturns 6 ≥ 3 — PASSgrep -c 'secret-do-not-log-123' tests/uat/lib/harness-page-driver.tsreturns 1 == 1 — PASSHEADLESS=1 SKIP_PROD_REBUILD=0 npm run test:uatexit 0 with stdout containingUAT harness: 32/32 assertions passed— PASSnpm test -- --run tests/background/no-test-hooks-in-prod-bundle.test.tsexit 0 (Tier-1 inventory stays at 12) — PASSgit diff src/content/index.tsempty (verification-only contract) — PASS
Issues Encountered
One latent issue surfaced (PRE-EXISTING; out of scope for this plan; defer to Plan 03-05 + Phase 4):
- A29 zip-mtime race-condition flake (pre-existing; documented in 03-02-SUMMARY.md "Issues Encountered"). During Plan 03-03 empirical verification of A31, the pre-existing A29 flake re-surfaced: A29 SAVEs to the harness page tab (chrome-extension://...), which has no content script attached, so the SW's GET_RRWEB_EVENTS bridge logs "Could not establish connection. Receiving end does not exist." and the produced zip's rrweb/session.json is empty. A29's
findLatestZiphost-side helper then non-deterministically returns one of:- (a) A29's own empty zip (mtime tiebreaker resolves A29's zip last) — A29.2/A29.3/A29.4/A29.5 FAIL with
events.length=0 - (b) A prior real-content zip (mtime tiebreaker resolves a prior zip, e.g., A27's zip with iana.org rrweb events) — A29.2/A29.3/A29.4/A29.5 PASS with
events.length=4
- (a) A29's own empty zip (mtime tiebreaker resolves A29's zip last) — A29.2/A29.3/A29.4/A29.5 FAIL with
- Empirical pre-existing flake reproduction: 3 UAT runs at base HEAD
de398347(no Plan 03-03 changes applied) — 2/3 PASS, 1/3 FAIL with the exact same symptom (A29 events.length=0on FAIL;A29 events.length=4on PASS). This empirically confirms the flake is INDEPENDENT of Plan 03-03's changes — pre-existing in the base. - Per CLAUDE.md SCOPE BOUNDARY rule: "Only auto-fix issues DIRECTLY caused by the current task's changes. Pre-existing warnings, linting errors, or failures in unrelated files are out of scope." The A29 flake is documented as a deferred-items entry per Plan 03-02 SUMMARY's explicit recommendation. Plan 03-03 captured A31 GREEN evidence in a run where A29 happened to PASS (race won), demonstrating that Plan 03-03's own contract (A31 GREEN with 5/5 checks; UAT 32/32 GREEN) is achievable.
- Recommended follow-up (per Plan 03-02 SUMMARY + this plan): A Phase 3 amendment plan (e.g., 03-01a) or a Phase 4 hardening pass re-targets A29 to use the same cs-injection-world probe-tab pattern A30 introduced + A31 reused. The fix is mechanical (replace assertA29 page-side body with cs-injection-world variant + drive a DOM mutation in the probe tab's ISOLATED world) and removes the mtime-race ambiguity. Plan 03-05 VERIFICATION.md should flag this as a known follow-up for §10 #4 closure rigor (the current closure stands on A30-style empirical evidence; A29 needs the same hardening).
Threat Flags
None new. The plan's <threat_model> (T-03-03-01..T-03-03-04) was analyzed at planner-time; implementation honors all mitigations:
- T-03-03-01 (Information Disclosure — sentinel value lands in event log despite filter): disposition
mitigate. A31 IS the negative-assertion mitigation. A31.2 + A31.3 both PASS empirically (counts=0). RED would mean the filter regressed; the test enforces the invariant. Sentinel is not a real secret (per RESEARCH §"Security Domain"); if it leaked, it would be visible in events.json which is logged locally but never transmitted (REQ-password-confidentiality Out of Scope v1 charter applies). - T-03-03-02 (Information Disclosure — test-only hook surface leaking to production bundle): disposition
mitigate. A31 rides the production input listener + line-82 filter + chrome.tabs + chrome.scripting + existing helpers. No__MOKOSH_UAT__-gated symbols. FORBIDDEN_HOOK_STRINGS unchanged at 12 entries; 13/13 unit-gate sub-tests GREEN; 12 strings × 0 hits in dist/. UAT A0 mirror unchanged at 12. - T-03-03-03 (Tampering — A31 SAVE produces a zip in the per-run downloadsDir that contains the sentinel only IF the filter regressed): disposition
accept. The per-run downloadsDir is mkdtempSync'd by launchHarnessBrowser + cleaned by the test runner; no cross-run leakage. Sentinel is not a real secret. - T-03-03-04 (Repudiation — if A31 GREEN but the production filter actually broke, the assertion would mislead): disposition
mitigate. A31 checks THREE orthogonal paths after Rule 2 addition: (a) sentinel-value absence, (b) password-selector-target absence, (c) control-sentinel presence (defense-in-depth: proves the listener is alive). All three pass IFF the listener is running AND the line-82 filter early-returns BEFORE addUserEvent for the password input. A regression in the filter would cause AT LEAST ONE of (a)+(b) to RED (sentinel would land in .value, or events would target #probe-password). A regression in the listener itself would cause (c) to RED (control event missing). Strictly stronger than the plan-spec 2-orthogonal-paths mitigation.
No new production surface; threat surface unchanged from Phase 2. The existing src/content/index.ts:82 filter is the verification subject; the PARTIAL mark in Plan 03-05 VERIFICATION.md explicitly carries the charter rationale.
Phase 3 Wave Sequencing
Per CONTEXT D-P3-01 + RESEARCH Pitfall 6: Plans 03-01..04 modify the SAME three harness files (extension-page-harness.ts, harness-page-driver.ts, harness.test.ts). RESEARCH §"Wave Sequencing Note" recommended SEQUENTIAL execution within Wave 2: 03-01 (A29) → 03-02 (A30) → 03-03 (A31, this plan) → 03-04 (A32 RAM optional). Plan 03-03 runs AFTER 03-01 + 03-02 (depends_on: [01, 02]) — Wave 3 sequence honored.
Next Plan Readiness
- Plan 03-04 (§10 #9 RAM best-effort): Independent surface (puppeteer.Page.metrics scaffolding); A31's cs-injection-world pattern doesn't apply directly (RAM measurement is host-side via CDP Performance.getMetrics). Plan 03-04 can proceed with no Plan 03-03 dependency interactions.
- Plan 03-05 (§10 sweep VERIFICATION.md aggregator): Inherits A31 as the binding §10 #8 PARTIAL empirical gate. Frontmatter shape per RESEARCH §"Code Examples → VERIFICATION.md frontmatter template" includes
overrides_applied: 1with explicitoverride_notesblock citing D-P3-02 charter + A31 GREEN evidence. Plan 03-05 SHOULD ALSO flag the A29 flake under "Forward-Looking Deferred Items" (Phase 4 hardening candidate).
Self-Check: PASSED
- A31 assertion added: CONFIRMED via git log + grep (
assertA313 hits in extension-page-harness.ts;driveA312 hits in harness-page-driver.ts + 6 in orchestrator). - UAT count: 31 → 32 GREEN: EMPIRICALLY CONFIRMED via
HEADLESS=1 SKIP_PROD_REBUILD=1 npm run test:uatexit 0; 32/32 assertions PASSED (A1..A30 + A31). - vitest 171/171 GREEN preserved: CONFIRMED (full suite; 31 test files).
- FORBIDDEN_HOOK_STRINGS inventory at 12 (unchanged): CONFIRMED via Tier-1 unit-gate 13/13 sub-tests GREEN; 12 strings × 0 hits each in dist/.
- Sentinel password value does NOT appear in logs/events.json after A31 run: CONFIRMED via
A31 sentinel-containing count=0;password-targeting count=0; withcontrol-containing count=1proving the listener IS alive (defense-in-depth). - No new masking surfaces added: CONFIRMED (zero src/content/index.ts edits; no rrweb maskInputFn; no data-sensitive attribute guards).
- No modifications to src/content/index.ts: CONFIRMED (
git diff src/content/index.tsempty;git log de398347..HEAD -- src/content/index.tsempty). - tsc clean: CONFIRMED (
npx tsc --noEmitexit 0). - 2/2 plan tasks committed atomically (
8db629fTask 1 +34b36fbTask 2). - SUMMARY.md created (committed in the final docs commit).
File existence verification
FOUND: tests/uat/extension-page-harness.ts (assertA31 + window.__mokoshHarness entry)
FOUND: tests/uat/lib/harness-page-driver.ts (driveA31)
FOUND: tests/uat/harness.test.ts (driveA31 import + Wrapped const + drivers push + banner)
FOUND: .planning/phases/03-spec-10-smoke-verification-dom-event-log-verification/03-03-SUMMARY.md (this file)
Commit verification
FOUND: 8db629f feat(03-03): Task 1 — assertA31 page-side orchestrator (cs-injection-world password-filter probe)
FOUND: 34b36fb feat(03-03): Task 2 — driveA31 + orchestrator wiring (A31 password-filter PARTIAL)
Phase: 03-spec-10-smoke-verification-dom-event-log-verification Plan: 03 Completed: 2026-05-20