Replace harness-page-mutation approach with verbatim port of the
canonical cs-injection-world pattern from Plan 03-02 (assertA30) +
Plan 03-03 (assertA31):
- chrome.tabs.create(https://example.com/, active:true) opens probe
tab where content script + rrweb's record() attach normally
(chrome-extension:// is NOT covered by <all_urls> per Chrome
match-pattern spec; was the root flake cause)
- 1.5s tab-attach + 11s segment-settle waits (canonical A27/A30/A31)
- chrome.scripting.executeScript world: 'ISOLATED' injects a sentinel-
bearing <div> (textContent='a29-mutation-sentinel') into document.body
— rrweb's MutationObserver lives in the same ISOLATED world so the
IncrementalSnapshot's data.adds[*].node.textContent will carry the
sentinel
- 500ms MutationObserver-enqueue settle
- SAVE_ARCHIVE while probe tab is active (SW harvests rrweb/session.json
from there)
- try/finally chrome.tabs.remove with silent-ignore (T-02-04-04 parity)
A29 constants block extended: A29_TAB_NAVIGATION_WAIT_MS,
A29_PROBE_TAB_URL, A29_MUTATION_SENTINEL, A29_PROBE_DIV_ID.
This closes the documented ~2/3 success-rate flake from Plans 03-02 +
03-03 where A29 "passed" by reading iana.org leftover DOM mutations
from A27/A28's probe tabs — a real rrweb regression at
src/content/index.ts:284 would have been masked because iana.org's
home page emits plenty of mutations during normal rendering.
Tier-1 FORBIDDEN_HOOK_STRINGS unchanged at 12; assertA30 + assertA31
untouched; __mokoshHarness wiring unchanged. Host-side driveA29
strict-sentinel filter lands in Task 2.
Verify:
- npx tsc --noEmit → 0
- npm run build:test → 0
- grep -c 'A29_MUTATION_SENTINEL' tests/uat/extension-page-harness.ts → 3
- grep -nE "world: 'ISOLATED'" tests/uat/extension-page-harness.ts → 3
call sites (A29 + A30 + A31) — ISOLATED parity per RESEARCH Pitfall 5