docs(04-06): re-plan iter-2 — real inline-SVG coverage + corrected 184/184 baseline
Re-plan-checker iter-1 (commitdeb68df) flagged 2 BLOCKERs against the iter-1 re-plan (b59bd24). Both fixed below; 3 advisories fixed; iter-1 verified-correct items preserved. EVERY test-infrastructure claim re-verified against actual code this session before being written into the plan — extension-page-harness.ts, harness-page-driver.ts, launch.ts, harness.test.ts, vitest.config.ts, vite.config.ts, vite.test.config.ts, full vitest run. No third false premise. BLOCKER 1 — fictitious A17.8 live-DOM delegation. The iter-1 re-plan claimed live-DOM injection + currentColor cascade was "delegated to A17.8 in real Chrome". Verified false: assertA17 reaches welcome.html only via fetch + DETACHED DOMParser string-parse; A17.8 is 100% string-grep on jsText; the harness opens exactly two pages (victimPage file://, harnessPage extension-page-harness.html — launch.ts:473-542); populateMark() never runs in the harness. Fixed: a NEW host-side harness assertion A35 is added (Task 3, modeled on driveA32/33/34) — opens welcome.html via browser.newPage() + page.goto, lets populateMark() run at DOMContentLoaded, then querySelector '.welcome-hero__mark svg' + getComputedStyle().stroke proves the LIVE currentColor cascade. welcome.html is a real web-accessible extension page (builds to dist-test/src/welcome/welcome.html); launchHarnessBrowser returns browser + extensionId so the new tab is cheaply reachable. A35 is genuine new harness work (new driver + drivers-array entry + banner-string update + import). A17.8 is narrowed honestly to a source-bundling check only. No fictitious delegation. BLOCKER 2 — DEFECT 3 named the wrong failing test. The iter-1 re-plan claimed strict-meta-json-validation.test.ts "fails on a clean tree" and hard-coded a Task 2 gate "failure set EXACTLY == {strict-meta-json}". Verified false this session: strict-meta-json in isolation = 8/8 GREEN; a full vitest run reproduced 183 passed / 1 failed where the RED was tests/background/webm-remux.test.ts (ffprobe -count_frames, timeout) — NOT strict-meta-json. webm-remux in isolation = 5/5 GREEN. The "1 failed" is a non-deterministic ffprobe/parallel-vitest timeout flake — exactly 04-CONTEXT #9 + #10. Fixed: baseline corrected to 184/184 GREEN when the flake doesn't fire; target after Plan 04-06 = 188/188 GREEN (+4 new tests). Task 2 gate now: 188/188 -> pass; 1 RED that passes on isolation re-run -> tolerate as the known flake; reproducible RED or 2+ RED -> regression. No test filename hard-coded. deferred-items.md mis-diagnosis corrected (Task 3 Edit 5). Advisories (all fixed): - A1: Task 3 action and verify both use SKIP_PROD_REBUILD=0 (intentional — harness must rebuild dist-test against Task 2 source edits). - A2: requirements:[] kept (Phase 4 has no new REQ-* per ROADMAP); charter linkage via the `charter-d-p4-03` tag — non-blocking. - A3: Task 1 acceptance criterion reworded to grep only import statements + the @vitest-environment directive, so the file's header prose explaining "no DOM-emulation library" doesn't trip it. Preserved (iter-1 verified-correct): - DEFECT 2 back-patch line classification (22/47/82/135/205 flip; 40/89/109/110 leave). - welcome.css drop from files_modified (bare class selector matches <svg>; color is inherited). - Thesis: currentColor Option A + cursor verification-only + operator-empirical Task 4 + PNG icons untouched. - FORBIDDEN_HOOK_STRINGS stays at 12 (no new __MOKOSH_UAT__ symbols). - Frontmatter shape (phase:04 / slug / plan:06 / type:execute / wave:5 / autonomous:false / depends_on:[01..05]). files_modified extended by 3 new entries (harness-page-driver.ts + harness.test.ts + the corrected deferred-items.md). Validation: - gsd-sdk frontmatter.validate --schema plan: valid:true (all 8 required fields present). - gsd-sdk verify.plan-structure: valid:true, 0 errors, 0 warnings, 4 tasks each with Files+Action+Verify+Done; Task 4 is checkpoint:human-verify per autonomous:false. Orchestrator: run the re-plan checker again on this iter-2 commit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -15,9 +15,12 @@ files_modified:
|
||||
- src/welcome/welcome.ts
|
||||
- globals.d.ts
|
||||
- tests/uat/extension-page-harness.ts
|
||||
- tests/uat/lib/harness-page-driver.ts
|
||||
- tests/uat/harness.test.ts
|
||||
- tests/welcome/inline-svg.test.ts
|
||||
- tests/build/cursor-visibility.test.ts
|
||||
- .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md
|
||||
- .planning/phases/04-harden-clean-up-optional/deferred-items.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
tags:
|
||||
@@ -35,22 +38,31 @@ revision_history:
|
||||
reason: "Full re-plan via /gsd-plan-phase ceremony. The prior 04-06-PLAN.md hit a blocking checkpoint (plan-assumption defect). Three defects corrected; thesis preserved (dark-logo currentColor Option A + cursor verification-only + A17.8 + operator-empirical Task 4)."
|
||||
defects_resolved:
|
||||
- id: "DEFECT 1 — false jsdom premise"
|
||||
was: "Prior Task 1 stated 'vitest jsdom environment (vitest.config.ts already configures this; verify by grep environment.*jsdom vitest.config.ts)'. FALSE — vitest.config.ts line 18 sets environment:'node'; no DOM-emulation library (jsdom/happy-dom/linkedom/@happy-dom/global-registrator) is in node_modules; no vitest unit test has ever used DOMParser. The prior plan required tests/welcome/inline-svg.test.ts to exercise populateMark()'s live DOMParser + document.querySelector path inside a jsdom that does not exist."
|
||||
now: "Test strategy decided — STRATEGY (a): reframe inline-svg.test.ts for node-env. The test reads src/shared/brand/mokosh-mark.svg + src/welcome/welcome.ts + globals.d.ts from disk (the canonical tests/i18n/manifest-i18n.test.ts file-read + string-assert pattern) and pins the SOURCE-LEVEL contract that populateMark's ?raw import + DOMParser injection depends on. Full live-DOM injection + currentColor CSS cascade verification is delegated to the A17.8 harness sub-check in real Chrome (Task 3). Rationale: the project DELIBERATELY ships no DOM library (UI-SPEC scope_kind:thin-design-surface; vitest.config.ts environment:'node' is a deliberate Plan 01-11 choice); two precedents (tests/background/toolbar-action.test.ts:279, tests/offscreen/display-surface-constraint.test.ts:41) explicitly 'manually stub' rather than install jsdom; CLAUDE.md mandates 'consistency with existing patterns > brevity'; feedback-trust-harness-over-manual-uat reserves real-browser behavior for the Puppeteer harness. Strategy (b) jsdom devDependency rejected — deviates from a twice-reaffirmed no-DOM-library stance to faithfully test what the harness already covers in real Chrome. Strategy (c) manual DOMParser stub rejected — the executor correctly assessed a faithful SVG-namespace + currentColor DOMParser stub as substantial + fragile; toolbar-action.test.ts only stubs simple element-getter surfaces, never DOMParser."
|
||||
was: "Prior Task 1 stated 'vitest jsdom environment (vitest.config.ts already configures this)'. FALSE — vitest.config.ts line 18 sets environment:'node'; no DOM-emulation library is in node_modules."
|
||||
now: "Test strategy decided — STRATEGY (a): reframe inline-svg.test.ts for node-env source-contract. Full live-DOM injection delegated to the A17.8 harness sub-check (later corrected by iter-2 BLOCKER 1 — see below)."
|
||||
- id: "DEFECT 2 — stale back-patch line numbers"
|
||||
was: "Prior Task 3 + PATTERNS.md cited 01-07-SUMMARY.md back-patch lines 47/82/109/135/205."
|
||||
now: "Verified against the live file (grep 2026-05-22): the genuine stale 'deferred to Phase 5' framing — the cursor-visibility deferral that is FALSE because cursor:'always' actually shipped in Plan 01-09 — is at lines 22, 47, 82, 135, 205. Lines 40, 89, 109, 110 are historical descriptions of what the Plan 01-07-closure ROADMAP/STATE commits DID (they record commit content + the [Phase 01-07-deferred-to-5] decision-log tag) and are LEFT unchanged. Task 3 flips 22/47/82/135/205 only."
|
||||
was: "Prior Task 3 cited 01-07-SUMMARY.md back-patch lines 47/82/109/135/205."
|
||||
now: "Verified against the live file (grep 2026-05-22): the genuine stale 'deferred to Phase 5' framing is at lines 22, 47, 82, 135, 205. Lines 40, 89, 109, 110 are historical commit-description records and are LEFT unchanged. Task 3 flips 22/47/82/135/205 only."
|
||||
- id: "DEFECT 3 — wrong vitest baseline"
|
||||
was: "Prior plan assumed a 184/184 GREEN vitest baseline and targeted '184 -> ~188 GREEN'."
|
||||
now: "Verified real baseline: 183 passed / 1 failed (184 total). tests/build/strict-meta-json-validation.test.ts fails on a clean tree (getMetaOrFail line 491 — meta.json not parsed from the SAVE_ARCHIVE flow). This is a pre-existing red, NOT caused by Plan 04-06 (whose surface is SVG recolor + welcome.ts + globals.d.ts + A17.8 + docs); logged to .planning/phases/04-harden-clean-up-optional/deferred-items.md at commit 6a989e8 and routed to /gsd-debug separately (consolidates with the A33 SAVE-ack flake). The test-count target is reframed below as '+4 new tests on top of the 183-GREEN/1-pre-existing-RED baseline' = 187 GREEN / 1 pre-existing RED."
|
||||
now: "Baseline reframed (later corrected by iter-2 BLOCKER 2 — see below)."
|
||||
- revised: 2026-05-22
|
||||
reason: "Re-plan iter-2 via /gsd-plan-phase. The iter-1 re-plan (commit b59bd24) introduced TWO new false premises; re-plan-checker iter-1 (commit deb68df) flagged both as BLOCKERs. Both fixed below. EVERY test-infrastructure claim re-verified against actual code this session (extension-page-harness.ts, harness-page-driver.ts, launch.ts, harness.test.ts, vitest.config.ts, vite.config.ts, full vitest run)."
|
||||
defects_resolved:
|
||||
- id: "BLOCKER 1 — fictitious A17.8 live-DOM delegation"
|
||||
was: "The iter-1 re-plan justified the weak node-env source-contract unit test by 'delegating full live-DOM injection + currentColor cascade verification to the A17.8 harness sub-check in real Chrome'. VERIFIED FALSE this session: assertA17 (extension-page-harness.ts:2090-2308) reaches the welcome page ONLY via fetch(chrome.runtime.getURL('src/welcome/welcome.html')) + new DOMParser().parseFromString(htmlText,'text/html') — a DETACHED string parse. A17.8 (lines 2273-2298) is 100% string-grep on jsText. The harness opens exactly TWO pages — victimPage (file://) and harnessPage (extension-page-harness.html) — verified at launch.ts:473-542. populateMark() NEVER runs in the UAT harness. 'welcome-hero__mark' returns zero grep hits across the entire harness. So the iter-1 re-plan delegated to a target that does not do that work."
|
||||
now: "Verified that a LIVE welcome-page tab IS cheaply reachable: launchHarnessBrowser returns { browser, extensionId, harnessPage, victimPage, ... } (launch.ts:534-542); welcome.html is a real web-accessible extension page that builds to dist-test/src/welcome/welcome.html (vite.test.config.ts:95) — exactly the path chrome.runtime.getURL('src/welcome/welcome.html') resolves to (the path A17 already fetches). A NEW host-side assertion A35 is added (Task 3): a host-side driver — pattern verified against driveA32/driveA33/driveA34 (host-side drivers that take Page + captured handles and build a CheckRecord[] directly, NOT page.evaluate(window.__mokoshHarness)) — that opens welcome.html in a fresh browser.newPage() tab, lets populateMark() run at DOMContentLoaded, then queries the LIVE injected .welcome-hero__mark svg element + getComputedStyle().stroke to prove the real currentColor cascade. This is genuine new harness work (a new driver + a new assertion + a new harness.test.ts driver-list entry), correctly scoped as such — NOT a result.checks.push inside assertA17. A17.8 itself is still updated to swap the data-URL grep for a raw-source grep (source-bundling check); A35 is the live-DOM behavior check. No fictitious delegation."
|
||||
- id: "BLOCKER 2 — DEFECT 3 named the wrong failing test"
|
||||
was: "The iter-1 re-plan asserted 'baseline = 183 passed / 1 failed; the 1 failure is tests/build/strict-meta-json-validation.test.ts, a pre-existing red on a clean tree' and hard-coded a Task 2/Task 4 gate 'failure set EXACTLY == {strict-meta-json-validation.test.ts}'. VERIFIED FALSE this session: npx vitest run tests/build/strict-meta-json-validation.test.ts in isolation = 8/8 GREEN. A full npx vitest run this session reproduced 183 passed / 1 failed (184 total) — and the RED was tests/background/webm-remux.test.ts > 'ffprobe -count_frames reports between 905 and 912 frames' (Error: Test timed out in 5000ms), NOT strict-meta-json. webm-remux.test.ts in isolation = 5/5 GREEN. The 1 failure is a non-deterministic ffprobe/parallel-vitest timeout flake — exactly 04-CONTEXT.md in-scope items #9 (parallel-vitest Tier-1-build-step race, ~1/5 runs) and #10 (2 ffprobe/ffmpeg flakes)."
|
||||
now: "Baseline corrected to 184/184 GREEN when the parallel-vitest flake family (04-CONTEXT #9/#10) does not fire. Target after Plan 04-06: 188/188 GREEN (+4 new tests). The hard-coded named-test gate is REMOVED. Task 2's vitest gate logic: run the full suite; if 188/188 -> pass; if there is exactly 1 RED AND that RED test passes when re-run in isolation (npm test -- <thatfile>) -> tolerate it as the known 04-CONTEXT #9/#10 flake and pass; if there is a NEW/reproducible failure (RED persists in isolation, or 2+ RED) -> FAIL the gate (Plan 04-06 caused a regression). No test filename is hard-coded. The stale deferred-items.md entry that mis-named strict-meta-json is corrected in Task 3."
|
||||
must_haves:
|
||||
truths:
|
||||
- "src/shared/brand/mokosh-mark.svg root <svg> element carries `stroke=\"currentColor\"` (was `stroke=\"#181b2a\"`); the 12 <line> + 1 <rect> children are unchanged"
|
||||
- "src/welcome/welcome.ts imports the mark via `import markSvg from '../shared/brand/mokosh-mark.svg?raw';` (was `?url`); populateMark uses DOMParser to inject an inline <svg> into the slot (NOT <img>, NEVER innerHTML)"
|
||||
- "Injected inline <svg> inherits color from the parent .welcome-hero__mark wrapper (color: var(--mks-fg-inverse)) so the stroke resolves via the CSS currentColor cascade — verified in real Chrome by the A17.8 harness sub-check"
|
||||
- "globals.d.ts contains a `declare module '*.svg?raw'` ambient module declaration alongside the existing `*.svg?url` and `*.webm?url`"
|
||||
- "tests/welcome/inline-svg.test.ts (node-env, source-level) pins the contract: mokosh-mark.svg source carries stroke='currentColor' + canonical viewBox; welcome.ts source uses the ?raw import + DOMParser + replaceChildren + aria attributes and does NOT use innerHTML; globals.d.ts carries the *.svg?raw ambient decl"
|
||||
- "A17.8 harness sub-check updated to assert the welcome chunk JS bundles the raw SVG source (stroke='currentColor' + canonical viewBox) AND, in real Chrome, populateMark injects an inline <svg> whose stroke resolves to currentColor via parent CSS color"
|
||||
- "tests/welcome/inline-svg.test.ts (node-env, source-level) pins the SOURCE contract: mokosh-mark.svg source carries stroke='currentColor' + canonical viewBox; welcome.ts source uses the ?raw import + DOMParser + replaceChildren and does NOT use innerHTML; globals.d.ts carries the *.svg?raw ambient decl. It does NOT exercise live DOM (vitest is environment:'node' — verified vitest.config.ts:18 — and ships no DOM library)"
|
||||
- "A17.8 harness sub-check updated to assert the welcome chunk JS bundles the raw SVG SOURCE string (stroke='currentColor' + canonical viewBox), replacing the prior data:image/svg+xml data-URL grep"
|
||||
- "NEW harness assertion A35 (host-side driver) opens welcome.html as a real Puppeteer tab, lets populateMark() run, and verifies the LIVE injected `.welcome-hero__mark svg` element exists with stroke='currentColor' AND its getComputedStyle().stroke resolves to the parent .welcome-hero__mark wrapper color (the real currentColor cascade) AND no `.welcome-hero__mark img` exists. This is the genuine automated live-DOM coverage for the inline-SVG injection + cascade"
|
||||
- "Cursor visibility verified ALREADY SHIPPED at src/offscreen/recorder.ts:285 (`cursor: 'always'`) per RESEARCH Finding 4; tests/build/cursor-visibility.test.ts (node-env file-grep) regression-pins the literal"
|
||||
- ".planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md back-patched: the stale 'deferred to Phase 5' lines 22, 47, 82, 135, 205 flipped to 'shipped opportunistically Plan 01-09; verified Phase 4 Plan 04-06'; the historical commit-description lines 40, 89, 109, 110 left unchanged"
|
||||
- "Operator empirical checkpoint: dark-mode visual aesthetic judgment on welcome-hero from dark + light Puppeteer screenshots (the ONE genuine operator-empirical case in Phase 4 per UI-SPEC §'Acceptance Criteria #6')"
|
||||
@@ -74,11 +86,20 @@ must_haves:
|
||||
contains: "cursor: 'always'"
|
||||
min_lines: 15
|
||||
- path: "tests/uat/extension-page-harness.ts"
|
||||
provides: "A17.8 sub-check updated to assert raw SVG source bundling + inline <svg> injection in real Chrome (NOT data URL)"
|
||||
provides: "A17.8 sub-check updated to assert raw SVG SOURCE bundling (replaces data:image/svg+xml data-URL grep)"
|
||||
contains: "A17.8"
|
||||
- path: "tests/uat/lib/harness-page-driver.ts"
|
||||
provides: "NEW host-side driveA35 — opens welcome.html as a real tab, runs populateMark(), queries the live injected <svg> + getComputedStyle().stroke for the currentColor cascade"
|
||||
contains: "driveA35"
|
||||
- path: "tests/uat/harness.test.ts"
|
||||
provides: "A35 wired into the orchestrator driver list (driveA35Wrapped closure capturing handles.browser + handles.extensionId); driver-count display string appended with A35"
|
||||
contains: "A35"
|
||||
- path: ".planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md"
|
||||
provides: "Surgical back-patch — stale 'deferred to Phase 5' lines 22/47/82/135/205 flipped; historical commit-description lines 40/89/109/110 left"
|
||||
contains: "Phase 4 Plan 04-06"
|
||||
- path: ".planning/phases/04-harden-clean-up-optional/deferred-items.md"
|
||||
provides: "Correction of the mis-diagnosed 'strict-meta-json fails on clean tree' entry — the real flake is the 04-CONTEXT #9/#10 parallel-vitest ffprobe family, not a named test"
|
||||
contains: "parallel-vitest"
|
||||
key_links:
|
||||
- from: "src/welcome/welcome.ts populateMark"
|
||||
to: "src/shared/brand/mokosh-mark.svg?raw"
|
||||
@@ -86,12 +107,12 @@ must_haves:
|
||||
pattern: "DOMParser"
|
||||
- from: "Injected inline <svg> stroke='currentColor'"
|
||||
to: ".welcome-hero__mark wrapper color: var(--mks-fg-inverse)"
|
||||
via: "CSS color cascade (W3C SVG2 §13.3) — verified in real Chrome by A17.8"
|
||||
via: "CSS color cascade (W3C SVG2 §13.3) — verified live in real Chrome by the NEW A35 host-side harness assertion (getComputedStyle().stroke on the injected <svg>)"
|
||||
pattern: "stroke=\"currentColor\""
|
||||
- from: "tests/uat/extension-page-harness.ts A17.8"
|
||||
to: "welcome chunk JS bundle string-grep + real-Chrome DOM query"
|
||||
via: "post-build grep for raw SVG source + querySelector inline <svg> after populateMark"
|
||||
pattern: "A17"
|
||||
- from: "tests/uat/lib/harness-page-driver.ts driveA35"
|
||||
to: "a live welcome.html Puppeteer tab where populateMark() runs"
|
||||
via: "browser.newPage() + page.goto(chrome-extension://<id>/src/welcome/welcome.html) + querySelector('.welcome-hero__mark svg') + getComputedStyle"
|
||||
pattern: "driveA35"
|
||||
---
|
||||
|
||||
<objective>
|
||||
@@ -104,15 +125,21 @@ Two additional polish items co-land:
|
||||
|
||||
3. **Cursor visibility verification** (RESEARCH Finding 4): `cursor: 'always'` is ALREADY SHIPPED at `src/offscreen/recorder.ts:285` (Plan 01-09 opportunistically lifted the original Phase 5 deferral). Plan 04-06 confirms via a defensive grep test (`tests/build/cursor-visibility.test.ts`) and back-patches the stale "deferred to Phase 5" lines in `.planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md`.
|
||||
|
||||
4. **Operator empirical checkpoint** (UI-SPEC Acceptance Criterion #6): the ONE Phase 4 genuine operator-empirical gate — dark-mode visual aesthetic judgment of the welcome hero. The harness covers `currentColor` resolution + inline-SVG injection automatically (Tasks 1-3); only the dark-OS aesthetic judgment is non-automatable. Per `feedback-trust-harness-over-manual-uat.md`, the operator judges from dark + light Puppeteer screenshots produced as the verification artifact, NOT a manual Chrome session.
|
||||
4. **Operator empirical checkpoint** (UI-SPEC Acceptance Criterion #6): the ONE Phase 4 genuine operator-empirical gate — dark-mode visual aesthetic judgment of the welcome hero. Per `feedback-trust-harness-over-manual-uat.md`, the operator judges from dark + light Puppeteer screenshots produced as the verification artifact, NOT a manual Chrome session.
|
||||
|
||||
Purpose: Closes the UI-SPEC dark-logo contrast strategy, RESEARCH Finding 4 cursor verification, the ROADMAP cursor visibility item, and the Plan 01-10 cycle-2 operator observation "also on dark surfaces probably either we need to place the logo on the light background or dunno".
|
||||
|
||||
Output: 3 source/test/spec file edits (SVG recolor + welcome.ts ?raw/DOMParser + globals.d.ts ambient decl) + 1 harness sub-check update (A17.8) + 2 new test files (`tests/welcome/inline-svg.test.ts` + `tests/build/cursor-visibility.test.ts`) + 1 docs back-patch + 1 operator empirical UAT cycle. The plan is `autonomous: false` because of the operator-empirical checkpoint (the ONLY autonomous: false plan in Phase 4).
|
||||
Output: 3 source/test/spec file edits (SVG recolor + welcome.ts ?raw/DOMParser + globals.d.ts ambient decl) + a 3-part harness change (A17.8 sub-check update + NEW host-side A35 assertion + A35 orchestrator wiring) + 2 new test files (`tests/welcome/inline-svg.test.ts` + `tests/build/cursor-visibility.test.ts`) + 2 docs edits (01-07-SUMMARY back-patch + deferred-items.md correction) + 1 operator empirical UAT cycle. The plan is `autonomous: false` because of the operator-empirical checkpoint (the ONLY autonomous: false plan in Phase 4).
|
||||
|
||||
**Test-strategy note (re-plan DEFECT 1 resolution):** `tests/welcome/inline-svg.test.ts` runs in vitest's `environment: 'node'` (the deliberate project default — `vitest.config.ts:18`). It pins the SOURCE-LEVEL contract — the SVG source carries `currentColor`, `populateMark` is rewritten correctly with a `?raw` import + DOMParser, and no `innerHTML` is used. It does NOT exercise `populateMark`'s live DOMParser + `document.querySelector` path, because the project deliberately ships no DOM-emulation library. Full live-DOM injection + `currentColor` CSS cascade verification is delegated to the **A17.8 harness sub-check (Task 3) in real Chrome** — the canonical home for real-browser behavior per `feedback-trust-harness-over-manual-uat.md`.
|
||||
**Test-strategy note (DEFECT 1 / iter-2 BLOCKER 1 resolution — VERIFIED this session).** The automated coverage of the inline-SVG technique is split deliberately and HONESTLY across two layers:
|
||||
|
||||
**Baseline note (re-plan DEFECT 3 resolution):** the vitest baseline is **183 passed / 1 failed (184 total)**. The 1 failure — `tests/build/strict-meta-json-validation.test.ts` — is a pre-existing red on a clean tree (NOT caused by this plan), logged to `deferred-items.md` and routed to `/gsd-debug` separately. Plan 04-06 adds 2 new test files (4 new tests: 3 in `inline-svg.test.ts` + 1 in `cursor-visibility.test.ts`). After this plan the expected state is **187 passed / 1 pre-existing fail (188 total)**. `strict-meta-json-validation.test.ts` is OUT OF SCOPE for Plan 04-06 — Task 2's vitest gate and Task 3's UAT gate explicitly tolerate that one pre-existing red and FAIL ONLY if a different test breaks.
|
||||
- **Source-contract layer — `tests/welcome/inline-svg.test.ts`.** This runs in vitest's `environment: 'node'` (the deliberate project default — verified at `vitest.config.ts:18`; there is NO jsdom/happy-dom/linkedom in `node_modules`). It pins the SOURCE-LEVEL contract — the SVG source carries `currentColor`, `populateMark` is rewritten correctly with a `?raw` import + DOMParser + `replaceChildren`, and no `innerHTML` is used. It does NOT instantiate a DOM, does NOT call `populateMark`, does NOT use `DOMParser` at runtime. It proves the source TEXT; it cannot prove the runtime injection.
|
||||
|
||||
- **Live-DOM behavior layer — the NEW host-side harness assertion A35 (Task 3).** Verified facts: the UAT harness opens exactly two pages (`victimPage` file://, `harnessPage` `extension-page-harness.html` — `launch.ts:473-542`), and `populateMark()` NEVER runs anywhere in the current harness — `assertA17` reaches the welcome page only via `fetch()` + a DETACHED `DOMParser` string-parse, and A17.8 is 100% string-grep on the welcome JS chunk. So the prior iter-1 re-plan's claim "live-DOM injection is delegated to A17.8 in real Chrome" was FALSE — A17.8 cannot and does not do that. THIS plan fixes that gap with real new harness work: A35 is a host-side driver that opens `welcome.html` in a fresh `browser.newPage()` tab (welcome.html is a real web-accessible extension page; verified to build to `dist-test/src/welcome/welcome.html` via `vite.test.config.ts:95`), waits for `populateMark()` to run at `DOMContentLoaded`, then queries the LIVE injected `.welcome-hero__mark svg` element and reads `getComputedStyle().stroke` to prove the `currentColor` cascade actually resolves. A35 is the canonical automated proof of the runtime injection + cascade — NOT a fiction.
|
||||
|
||||
A17.8 is still updated (Task 3) but its honest scope is narrowed: it asserts the welcome chunk JS bundles the raw SVG SOURCE string (a source-bundling check — it confirms `?raw` inlined the SVG; it does NOT prove injection). A35 carries the live-DOM behavior proof. The Task 4 operator screenshot is the additional human aesthetic gate, not a substitute for automated coverage.
|
||||
|
||||
**Baseline note (DEFECT 3 / iter-2 BLOCKER 2 resolution — VERIFIED this session).** The vitest baseline is **184/184 GREEN** when the parallel-vitest flake family does NOT fire. A full `npx vitest run` this session produced `183 passed / 1 failed (184 total)` — and the RED was `tests/background/webm-remux.test.ts > 'ffprobe -count_frames reports between 905 and 912 frames'` (`Error: Test timed out in 5000ms`). Both `webm-remux.test.ts` (5/5) and `tests/build/strict-meta-json-validation.test.ts` (8/8) pass deterministically when run in isolation. The "1 failed" is a **non-deterministic ffprobe/parallel-worker timeout flake** — exactly the pre-existing flake family `04-CONTEXT.md` enumerates as in-scope items **#9** (parallel-vitest Tier-1-build-step race, ~1/5 full-suite runs) and **#10** (2 ffprobe/ffmpeg vitest flakes). It is NOT a real regression and NOT tied to one specific test name (the prior iter-1 re-plan wrongly named `strict-meta-json-validation.test.ts` as a "pre-existing red on a clean tree" — that test is GREEN on a clean tree). Plan 04-06 adds 2 new test files (4 new tests: 3 in `inline-svg.test.ts` + 1 in `cursor-visibility.test.ts`). Target after this plan: **188/188 GREEN**. Task 2's vitest gate (below) tolerates a single isolation-passing flake from the 04-CONTEXT #9/#10 family but FAILS on any new or reproducible failure — it hard-codes NO test filename.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@@ -137,7 +164,13 @@ Output: 3 source/test/spec file edits (SVG recolor + welcome.ts ?raw/DOMParser +
|
||||
@src/welcome/welcome.css
|
||||
@globals.d.ts
|
||||
@src/offscreen/recorder.ts
|
||||
|
||||
# Harness files — A17.8 update + NEW A35 host-side assertion
|
||||
@tests/uat/extension-page-harness.ts
|
||||
@tests/uat/lib/harness-page-driver.ts
|
||||
@tests/uat/harness.test.ts
|
||||
@tests/uat/lib/launch.ts
|
||||
@tests/uat/lib/assertions.ts
|
||||
|
||||
# Analog test scaffolds — node-env file-read + string-assertion (NO DOM library)
|
||||
@tests/i18n/manifest-i18n.test.ts
|
||||
@@ -145,16 +178,26 @@ Output: 3 source/test/spec file edits (SVG recolor + welcome.ts ?raw/DOMParser +
|
||||
@vitest.config.ts
|
||||
|
||||
<interfaces>
|
||||
<!-- Key shapes the executor consumes directly. Extracted from codebase 2026-05-22. -->
|
||||
<!-- Key shapes the executor consumes directly. Extracted from codebase + VERIFIED 2026-05-22 this session. -->
|
||||
|
||||
PROJECT TEST ENVIRONMENT (re-plan DEFECT 1 — verified, NON-NEGOTIABLE):
|
||||
PROJECT TEST ENVIRONMENT (DEFECT 1 — verified this session, NON-NEGOTIABLE):
|
||||
- vitest.config.ts line 18 sets environment:'node'. There is NO jsdom / happy-dom / linkedom / @happy-dom/global-registrator in node_modules. No vitest unit test uses DOMParser. Do NOT grep for environment.*jsdom and do NOT assume a jsdom environment — there is none.
|
||||
- When a vitest test needs DOM surfaces, the project pattern is to MANUALLY STUB the minimal surface touched (precedent: tests/background/toolbar-action.test.ts:279 "jsdom is not installed; instead we manually stub"; tests/offscreen/display-surface-constraint.test.ts:41). Those precedents stub only simple element-getter surfaces (getElementById / querySelector returning plain-object stubs) — they do NOT stub DOMParser.
|
||||
- tests/welcome/inline-svg.test.ts therefore uses the file-read + string-assert pattern (precedent: tests/i18n/manifest-i18n.test.ts). It does NOT instantiate a DOM, does NOT call populateMark, does NOT use DOMParser at runtime.
|
||||
- tests/welcome/inline-svg.test.ts uses the file-read + string-assert pattern (precedent: tests/i18n/manifest-i18n.test.ts). It does NOT instantiate a DOM, does NOT call populateMark, does NOT use DOMParser at runtime.
|
||||
- The LIVE-DOM behavior (populateMark injecting an inline <svg>; the currentColor cascade) is verified by the NEW host-side harness assertion A35, NOT by the unit test and NOT by A17.8. See "NEW A35 host-side assertion" below.
|
||||
|
||||
Canonical node-env file-read + string-assert scaffold (from tests/i18n/manifest-i18n.test.ts:29-46):
|
||||
UAT HARNESS ARCHITECTURE (BLOCKER 1 — verified this session, NON-NEGOTIABLE):
|
||||
- launchHarnessBrowser (tests/uat/lib/launch.ts) opens exactly TWO pages: victimPage (a file:// URL, lines 473-474) and harnessPage (chrome-extension://<id>/tests/uat/extension-page-harness.html, lines 523-527). It returns { browser, extensionId, harnessPage, victimPage, downloadsDir, swConsole, offConsole } (lines 534-542).
|
||||
- NO welcome.html tab is ever opened. populateMark() NEVER runs in the current harness. assertA17 (extension-page-harness.ts:2090-2308) reaches the welcome page ONLY via fetch(chrome.runtime.getURL('src/welcome/welcome.html')) + new DOMParser().parseFromString(htmlText,'text/html') — a DETACHED string parse, not a live tab. A17.8 (lines 2273-2298) is 100% string-grep on the welcome JS chunk text (jsText). 'welcome-hero__mark' returns ZERO grep hits across the whole harness file.
|
||||
- Drivers A1..A22 are page.evaluate(() => window.__mokoshHarness.assertXX()) wrappers — they run INSIDE the single harness page. driveA32/driveA33/driveA34 are HOST-SIDE drivers: they take a Page (plus, for A33/A34, captured handles) and build a CheckRecord[] / AssertionRecord directly without page.evaluate(window.__mokoshHarness). A35 follows the driveA32/A33/A34 host-side pattern.
|
||||
- The orchestrator driver list is in harness.test.ts (the `drivers` array, lines 374-535). Each entry is { name: string, drive: (page) => Promise<AssertionRecord> }. Wrapped closures capture extra handles, e.g. driveA33Wrapped = (page) => driveA33(page, handles.browser, handles.extensionId, handles.downloadsDir) (lines 367-368). The harness total is computed as `total = drivers.length + 1` (line 580) — adding A35 to the array auto-increments the count; no hardcoded total to bump. The architecture banner string at harness.test.ts:283 lists driver names literally and must have ', A35' appended.
|
||||
|
||||
AssertionRecord / CheckRecord shape (tests/uat/lib/assertions.ts:25-44):
|
||||
- interface CheckRecord { readonly name: string; readonly expected: unknown; readonly actual: unknown; readonly passed: boolean; }
|
||||
- interface AssertionRecord { readonly passed: boolean; readonly name: string; readonly checks: ReadonlyArray<CheckRecord>; readonly diagnostics: ReadonlyArray<string>; readonly error?: string; }
|
||||
|
||||
Canonical node-env file-read + string-assert scaffold (from tests/i18n/manifest-i18n.test.ts:27-33):
|
||||
- import { describe, expect, it } from 'vitest';
|
||||
- import { existsSync, readFileSync } from 'node:fs';
|
||||
- import { readFileSync } from 'node:fs';
|
||||
- import { resolve as resolvePath } from 'node:path';
|
||||
- const SOME_PATH = resolvePath(process.cwd(), 'relative/path');
|
||||
- inside it(): const text = readFileSync(SOME_PATH, 'utf8'); expect(text).toContain('...');
|
||||
@@ -172,14 +215,14 @@ import markUrl from '../shared/brand/mokosh-mark.svg?url';
|
||||
After Plan 04-06 (swap to ?raw):
|
||||
import markSvg from '../shared/brand/mokosh-mark.svg?raw';
|
||||
|
||||
From src/welcome/welcome.ts:159-179 (current populateMark — <img> injection): builds an HTMLImageElement, sets img.src = markUrl, img.alt, img.width/height = 64, img.className = 'welcome-hero__mark-img', img.setAttribute('aria-hidden','true'), then slot.replaceChildren(img). Filter-pipeline form; empty-slot logger.warn fallback at the end.
|
||||
From src/welcome/welcome.ts:159-179 (current populateMark — <img> injection): builds an HTMLImageElement, sets img.src = markUrl, img.alt = altText, img.width/height = 64, img.className = 'welcome-hero__mark-img', img.setAttribute('aria-hidden','true'), then slot.replaceChildren(img). Filter-pipeline form; empty-slot logger.warn fallback at the end. altText = COPY['welcome.hero.mark.alt'] ?? 'Знак Mokosh'.
|
||||
|
||||
After Plan 04-06 (DOMParser inline-SVG injection — the populateMark body becomes):
|
||||
- const parser = new DOMParser();
|
||||
- for each slot: const doc = parser.parseFromString(markSvg, 'image/svg+xml'); const svg = doc.documentElement;
|
||||
- svg.classList.add('welcome-hero__mark-img'); svg.setAttribute('aria-hidden','true'); svg.setAttribute('role','img'); svg.setAttribute('aria-label', altText);
|
||||
- slot.replaceChildren(svg);
|
||||
- preserve the function signature, the slots query, altText = COPY['welcome.hero.mark.alt'] ?? 'Знак Mokosh', the filter-pipeline form, and the empty-slot logger.warn fallback.
|
||||
- preserve the function signature, the slots query, the altText resolution, the filter-pipeline form, and the empty-slot logger.warn fallback.
|
||||
|
||||
NEVER use innerHTML — DOMParser + replaceChildren ONLY (MV3 CSP discipline; T-04-06-01 mitigation). The DOMParser parse is safe because the input is a Vite-bundled compile-time literal (no runtime untrusted input).
|
||||
|
||||
@@ -188,15 +231,22 @@ declare module '*.svg?url' { const url: string; export default url; }
|
||||
|
||||
After Plan 04-06 (append a *.svg?raw block immediately after the *.svg?url block):
|
||||
declare module '*.svg?raw' { const raw: string; export default raw; }
|
||||
(with an explanatory comment block per project docstring convention; cite the UI-SPEC dark-logo strategy + Vite ?raw asset-as-string semantics).
|
||||
(with an explanatory comment block per project docstring convention; cite the UI-SPEC dark-logo strategy + Vite ?raw asset-as-string semantics.)
|
||||
|
||||
From src/offscreen/recorder.ts:284-291 (verification target — ALREADY shipped; do NOT modify): the getDisplayMedia constraints object carries video: { displaySurface: 'monitor', cursor: 'always' }. The cursor: 'always' literal is at line 285.
|
||||
|
||||
From src/welcome/welcome.css:91-95 (existing rule — bare class selector matches both <img> and <svg>; NO change required): .welcome-hero__mark-img { width: 60%; height: 60%; display: block; }. welcome.css is NOT in files_modified — the bare class selector .welcome-hero__mark-img matches <svg class="welcome-hero__mark-img"> identically to <img class="welcome-hero__mark-img">. The inline <svg> inherits color: var(--mks-fg-inverse) from the .welcome-hero__mark wrapper automatically (CSS color is an inherited property). No CSS edit is needed; the prior plan's "optional welcome.css broadening" is dropped.
|
||||
From src/welcome/welcome.css:64-73 + 91-95 (existing rules — NO change required): .welcome-hero__mark { ... color: var(--mks-fg-inverse); } (the wrapper sets an inherited color); .welcome-hero__mark-img { width: 60%; height: 60%; display: block; } is a BARE class selector (no img. tag qualifier) so it matches <svg class="welcome-hero__mark-img"> identically to <img>. welcome.css is NOT in files_modified — the inline <svg> inherits color: var(--mks-fg-inverse) from the .welcome-hero__mark wrapper automatically (CSS color is an inherited property; CSS Color L3). stroke="currentColor" then resolves to that inherited color. No CSS edit is needed.
|
||||
|
||||
From tests/uat/extension-page-harness.ts:2249-2298 (current A17.8 — Plan 01-10 mark-bundling invariant): the A17 driver locates the welcome JS chunk via the parsed <script src>, fetches jsText, then computes hasInlineDataUrl = jsText.includes('data:image/svg+xml'), hasSvgFileUrl from a .svg-filename regex, and hasCanonicalViewBox from viewBox='0 0 32 32' / URL-percent-encoded variants. The A17.8 check is pushed via result.checks.push({ name: 'A17.8: ...', expected, actual, passed }). After Plan 04-06 the ?raw import inlines the SVG SOURCE as a string literal (no data URL): assert jsText contains the raw source signature stroke="currentColor" AND viewBox="0 0 32 32"; drop the data:image/svg+xml check.
|
||||
From tests/uat/extension-page-harness.ts:2249-2298 (current A17.8 — Plan 01-10 mark-bundling invariant): the A17 driver locates the welcome JS chunk via the parsed <script src>, fetches jsText, then computes hasInlineDataUrl = jsText.includes('data:image/svg+xml'), hasSvgFileUrl from a .svg-filename regex, and hasCanonicalViewBox from viewBox='0 0 32 32' / URL-percent-encoded variants. The A17.8 check is pushed via result.checks.push({ name: 'A17.8: ...', expected, actual, passed }). After Plan 04-06 the ?raw import inlines the SVG SOURCE as a string literal (no data URL): assert jsText contains the raw source signature stroke="currentColor" AND viewBox="0 0 32 32"; drop the data:image/svg+xml check. A17.8 stays a SOURCE-BUNDLING check — the live-DOM behavior moves to the NEW A35.
|
||||
|
||||
SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW chunk is dist/assets/index.ts-<hash>.js (verified empirically; pinned by RESEARCH Q1). All Phase 4 bundle gates use the dist/assets/index.ts-*.js glob, NOT dist/assets/index*-bg.js (which matches no files and would silently 0-grep). Task 4 pre-checkpoint gates use this canonical glob.
|
||||
NEW A35 host-side assertion (driveA35 in tests/uat/lib/harness-page-driver.ts; wired in harness.test.ts):
|
||||
- Signature: export async function driveA35(page: Page, browser: Browser, extensionId: string): Promise<AssertionRecord>. (The `page` param is the harness page — unused for navigation but kept for driver-list signature uniformity; A35 opens its OWN tab via browser.newPage(). Reference the driveA32 page-param-kept-for-uniformity precedent if `page` is genuinely unused, or use it only to read the harness console.)
|
||||
- Body: const welcomePage = await browser.newPage(); const welcomeUrl = `chrome-extension://${extensionId}/src/welcome/welcome.html`; await welcomePage.goto(welcomeUrl, { waitUntil: 'domcontentloaded' }); — welcome.ts init() runs populateMark() at DOMContentLoaded (welcome.ts:194-198). Optionally await welcomePage.waitForSelector('.welcome-hero__mark svg', { timeout: ... }) to be race-free against the readyState branch.
|
||||
- Then run welcomePage.evaluate(() => { ... }) to read the LIVE DOM: querySelector('.welcome-hero__mark svg') is non-null; that element's getAttribute('stroke') === 'currentColor'; getComputedStyle(svgEl).stroke is a resolved non-default color (NOT 'none', NOT empty — proves the currentColor cascade resolved through the .welcome-hero__mark wrapper's color: var(--mks-fg-inverse)); and querySelector('.welcome-hero__mark img') is null (the old <img> path is gone).
|
||||
- Build a CheckRecord[] (e.g. A35.1 svg present, A35.2 stroke attribute === currentColor, A35.3 getComputedStyle().stroke resolved non-default, A35.4 no <img> in the slot) and return an AssertionRecord. ALWAYS await welcomePage.close() in a finally block so the extra tab does not leak. Wrap in try/catch and set result.error on throw — mirror the driveA33/driveA34 host-side error handling.
|
||||
- Wiring in harness.test.ts: add a driveA35Wrapped closure — const driveA35Wrapped: (page: import('puppeteer').Page) => Promise<AssertionRecord> = (page) => driveA35(page, handles.browser, handles.extensionId); — alongside driveA33Wrapped/driveA34Wrapped (lines 367-372). Add { name: 'A35', drive: driveA35Wrapped } as the LAST entry of the `drivers` array (after the A34 entry at line 534). Append ', A35' to the architecture banner string at line 283. driveA35 must be added to the import-from-'./lib/harness-page-driver' block (lines 81-116). The harness total auto-increments via `total = drivers.length + 1` (line 580).
|
||||
|
||||
SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW chunk is dist/assets/index.ts-<hash>.js (verified empirically; pinned by RESEARCH Q1). All Phase 4 bundle gates use the dist/assets/index.ts-*.js glob. Task 4 pre-checkpoint gates use this canonical glob.
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
@@ -216,7 +266,7 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
<action>
|
||||
1. Create the tests/welcome/ directory (NEW — mirrors src/welcome/ per the source-tree convention used by tests/background/, tests/i18n/).
|
||||
|
||||
2. Create tests/welcome/inline-svg.test.ts as a node-env source-contract test (NO DOM, NO runtime DOMParser, NO jsdom — this is the re-plan DEFECT 1 resolution). Mirror the tests/i18n/manifest-i18n.test.ts scaffold:
|
||||
2. Create tests/welcome/inline-svg.test.ts as a node-env source-contract test (NO DOM, NO runtime DOMParser, NO jsdom — this is the DEFECT 1 resolution). Mirror the tests/i18n/manifest-i18n.test.ts scaffold:
|
||||
- import { describe, expect, it } from 'vitest';
|
||||
- import { readFileSync } from 'node:fs'; and import { resolve as resolvePath } from 'node:path';
|
||||
- Define path constants via resolvePath(process.cwd(), ...): MARK_SVG_PATH for src/shared/brand/mokosh-mark.svg, WELCOME_TS_PATH for src/welcome/welcome.ts, GLOBALS_DTS_PATH for globals.d.ts.
|
||||
@@ -224,7 +274,7 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
- Test A: read MARK_SVG_PATH; assert the text contains stroke="currentColor"; assert it contains viewBox="0 0 32 32"; assert it does NOT contain stroke="#181b2a".
|
||||
- Test B: read WELCOME_TS_PATH; assert the text contains the substring mokosh-mark.svg?raw; assert it contains DOMParser; assert it contains replaceChildren; assert it does NOT contain mokosh-mark.svg?url; assert it does NOT contain innerHTML (the MV3-CSP discipline pin and the T-04-06-01 mitigation).
|
||||
- Test C: read GLOBALS_DTS_PATH; assert the text contains the *.svg?raw ambient module declaration string.
|
||||
- Add a header comment block (per the project docstring convention) explaining: this is a node-env source-contract test; live-DOM injection + currentColor cascade is delegated to the A17.8 harness sub-check in real Chrome; cite the re-plan DEFECT 1 resolution and the deliberate no-DOM-library project stance.
|
||||
- Add a header comment block (per the project docstring convention) explaining: this is a node-env source-contract test pinning source TEXT only; it does NOT exercise live DOM because vitest is environment:'node' (vitest.config.ts:18) and the project ships no DOM-emulation library; the live-DOM injection + currentColor cascade is verified by the harness A35 host-side assertion (Task 3), NOT here. Phrase the header so it states "no DOM-emulation library is used" WITHOUT relying on the bare token jsdom being absent — i.e. do not write the explanation in a way that an acceptance grep would trip on (advisory A3).
|
||||
|
||||
3. Create tests/build/cursor-visibility.test.ts per the PATTERNS.md cursor-visibility scaffold (node-env, single-it, file-grep):
|
||||
- import { describe, expect, it } from 'vitest'; import { readFileSync } from 'node:fs'; import { resolve as resolvePath } from 'node:path';
|
||||
@@ -246,7 +296,7 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
- Cursor-visibility test: 1 GREEN today (regression pin for the already-shipped literal).
|
||||
- grep -c 'currentColor' tests/welcome/inline-svg.test.ts returns at least 2.
|
||||
- grep -c "cursor: 'always'" tests/build/cursor-visibility.test.ts returns at least 1.
|
||||
- grep -c 'jsdom' tests/welcome/inline-svg.test.ts returns 0 (no jsdom dependency — DEFECT 1 resolution).
|
||||
- tests/welcome/inline-svg.test.ts has no `import` of any DOM-emulation library and no jsdom-environment directive (verify with: grep -E "^import .*jsdom|@vitest-environment jsdom" tests/welcome/inline-svg.test.ts returns 0 — this scopes the check to import statements + the environment directive, NOT prose, per advisory A3).
|
||||
</acceptance_criteria>
|
||||
<done>2 test files committed; 3 RED + 1 GREEN-on-arrival. Atomic commit: test(04-06): Wave 0 — inline-SVG source-contract RED + cursor-visibility regression pin.</done>
|
||||
</task>
|
||||
@@ -254,7 +304,7 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Wave 1 GREEN — SVG stroke recolor + welcome.ts ?raw import + populateMark inline injection + globals.d.ts ambient decl</name>
|
||||
<files>src/shared/brand/mokosh-mark.svg, src/welcome/welcome.ts, globals.d.ts</files>
|
||||
<read_first>src/shared/brand/mokosh-mark.svg, src/welcome/welcome.ts (full; ~199 lines), globals.d.ts (full; ~51 lines), .planning/phases/04-harden-clean-up-optional/04-UI-SPEC.md section "Implementation amendment", .planning/phases/04-harden-clean-up-optional/04-PATTERNS.md section "src/welcome/welcome.ts:46 + 159-179"</read_first>
|
||||
<read_first>src/shared/brand/mokosh-mark.svg, src/welcome/welcome.ts (full; ~198 lines), globals.d.ts (full; 50 lines), .planning/phases/04-harden-clean-up-optional/04-UI-SPEC.md section "Implementation amendment", .planning/phases/04-harden-clean-up-optional/04-PATTERNS.md section "src/welcome/welcome.ts:46 + 159-179"</read_first>
|
||||
<action>
|
||||
Edit 1 — src/shared/brand/mokosh-mark.svg (1-attribute change on the root <svg>):
|
||||
- Use the Edit tool to replace stroke="#181b2a" with stroke="currentColor" on the root <svg> element (line 2). The 12 <line> + 1 <rect> children inherit stroke from the root and are UNCHANGED — do not touch them.
|
||||
@@ -272,7 +322,11 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
- npx tsc --noEmit exits 0 (the ?raw ambient decl makes welcome.ts tsc-clean).
|
||||
- npm test against tests/welcome/inline-svg.test.ts — Wave 0 RED flips to 3/3 GREEN.
|
||||
- npm test against tests/build/cursor-visibility.test.ts — stays 1/1 GREEN.
|
||||
- Full vitest (npm test --run). Expected: 187 passed / 1 failed (188 total). The 1 failure is the pre-existing tests/build/strict-meta-json-validation.test.ts red (re-plan DEFECT 3 — logged to deferred-items.md, routed to /gsd-debug, OUT OF SCOPE for Plan 04-06). Verify the failure set is EXACTLY {strict-meta-json-validation.test.ts}. If any OTHER test fails, Plan 04-06 caused a regression and must be fixed before commit.
|
||||
- Full vitest (npm test --run). VITEST GATE LOGIC (DEFECT 3 / BLOCKER 2 resolution — do NOT hard-code any test filename):
|
||||
* If the suite is 188/188 GREEN -> PASS.
|
||||
* If the suite is 187 passed / 1 failed AND the single failing test PASSES when re-run in isolation (npm test -- <that-failing-file> --run) -> this is the known 04-CONTEXT #9/#10 non-deterministic parallel-vitest / ffprobe-timeout flake family; TOLERATE it and PASS. Record the flake test name + the isolation re-run result in the SUMMARY.
|
||||
* If the suite has 2+ failures, OR the single failing test STILL FAILS in isolation (i.e. it is reproducible, not a flake) -> FAIL the gate: Plan 04-06 caused a regression; fix it before commit.
|
||||
Note the true clean baseline is 184/184 GREEN; +4 new tests -> 188/188 target. The flake (when it fires) lands on ffprobe/parallel-race tests such as tests/background/webm-remux.test.ts — but the gate decision is by the isolation-re-run rule above, NOT by a name match.
|
||||
- npm run build exits 0; verify the welcome chunk JS bundles the raw SVG source — at least one file under dist/assets/*.js contains the currentColor signature from the inlined SVG source.
|
||||
</action>
|
||||
<verify>
|
||||
@@ -289,30 +343,55 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
- The *.svg?raw ambient module declaration is present in globals.d.ts.
|
||||
- npm test against tests/welcome/inline-svg.test.ts exits 0 with 3/3 GREEN (was 3-RED).
|
||||
- npm test against tests/build/cursor-visibility.test.ts exits 0 with 1/1 GREEN (preserved).
|
||||
- Full vitest: 187 passed / 1 failed; the single failure is exactly tests/build/strict-meta-json-validation.test.ts (pre-existing; out of scope).
|
||||
- Full vitest passes the Task 2 VITEST GATE LOGIC: either 188/188 GREEN, or 187/1 where the single RED passes in isolation (the tolerated 04-CONTEXT #9/#10 flake). 2+ failures or a reproducible single failure FAILS the gate.
|
||||
</acceptance_criteria>
|
||||
<done>SVG + welcome.ts + globals.d.ts edits landed; inline-SVG tests flip 3-RED to 3-GREEN; only the pre-existing strict-meta-json red remains. Atomic commit: feat(04-06): Wave 1 — dark-logo currentColor strategy + inline-SVG injection.</done>
|
||||
<done>SVG + welcome.ts + globals.d.ts edits landed; inline-SVG tests flip 3-RED to 3-GREEN; vitest gate passes (188/188 or a tolerated isolation-passing flake). Atomic commit: feat(04-06): Wave 1 — dark-logo currentColor strategy + inline-SVG injection.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: A17.8 harness sub-check update + 01-07-SUMMARY.md back-patch (corrected line set)</name>
|
||||
<files>tests/uat/extension-page-harness.ts, .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md</files>
|
||||
<read_first>tests/uat/extension-page-harness.ts:2244-2310 (existing A17.8 region — the A17 driver's Step 7 block), .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md (full; ~244 lines), .planning/phases/04-harden-clean-up-optional/04-PATTERNS.md section "tests/uat/extension-page-harness.ts A17.8 sub-check update" and section "01-07-SUMMARY.md back-patch"</read_first>
|
||||
<name>Task 3: A17.8 raw-source update + NEW A35 live-DOM harness assertion + 01-07-SUMMARY back-patch + deferred-items correction</name>
|
||||
<files>tests/uat/extension-page-harness.ts, tests/uat/lib/harness-page-driver.ts, tests/uat/harness.test.ts, .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md, .planning/phases/04-harden-clean-up-optional/deferred-items.md</files>
|
||||
<read_first>tests/uat/extension-page-harness.ts:2249-2308 (existing A17.8 region — the A17 driver Step 7 block), tests/uat/lib/harness-page-driver.ts:2425-2500 + 2622-2760 (driveA32 + driveA33/A34 host-side driver pattern — signature, browser.newPage usage, CheckRecord[] build, try/catch + error handling), tests/uat/harness.test.ts:81-116 (driver imports) + 360-535 (driveA33Wrapped/driveA34Wrapped closures + the `drivers` array) + 283 (architecture banner string) + 577-580 (total = drivers.length + 1), tests/uat/lib/launch.ts:473-542 (launchHarnessBrowser return shape — confirm handles.browser + handles.extensionId), tests/uat/lib/assertions.ts:25-44 (AssertionRecord/CheckRecord), src/welcome/welcome.ts:194-198 (init() runs populateMark at DOMContentLoaded), .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md (full; 244 lines), .planning/phases/04-harden-clean-up-optional/deferred-items.md (full), .planning/phases/04-harden-clean-up-optional/04-PATTERNS.md section "tests/uat/extension-page-harness.ts A17.8 sub-check update" and section "01-07-SUMMARY.md back-patch"</read_first>
|
||||
<action>
|
||||
Edit 1 — A17.8 sub-check (tests/uat/extension-page-harness.ts, the A17 driver Step 7 block at lines 2249-2298):
|
||||
Edit 1 — A17.8 sub-check (tests/uat/extension-page-harness.ts, the A17 driver Step 7 block at lines 2249-2298) — SOURCE-BUNDLING check only:
|
||||
- Read the existing A17.8 block.
|
||||
- The ?raw import bundles the SVG SOURCE as a string literal into the welcome chunk (no more data:image/svg+xml data URL). Update the sub-check:
|
||||
(a) Replace the hasInlineDataUrl computation (jsText.includes('data:image/svg+xml')) and the svgFileUrl regex with a raw-source check: assert jsText contains the canonical mark signature stroke="currentColor" AND viewBox="0 0 32 32" (the inlined raw SVG source). Keep the hasCanonicalViewBox check; it remains valid (the raw source carries the literal viewBox="0 0 32 32"). Update the diag() line and the result.checks.push({ name, expected, actual, passed }) object so the A17.8 name + expected describe the raw-source bundling.
|
||||
(b) RECOMMENDED defense-in-depth (pick this unless the harness page cannot reach the welcome DOM in this driver): add an A17.8b check that, in the already-loaded welcome page DOM, document.querySelector('.welcome-hero__mark svg') is non-null AND that element's getAttribute('stroke') === 'currentColor' AND document.querySelector('.welcome-hero__mark img') is null. This is the real-Chrome verification of the inline-SVG injection + the currentColor attribute that the node-env unit test deliberately cannot cover (re-plan DEFECT 1 — the unit test pins source; the harness pins real DOM). If A17.8b is added, push it as a second result.checks entry; if the driver structure makes a second DOM query infeasible, document why in a code comment and ship A17.8a only — A17.8a alone still satisfies UI-SPEC Acceptance Criterion #3.
|
||||
- Note the line range: A17.8 is at ~line 2294 — disjoint from the Plan 04-04/04-05 harness appends at ~line 3970+, which is what lets Plan 04-06 land parallel-safe.
|
||||
- The ?raw import bundles the SVG SOURCE as a string literal into the welcome chunk (no more data:image/svg+xml data URL). Update the sub-check: replace the hasInlineDataUrl computation (jsText.includes('data:image/svg+xml')) and the svgFileUrl regex with a raw-source check — assert jsText contains the canonical mark signature stroke="currentColor" AND viewBox="0 0 32 32" (the inlined raw SVG source). Keep the hasCanonicalViewBox check; it remains valid (the raw source carries the literal viewBox="0 0 32 32"). Update the diag() line and the result.checks.push({ name, expected, actual, passed }) object so the A17.8 name + expected describe RAW-SOURCE bundling.
|
||||
- Update the A17.8 explanatory comment block (lines ~2249-2272) to state honestly: A17.8 is a SOURCE-BUNDLING check (it confirms the ?raw import inlined the SVG source string); it does NOT prove the inline-SVG is injected into the live DOM or that the currentColor cascade resolves — that runtime behavior is verified by the NEW host-side assertion A35 (see driveA35). Do NOT claim A17.8 covers live-DOM injection.
|
||||
- Note the line range: A17.8 is at ~line 2294 — disjoint from the Plan 04-04/04-05 harness appends at ~line 3970+, which keeps Plan 04-06 land parallel-safe.
|
||||
|
||||
Edit 2 — 01-07-SUMMARY.md back-patch (CORRECTED line set per re-plan DEFECT 2):
|
||||
Edit 2 — NEW host-side A35 assertion (tests/uat/lib/harness-page-driver.ts) — the LIVE-DOM injection + currentColor cascade proof:
|
||||
- Append a new driveA35 host-side driver function at the end of the harness-page-driver.ts driver section, modeled on the driveA32/driveA33/driveA34 host-side pattern (host-side: builds a CheckRecord[] + returns an AssertionRecord directly; NOT a page.evaluate(window.__mokoshHarness) wrapper).
|
||||
- Signature: export async function driveA35(page: Page, browser: Browser, extensionId: string): Promise<AssertionRecord>. (Browser + Page are already imported at harness-page-driver.ts:43.) The `page` param is the harness page; A35 does not navigate it — A35 opens its OWN welcome tab. If `page` is genuinely unused, follow the driveA32 page-param-kept-for-signature-uniformity precedent (or annotate it).
|
||||
- Body:
|
||||
- const checks: CheckRecord[] = []; const diagnostics: string[] = [];
|
||||
- Wrap the work in try/catch. In a finally block, await welcomePage.close() so the extra tab never leaks.
|
||||
- const welcomePage = await browser.newPage();
|
||||
- const welcomeUrl = `chrome-extension://${extensionId}/src/welcome/welcome.html`; (this is the canonical web-accessible welcome page path — the same path chrome.runtime.getURL('src/welcome/welcome.html') resolves to, which A17 already fetches; welcome.html builds to dist-test/src/welcome/welcome.html per vite.test.config.ts).
|
||||
- await welcomePage.goto(welcomeUrl, { waitUntil: 'domcontentloaded' }); — welcome.ts init() runs populateMark() at DOMContentLoaded (welcome.ts:194-198).
|
||||
- await welcomePage.waitForSelector('.welcome-hero__mark svg', { timeout: <a reasonable ms; reuse a HARNESS_*_TIMEOUT_MS-class constant or define an A35-local one> }) to be race-free against the welcome.ts readyState branch. If the selector never appears, the catch path records the failure.
|
||||
- Run a single welcomePage.evaluate(() => { ... }) that reads the LIVE DOM and returns a plain-object result: { svgPresent: querySelector('.welcome-hero__mark svg') !== null, strokeAttr: <that svg>.getAttribute('stroke'), computedStroke: getComputedStyle(<that svg>).stroke, imgPresent: querySelector('.welcome-hero__mark img') !== null }. Guard the getAttribute/getComputedStyle calls so a null element does not throw inside evaluate.
|
||||
- Push 4 CheckRecords:
|
||||
A35.1 — inline <svg> injected into the mark slot: expected non-null, actual svgPresent, passed === svgPresent.
|
||||
A35.2 — injected <svg> carries stroke="currentColor": expected 'currentColor', actual strokeAttr, passed === (strokeAttr === 'currentColor').
|
||||
A35.3 — getComputedStyle().stroke resolves to a non-default color (the currentColor cascade resolved through .welcome-hero__mark color: var(--mks-fg-inverse)): expected a non-empty, non-'none' resolved color, actual computedStroke, passed === (computedStroke is non-empty AND computedStroke !== 'none'). This is the real proof the cascade works.
|
||||
A35.4 — no legacy <img> in the mark slot: expected null/false, actual imgPresent, passed === !imgPresent.
|
||||
- result.passed = checks.every((c) => c.passed); return { passed, name: 'A35 — welcome-page inline-SVG injected at populateMark() runtime; currentColor stroke resolves via parent CSS color cascade (UI-SPEC dark-logo strategy live-DOM proof)', checks, diagnostics }.
|
||||
- On a thrown error, return an AssertionRecord with passed:false + error set (mirror driveA33/driveA34 host-side error handling). Always close welcomePage.
|
||||
- Add a function docstring per project convention explaining: A35 is the LIVE-DOM counterpart to the source-only A17.8 + the source-only inline-svg.test.ts; it opens welcome.html as a real tab so populateMark() actually runs; it verifies the injected <svg> + the currentColor cascade — the runtime behavior the node-env unit test deliberately cannot cover. Cite the iter-2 BLOCKER 1 resolution.
|
||||
|
||||
Edit 3 — wire A35 into the orchestrator (tests/uat/harness.test.ts):
|
||||
- Add driveA35 to the import block from './lib/harness-page-driver' (the block at lines 81-116, where driveA33/driveA34 are imported).
|
||||
- Add a driveA35Wrapped closure alongside driveA33Wrapped/driveA34Wrapped (lines 367-372): const driveA35Wrapped: (page: import('puppeteer').Page) => Promise<AssertionRecord> = (page) => driveA35(page, handles.browser, handles.extensionId);
|
||||
- Add { name: 'A35', drive: driveA35Wrapped } as the LAST entry of the `drivers` array (immediately after the A34 entry at line 534). The harness total auto-increments via `total = drivers.length + 1` (line 580) — no hardcoded count to bump.
|
||||
- Append ', A35' to the architecture banner string at line 283 (the literal driver-name list).
|
||||
|
||||
Edit 4 — 01-07-SUMMARY.md back-patch (CORRECTED line set per DEFECT 2):
|
||||
- Read .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md (full).
|
||||
- Re-confirm the line set with: grep -nE 'Phase 5|deferred to Phase 5' .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md (the executor MUST verify the live file rather than trusting cited numbers).
|
||||
- FLIP these 5 lines — the genuine stale "deferred to Phase 5" framing (cursor:'always' actually shipped opportunistically in Plan 01-09, so the deferral framing is false):
|
||||
- Line 22 (the affects: dependency-graph entry: "Phase 5 (P1/P2 hardening) — new entry: getDisplayMedia cursor visibility constraint"). Flip to note the constraint shipped in Plan 01-09 and was verified in Phase 4 Plan 04-06.
|
||||
- Line 47 (key-decisions: "Cursor visibility ... deferred to Phase 5, not back-patched into Phase 1 ..."). Flip to "Cursor visibility (cursor: 'always') was opportunistically shipped in Plan 01-09 at recorder.ts:285 and verified in Phase 4 Plan 04-06; the Phase 1 closure correctly did not back-patch it into Phase 1."
|
||||
- Line 82 (accomplishments: "Cursor-visibility refinement deferred to Phase 5 ..."). Flip per the PATTERNS.md back-patch text: "Cursor-visibility refinement opportunistically shipped in Plan 01-09 (recorder.ts:285 cursor: 'always'); verified in Phase 4 Plan 04-06 via tests/build/cursor-visibility.test.ts + operator-empirical SAVE flow showing the pointer visible in last_30sec.webm."
|
||||
- Line 82 (accomplishments: "Cursor-visibility refinement deferred to Phase 5 ..."). Flip to: "Cursor-visibility refinement opportunistically shipped in Plan 01-09 (recorder.ts:285 cursor: 'always'); verified in Phase 4 Plan 04-06 via tests/build/cursor-visibility.test.ts + operator-empirical SAVE flow showing the pointer visible in last_30sec.webm."
|
||||
- Line 135 (Decisions Made #5: "Cursor visibility refinement deferred to Phase 5 ..."). Flip the framing to reflect the Plan 01-09 opportunistic shipping while keeping the historical point that Phase 1 closure did not widen its own diff.
|
||||
- Line 205 (User Setup Required: "The cursor visibility refinement (Phase 5) will, when activated, add a cursor: 'always' constraint ..."). Flip to past tense: the constraint was added in Plan 01-09; no user action was required; Phase 4 Plan 04-06 verified it.
|
||||
- LEAVE these 4 lines UNCHANGED — they are historical descriptions of what specific Plan 01-07-closure commits DID, not forward-looking deferrals:
|
||||
@@ -322,25 +401,35 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
- Line 110 (Files Created/Modified: describes the STATE.md modification, including the [Phase 01-07-deferred-to-5] decision-log tag — a historical commit record).
|
||||
- Each flip is surgical (single line or single bullet, with surrounding context). Do NOT rewrite the whole SUMMARY. After the flips, the SUMMARY narrative is internally consistent: the 5 flipped lines acknowledge Plan 01-09 + Phase 4; the 4 historical lines still accurately describe what the Phase-1-closure commits recorded at the time.
|
||||
|
||||
Edit 5 — deferred-items.md correction (DEFECT 3 / BLOCKER 2 — the prior entry mis-diagnosed the flake):
|
||||
- Read .planning/phases/04-harden-clean-up-optional/deferred-items.md.
|
||||
- The existing entry "tests/build/strict-meta-json-validation.test.ts failing on clean tree" is a MISDIAGNOSIS: that test is GREEN in isolation (8/8) AND GREEN on a clean tree; the actual full-suite RED observed this session was tests/background/webm-remux.test.ts (ffprobe -count_frames timeout), which is also GREEN in isolation (5/5). Correct the entry: re-title/rewrite it to describe the real root cause — a non-deterministic parallel-vitest / ffprobe-timeout flake family that is already 04-CONTEXT.md in-scope items #9 (parallel-vitest Tier-1-build-step race, ~1/5 runs) and #10 (2 ffprobe/ffmpeg flakes). State that the true clean baseline is 184/184 GREEN; the "1 failed" is the flake, which lands non-deterministically on whichever ffprobe/parallel-race test loses the worker race. Remove the false claim that strict-meta-json-validation.test.ts "fails on a clean tree". If a /gsd-debug route is noted, route it as "the 04-CONTEXT #9/#10 parallel-vitest ffprobe flake family", NOT as "strict-meta-json fails on a clean tree".
|
||||
|
||||
Verify gates:
|
||||
- npx tsc --noEmit exits 0 (the new driveA35 + harness wiring is tsc-clean).
|
||||
- npm run build:test exits 0.
|
||||
- Run the UAT harness (HEADLESS=1 SKIP_PROD_REBUILD=0 SKIP_LONG_UAT=1 npm run test:uat). Expected: the full harness driver set GREEN with the updated A17.8. The dark-logo strategy land must not regress any other assertion. (npm run test:uat performs its own build:test internally; SKIP_PROD_REBUILD/SKIP_LONG_UAT are consumed inside the harness.)
|
||||
- Run the UAT harness (HEADLESS=1 SKIP_PROD_REBUILD=0 SKIP_LONG_UAT=1 npm run test:uat). SKIP_PROD_REBUILD=0 is intentional: the harness must rebuild dist-test against the Task 2 source edits so A17.8 (raw-source grep) and the new A35 (live welcome tab) run against the updated welcome bundle — a stale dist-test would not carry the ?raw inlining (advisory A1: action and verify both use SKIP_PROD_REBUILD=0). Expected: the full harness driver set GREEN with the updated A17.8 AND the new A35. The dark-logo strategy land must not regress any other assertion.
|
||||
- grep -c 'Phase 4 Plan 04-06' .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md returns at least 1 (the new back-patch citation appears in the flipped lines).
|
||||
- The 5 flipped lines no longer carry "deferred to Phase 5" framing; the 4 historical lines still mention Phase 5 as a record of what the closure commits did — that is correct and expected.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npm run build:test 2>&1 | tail -5 && HEADLESS=1 SKIP_PROD_REBUILD=1 SKIP_LONG_UAT=1 npm run test:uat 2>&1 | tail -15</automated>
|
||||
<automated>npx tsc --noEmit && npm run build:test 2>&1 | tail -5 && HEADLESS=1 SKIP_PROD_REBUILD=0 SKIP_LONG_UAT=1 npm run test:uat 2>&1 | tail -20</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- npx tsc --noEmit exits 0.
|
||||
- npm run build:test exits 0.
|
||||
- The UAT harness driver set is GREEN with the updated A17.8 (no harness regression from the dark-logo land).
|
||||
- The UAT harness driver set is GREEN with the updated A17.8 AND the new A35 (no harness regression from the dark-logo land); the harness total reported is the prior total + 1 (A35 added).
|
||||
- grep -c 'driveA35' tests/uat/lib/harness-page-driver.ts returns at least 1.
|
||||
- grep -c 'driveA35' tests/uat/harness.test.ts returns at least 2 (the import + the wrapped-closure entry).
|
||||
- grep -c "name: 'A35'" tests/uat/harness.test.ts returns at least 1 (the drivers-array entry).
|
||||
- grep -c 'getComputedStyle' tests/uat/lib/harness-page-driver.ts returns at least 1 (the A35 currentColor-cascade probe).
|
||||
- grep -c 'stroke="currentColor"' tests/uat/extension-page-harness.ts returns at least 1 (the new A17.8 raw-source grep target).
|
||||
- The data:image/svg+xml grep target is removed from the A17.8 block (the ?raw import no longer produces a data URL).
|
||||
- 01-07-SUMMARY.md lines 22, 47, 82, 135, 205 no longer carry the stale "deferred to Phase 5" framing.
|
||||
- 01-07-SUMMARY.md lines 40, 89, 109, 110 are unchanged (historical commit-description lines).
|
||||
- grep -c 'Phase 4 Plan 04-06' .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md returns at least 1.
|
||||
- deferred-items.md no longer claims strict-meta-json-validation.test.ts "fails on a clean tree"; the entry now describes the 04-CONTEXT #9/#10 parallel-vitest flake family.
|
||||
</acceptance_criteria>
|
||||
<done>A17.8 updated to assert raw-source bundling (+ optional real-Chrome A17.8b); 01-07-SUMMARY.md back-patched with the corrected 22/47/82/135/205 line set. Atomic commit: feat(04-06): A17.8 inline-SVG check + back-patch 01-07-SUMMARY stale Phase-5 lines.</done>
|
||||
<done>A17.8 updated to raw-source bundling check; NEW host-side A35 assertion opens a live welcome tab + verifies the injected <svg> + the currentColor cascade; A35 wired into the orchestrator; 01-07-SUMMARY.md back-patched (22/47/82/135/205); deferred-items.md flake mis-diagnosis corrected. Atomic commit: feat(04-06): A35 live-DOM inline-SVG harness check + A17.8 raw-source update + back-patch.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
@@ -358,7 +447,7 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
5. Tier-1 SW-bundle-import gate: vitest run tests/background/sw-bundle-import.test.ts is GREEN.
|
||||
6. Tier-1 FORBIDDEN_HOOK_STRINGS gate: vitest run tests/background/no-test-hooks-in-prod-bundle.test.ts is GREEN (12 strings, 0 hits each — Plan 04-06 adds no new __MOKOSH_UAT__-gated symbols, so the inventory stays at 12).
|
||||
|
||||
Also confirm the vitest baseline: full vitest is 187 passed / 1 failed, where the single failure is exactly tests/build/strict-meta-json-validation.test.ts (pre-existing; re-plan DEFECT 3; out of scope). And the UAT harness driver set is GREEN.
|
||||
Also confirm the vitest baseline per the Task 2 VITEST GATE LOGIC: the full suite is 188/188 GREEN, OR 187/1 where the single RED passes when re-run in isolation (the tolerated 04-CONTEXT #9/#10 non-deterministic parallel-vitest / ffprobe-timeout flake family — NOT a regression, NOT a named test). 2+ failures or a reproducible single failure is a regression. And the UAT harness driver set (now including the new A35) is GREEN.
|
||||
|
||||
Step 2 — Produce the verification artifact (light + dark Puppeteer screenshots; per feedback-trust-harness-over-manual-uat.md the operator judges from screenshots, NOT a manual Chrome session):
|
||||
- Load dist/ as an unpacked extension and open the welcome page in a Puppeteer-driven Chrome.
|
||||
@@ -374,7 +463,7 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
<automated>npm run build 2>&1 | tail -3 && ls dist/assets/index.ts-*.js</automated>
|
||||
</verify>
|
||||
<what-built>
|
||||
UI-SPEC dark-logo contrast strategy: SVG stroke recolor (stroke="currentColor") + inline-SVG injection in welcome.ts (DOMParser + replaceChildren; no <img>, no innerHTML, no eval) + globals.d.ts *.svg?raw ambient decl + A17.8 harness raw-source assertion + cursor-visibility node-env regression pin + 01-07-SUMMARY.md back-patch (5 stale Phase-5 lines flipped). Pre-checkpoint bundle gates 6/6 PASS:
|
||||
UI-SPEC dark-logo contrast strategy: SVG stroke recolor (stroke="currentColor") + inline-SVG injection in welcome.ts (DOMParser + replaceChildren; no <img>, no innerHTML, no eval) + globals.d.ts *.svg?raw ambient decl + A17.8 harness raw-source assertion + NEW A35 live-DOM harness assertion (opens a real welcome tab, verifies the injected <svg> + currentColor cascade) + cursor-visibility node-env regression pin + 01-07-SUMMARY.md back-patch (5 stale Phase-5 lines flipped) + deferred-items.md flake mis-diagnosis correction. Pre-checkpoint bundle gates 6/6 PASS:
|
||||
- Gate 1: npm run build exit 0.
|
||||
- Gate 2: new Function / eval count in dist/assets/index.ts-*.js returns 0 (Plan 04-02 effect preserved; canonical SW chunk glob).
|
||||
- Gate 3: SW Node-globals grep clean against dist/assets/index.ts-*.js.
|
||||
@@ -382,11 +471,12 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
- Gate 5: Tier-1 SW-bundle-import unit gate GREEN.
|
||||
- Gate 6: FORBIDDEN_HOOK_STRINGS at 12 (no change — Plan 04-06 adds no test-mode symbols).
|
||||
- Glob-existence pre-gate: ls dist/assets/index.ts-*.js lists at least one file (prevents silent 0-hit pass on a mis-glob).
|
||||
- Plus: vitest 187 passed / 1 pre-existing fail (strict-meta-json-validation.test.ts, out of scope); UAT harness driver set GREEN.
|
||||
- Plus: vitest 188/188 GREEN (or a tolerated single isolation-passing 04-CONTEXT #9/#10 flake); UAT harness driver set GREEN including the new A35.
|
||||
|
||||
Auto-confirmed (no operator action needed):
|
||||
- mokosh-mark.svg now uses stroke="currentColor" (1-attribute semantic change; 13 children unchanged).
|
||||
- welcome.ts inline-injects the SVG via DOMParser + replaceChildren (no <img>, no innerHTML, no eval).
|
||||
- The inline-SVG injection + the currentColor cascade are now verified AUTOMATICALLY in real Chrome by the new A35 harness assertion (a live welcome tab where populateMark() runs; A35 reads getComputedStyle().stroke on the injected <svg>).
|
||||
- On the LIGHT surface the mark appears identical to the Plan 01-10 cycle-2 ack — color: var(--mks-fg-inverse) at .welcome-hero__mark cascades to the inline SVG's currentColor, resolving to linen-50 (off-white) stroke on the madder-orange wrapper background.
|
||||
- PNG toolbar icons unchanged (Chrome auto-inverts dark icons on dark toolbars).
|
||||
- cursor: 'always' already at recorder.ts:285 (Plan 01-09 opportunistic); pinned by tests/build/cursor-visibility.test.ts.
|
||||
@@ -419,28 +509,31 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
| Vite ?raw import to compile-time string literal | SVG source is bundled as a compile-time string; NO runtime untrusted input flows into DOMParser; the parser input is statically known at build time |
|
||||
| DOMParser parse to live DOM | parseFromString('image/svg+xml') is a CSP-safe operation per MDN — it does NOT execute scripts in the parsed SVG and runs in a sandboxed parser context; no innerHTML; no eval |
|
||||
| Inline <svg> CSS color inheritance | inline SVG (parsed + appended via DOMParser + replaceChildren) inherits parent CSS color per W3C SVG2 §13.3; currentColor on stroke resolves to the wrapper's color: var(--mks-fg-inverse) cascade |
|
||||
| New A35 harness tab | driveA35 opens an extra browser.newPage() against chrome-extension://<id>/src/welcome/welcome.html; the tab is test-only (gated dist-test build), always closed in a finally block — no production surface, no leaked tab |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|-------------|-----------------|
|
||||
| T-04-06-01 | Tampering | a future developer might switch DOMParser to innerHTML for "simplicity" — innerHTML in an extension-page context is unsafe (CSP risk + script-execution surface) | mitigate | Inline code comment in populateMark + the executor instruction "NEVER use innerHTML — DOMParser + replaceChildren only (MV3 CSP discipline)"; tests/welcome/inline-svg.test.ts Test B asserts the welcome.ts source does NOT contain innerHTML; the A17.8 harness check verifies the inline-SVG DOM shape in real Chrome (defense in depth) |
|
||||
| T-04-06-01 | Tampering | a future developer might switch DOMParser to innerHTML for "simplicity" — innerHTML in an extension-page context is unsafe (CSP risk + script-execution surface) | mitigate | Inline code comment in populateMark + the executor instruction "NEVER use innerHTML — DOMParser + replaceChildren only (MV3 CSP discipline)"; tests/welcome/inline-svg.test.ts Test B asserts the welcome.ts source does NOT contain innerHTML; the NEW A35 harness assertion verifies the inline-SVG DOM shape live in real Chrome (defense in depth) |
|
||||
| T-04-06-02 | Information Disclosure | the inline SVG carries no PII or secret — it is the canonical Mokosh brand mark | accept | Static brand asset; out-of-tree threat surface |
|
||||
| T-04-06-03 | Spoofing | cursor visibility in captured frames could leak transient UI overlay state (e.g. a 2FA OTP digit the operator was about to type) — but this is a known and intended diagnostic feature per the Plan 01-07 observation; password masking is out of scope per the D-P3-02 charter | accept | Operator-side responsibility per the CONTEXT charter; v2 candidate per CONTEXT Deferred Ideas |
|
||||
| T-04-06-04 | Repudiation | a mis-globbed dist/assets/index*-bg.js matches no files; grep -c new Function returns 0; Gate 2 spuriously PASSES even if a CSP regression slipped in | mitigate | Canonical SW chunk glob dist/assets/index.ts-*.js (verified empirically; pinned by RESEARCH Q1); the Task 4 glob-existence pre-gate (ls dist/assets/index.ts-*.js lists at least one file) runs BEFORE the grep gates and fails loudly if the glob matches nothing |
|
||||
| T-04-06-SC | Tampering | npm/pip/cargo installs introducing a supply-chain risk | accept | Plan 04-06 introduces NO new package-manager installs — files_modified contains only existing source/test/spec/docs files; no devDependency is added (the DEFECT 1 resolution explicitly rejected adding jsdom). Package legitimacy gate is therefore not triggered. |
|
||||
| T-04-06-05 | Repudiation | the parallel-vitest ffprobe-timeout flake (04-CONTEXT #9/#10) could mask a real Plan-04-06 regression — a hard-coded named-test gate would either falsely blame the plan or falsely pass | mitigate | Task 2 VITEST GATE LOGIC tolerates a single RED ONLY when that exact test passes on an isolation re-run (npm test -- <file>); a reproducible failure or 2+ failures FAILS the gate. No test filename is hard-coded — the gate distinguishes flake from regression by re-run behavior, not by name |
|
||||
| T-04-06-SC | Tampering | npm/pip/cargo installs introducing a supply-chain risk | accept | Plan 04-06 introduces NO new package-manager installs — files_modified contains only existing source/test/spec/docs/harness files; no devDependency is added (the DEFECT 1 resolution explicitly rejected adding jsdom; the live-DOM coverage is the new in-repo A35 harness assertion, not a new dependency). Package legitimacy gate is therefore not triggered. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- npx tsc --noEmit exits 0.
|
||||
- npx tsc --noEmit exits 0 (including the new driveA35 + harness wiring).
|
||||
- npm run build exits 0.
|
||||
- Full vitest: 187 passed / 1 failed (188 total); the single failure is exactly tests/build/strict-meta-json-validation.test.ts (pre-existing; re-plan DEFECT 3; out of scope, routed to /gsd-debug). +4 new tests over the 183-GREEN baseline (Task 1's 3 RED inline-svg tests flip GREEN in Task 2; Task 1's 1 cursor-visibility test is GREEN throughout).
|
||||
- Full vitest per the Task 2 VITEST GATE LOGIC: 188/188 GREEN, OR 187/1 where the single RED passes when re-run in isolation (the tolerated 04-CONTEXT #9/#10 non-deterministic parallel-vitest / ffprobe-timeout flake family — NOT a regression, NOT a named test). +4 new tests over the 184/184 GREEN baseline (Task 1's 3 RED inline-svg tests flip GREEN in Task 2; Task 1's 1 cursor-visibility test is GREEN throughout). 2+ failures or a reproducible single failure FAILS.
|
||||
- grep -c 'stroke="currentColor"' src/shared/brand/mokosh-mark.svg returns 1.
|
||||
- grep -c 'svg?raw' src/welcome/welcome.ts returns at least 1; grep -c 'svg?url' src/welcome/welcome.ts returns 0.
|
||||
- grep -c 'innerHTML' src/welcome/welcome.ts returns 0.
|
||||
- The *.svg?raw ambient module declaration is present in globals.d.ts.
|
||||
- UAT harness driver set GREEN with the updated A17.8 (assertion quality changes; driver count unchanged).
|
||||
- UAT harness driver set GREEN with the updated A17.8 (raw-source bundling check) AND the new host-side A35 (live-DOM inline-SVG injection + currentColor cascade proof); harness total = prior total + 1.
|
||||
- 01-07-SUMMARY.md back-patched: lines 22/47/82/135/205 flipped; lines 40/89/109/110 unchanged.
|
||||
- deferred-items.md flake entry corrected — no longer mis-names strict-meta-json-validation.test.ts; describes the 04-CONTEXT #9/#10 parallel-vitest flake family.
|
||||
- Operator empirical ack received (Task 4 resume signal "approved") from the dark + light screenshot artifact.
|
||||
- Pre-checkpoint bundle gates 6/6 PASS (canonical SW chunk glob dist/assets/index.ts-*.js across Gates 2/3/4; glob-existence pre-gate validates the glob first).
|
||||
- PNG toolbar icons byte-identical (no change to scripts/rasterize-icons.sh or icons/*.png).
|
||||
@@ -449,13 +542,13 @@ SW chunk canonical glob (inherited from Phase 4 REVISION iter-2): the Vite SW ch
|
||||
<success_criteria>
|
||||
- UI-SPEC dark-logo strategy landed end-to-end (Tasks 1-3 automation + Task 4 operator ack).
|
||||
- Cursor visibility VERIFIED at recorder.ts:285 via the node-env regression-pin test + the 01-07-SUMMARY back-patch (RESEARCH Finding 4 closure).
|
||||
- Inline-SVG injection contract pinned by 3 node-env source-level unit tests (DEFECT 1 strategy) + the A17.8 real-Chrome harness assertion.
|
||||
- Inline-SVG injection contract pinned at TWO honest layers: 3 node-env source-level unit tests (source TEXT contract) + the NEW A35 host-side harness assertion (live-DOM injection + currentColor cascade in real Chrome). A17.8 is a source-bundling check only — no claim of live-DOM coverage.
|
||||
- ROADMAP cursor visibility item GREEN.
|
||||
- ROADMAP dark-surface logo contrast item GREEN.
|
||||
- Operator empirical ack received on the dark-mode visual aesthetic, judged from the Puppeteer screenshot artifact.
|
||||
- Pre-checkpoint bundle gates 6/6 PASS preserved (canonical glob).
|
||||
- UAT harness driver set GREEN preserved.
|
||||
- Vitest: 187 GREEN / 1 pre-existing RED (strict-meta-json-validation.test.ts is out of scope and explicitly tolerated).
|
||||
- UAT harness driver set GREEN preserved, now +1 (A35).
|
||||
- Vitest: 188/188 GREEN (or a tolerated single isolation-passing 04-CONTEXT #9/#10 flake; no hard-coded named-test gate).
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
@@ -463,14 +556,15 @@ After completion, create .planning/phases/04-harden-clean-up-optional/04-06-SUMM
|
||||
- mokosh-mark.svg diff (1-attribute change documented).
|
||||
- welcome.ts diff (?raw import + DOMParser + populateMark rewrite).
|
||||
- globals.d.ts diff (*.svg?raw ambient decl append).
|
||||
- A17.8 harness sub-check diff (old data-URL grep to new raw-source grep; note whether A17.8b real-Chrome DOM check was added).
|
||||
- A17.8 harness sub-check diff (old data-URL grep to new raw-source grep; A17.8 documented as source-bundling-only).
|
||||
- The NEW A35 host-side harness assertion (driveA35 — opens a live welcome tab, verifies the injected <svg> + getComputedStyle().stroke currentColor cascade) + the harness.test.ts wiring (driveA35Wrapped + drivers-array entry + banner string).
|
||||
- 01-07-SUMMARY.md back-patch (5 stale Phase-5 lines flipped — 22/47/82/135/205; 4 historical lines left — 40/89/109/110).
|
||||
- deferred-items.md correction (the strict-meta-json mis-diagnosis replaced with the 04-CONTEXT #9/#10 parallel-vitest flake-family description).
|
||||
- 2 new test files (inline-svg source-contract + cursor-visibility) with the RED-to-GREEN cycle commits.
|
||||
- The DEFECT 1 test-strategy decision recorded (node-env source-contract, no jsdom) so future plans understand why inline-svg.test.ts does not exercise live DOM.
|
||||
- The DEFECT 1 / BLOCKER 1 test-strategy decision recorded (node-env source-contract for source TEXT + the new A35 host-side harness assertion for live-DOM behavior; A17.8 is source-bundling only) so future plans understand the coverage split.
|
||||
- Operator empirical UAT verbatim ack (e.g. "approved", or the specific issue raised).
|
||||
- Pre-checkpoint bundle gates 6/6 PASS evidence (grep outputs against the canonical dist/assets/index.ts-*.js glob).
|
||||
- vitest before/after (183 GREEN + 1 pre-existing RED to 187 GREEN + 1 pre-existing RED).
|
||||
- UAT harness driver-set GREEN preservation.
|
||||
- Note that tests/build/strict-meta-json-validation.test.ts remains a pre-existing RED out of Plan 04-06 scope (routed to /gsd-debug per deferred-items.md).
|
||||
- vitest before/after (184/184 GREEN baseline -> 188/188 GREEN; note the 04-CONTEXT #9/#10 parallel-vitest flake family may transiently land 1 RED that passes on an isolation re-run — that is tolerated, not a regression).
|
||||
- UAT harness driver-set GREEN preservation (now +1 with A35).
|
||||
- Commit refs (Tasks 1 + 2 + 3 + 4 ack).
|
||||
</output>
|
||||
|
||||
Reference in New Issue
Block a user