Plan 04-06 closure — the most ceremony-heavy plan in Phase 4: 3 planner passes + 2 plan-checker passes + 4 task commits + 1 /gsd-debug fix cycle + this closure commit. D-P4-03 (locked, 04-CONTEXT.md) CLOSED — both visual polish items: (a) cursor visibility verification + (b) dark-surface logo contrast. Closure trail:6a989e8mis-diagnosed strict-meta-json deferred-items entryb59bd24re-plan iter-1 — correct false jsdom premise + back-patch linesdeb68dfre-plan-checker iter-1 — ITERATE-NEEDED (2 BLOCKER)f3baa3are-plan iter-2 — real A35 + corrected 184/184 baseline48c7053re-plan-checker iter-2 — PASSED (0B + 0W + 3 cosmetic-advisories)f0b88d4Task 1 — Wave 0 RED inline-SVG source-contract + cursor pinc416143Task 2 — Wave 1 GREEN SVG+welcome.ts+globals.d.ts3f8e31aTask 3 — A35 driver + A17.8 narrowed + back-patch + correctiond66cbf6Task 4 artifact — operator-empirical screenshot harness (Task 4 first operator empirical: TWEAK verdict 2026-05-26)a8bcc17debug-fix — decouple via --mks-mark-stroke + A35.5 sub-check (Task 4 re-empirical: CONFIRMED FIXED 2026-05-26) THIS closure (SUMMARY + STATE.md + ROADMAP.md + debug archive) Key deliverables: - mokosh-mark.svg stroke="#181b2a" -> stroke="currentColor" - welcome.ts ?url/<img> -> ?raw/DOMParser/replaceChildren inline-<svg> - globals.d.ts *.svg?raw ambient decl - src/shared/tokens.css NEW --mks-mark-stroke = var(--mks-linen-50) in :root (NOT overridden in .dark — theme-independent brand-component token) - src/welcome/welcome.css .welcome-hero__mark rewired to --mks-mark-stroke - NEW A35 host-side harness (5 sub-checks incl. A35.5 light+dark equality decouple-proof) at tests/uat/lib/harness-page-driver.ts - A17.8 honestly narrowed to SOURCE-BUNDLING only; points to A35 - tests/welcome/inline-svg.test.ts (3 source-contract tests) - tests/build/cursor-visibility.test.ts (1 regression pin) - scripts/04-06-welcome-hero-screenshots.mjs (reproducible artifact) - 01-07-SUMMARY back-patch (5 stale lines flipped; 4 historical left) - deferred-items.md mis-diagnosis correction Baselines preserved: - vitest 188/188 GREEN (most recent 187/188 with 04-CONTEXT #9/#10 webm-remux flake; passes in isolation; tolerated per Task 2 gate) - UAT 36/36 GREEN; FORBIDDEN_HOOK_STRINGS unchanged at 12 - Pre-checkpoint bundle gates 6/6 PASS at both checkpoint + re-checkpoint - All 4 ROADMAP SC CLOSED; D-P4-03 CLOSED Phase 4 progress: 6/8 -> 7/8 (Plan 04-07 NEXT). SUMMARY: .planning/phases/04-harden-clean-up-optional/04-06-SUMMARY.md Debug session archived: .planning/debug/resolved/04-06-dark-mode-mark-decouple.md
64 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, decisions, requirements-completed, duration, completed, tasks, files-modified
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | decisions | requirements-completed | duration | completed | tasks | files-modified | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 04-harden-clean-up-optional | 06 | ui |
|
|
|
|
|
|
|
~4 days end-to-end (3 planner passes + 2 checker passes + 4 task commits + 1 debug fix; sequential foreground with operator empirical pause in the middle) | 2026-05-26 | 4 | 11 |
Phase 04 Plan 06: Dark-Logo Contrast + Inline-SVG Injection + Cursor Visibility Verification + D-P4-03 Closure Summary
Dark-logo currentColor strategy + theme-independent --mks-mark-stroke brand-component token + DOMParser inline-SVG injection (no <img>, no innerHTML) + NEW A35 live-DOM host-side harness with 5-sub-check decouple-proof + cursor-visibility regression pin + 01-07-SUMMARY back-patch — closes D-P4-03 (both visual polish items) after 3 planner passes + 2 checker passes + 1 /gsd-debug fix cycle.
Performance
- Duration: ~4 days end-to-end (executor wall-clock ~6h across 4 task commits + 1 debug-fix commit; operator-empirical pause for the dark-mode aesthetic judgment + debug fix re-confirmation)
- Started: 2026-05-22 (executor spawn after re-plan-checker iter-2 PASSED at
48c7053) - Completed: 2026-05-26 (operator re-empirical confirmation "Confirmed fixed — close Plan 04-06")
- Tasks: 4 of 4 plan tasks complete (Tasks 1-3 autonomous + Task 4 operator-empirical UAT with TWEAK -> debug-fix -> CONFIRMED FIXED arc)
- Files modified: 11 (3 source files + 4 test files + 2 spec docs + STATE.md + ROADMAP.md) + 1 created script (operator-empirical screenshot harness) + 1 archived debug note
- Production source changes: 3 files (src/shared/brand/mokosh-mark.svg + src/welcome/welcome.ts + src/welcome/welcome.css + src/shared/tokens.css — the latter two are SCOPE EXPANSIONS authorized by the Task 4 TWEAK verdict)
Accomplishments
- Dark-logo currentColor strategy landed end-to-end (Task 2 commit
c416143): SVG stroke recolor (stroke=\"currentColor\") + welcome.ts?rawimport + DOMParser-based inline-SVG injection (no<img>, no innerHTML, no eval) + globals.d.ts*.svg?rawambient module declaration. Inline<svg>inherits parent CSScolorvia W3C SVG2 §13.3 cascade;stroke=\"currentColor\"resolves to that inherited color. MV3 CSP discipline preserved (T-04-06-01 mitigation). - Theme decoupling via
--mks-mark-strokebrand-component token (debug-fix commita8bcc17): introduced a NEW brand-component token--mks-mark-stroke = var(--mks-linen-50)in the universal:rootblock of src/shared/tokens.css. CRUCIALLY this token is NOT overridden in.dark, [data-theme=\"dark\"]— stays linen-50 on every surface. Rewired.welcome-hero__mark { color: var(--mks-mark-stroke); }at src/welcome/welcome.css line 72. Both light and dark themes now resolvecomputedStroketorgb(250, 247, 241)(linen-50) — crisp linen-on-madder mark in both themes. The SVG remains untouched. - NEW host-side A35 harness assertion (Task 3 commit
3f8e31a+ debug-fix commita8bcc17strengthening): opens welcome.html as a real Puppeteer tab; letspopulateMark()run at DOMContentLoaded; reads the LIVE injected.welcome-hero__mark svgelement +getComputedStyle().stroke. 5 CheckRecords: A35.1 svg present; A35.2 stroke="currentColor"; A35.3 getComputedStyle().stroke resolved non-default; A35.4 no<img>in slot; A35.5 (NEW from debug) light.computedStroke === dark.computedStroke === 'rgb(250, 247, 241)' (the decouple-proof). welcomePage.close() in finally block; UAT 36/36 GREEN. - Honest A17.8 narrowing (Task 3 commit
3f8e31a): replaced thedata:image/svg+xmldata-URL grep with a raw-source grep (stroke=\"currentColor\"+viewBox=\"0 0 32 32\"in jsText). A17.8 is now a SOURCE-BUNDLING check only — the explanatory comment block explicitly disavows live-DOM coverage and points to A35 for runtime behavior proof. - Source-contract unit tests (Task 1 commit
f0b88d4): tests/welcome/inline-svg.test.ts (3 tests; node-env file-read + string-assert; NO DOM library; NO@vitest-environment jsdom). Tests pin source TEXT: SVG recolor + welcome.ts ?raw/DOMParser/replaceChildren/no-innerHTML + globals.d.ts ambient decl. 3 RED today -> 3 GREEN after Task 2. - Cursor-visibility regression pin (Task 1 commit
f0b88d4): tests/build/cursor-visibility.test.ts (1 test; node-env file-grep) regression-pins the literalcursor: 'always'at src/offscreen/recorder.ts:285 (already shipped opportunistically by Plan 01-09). 1 GREEN-on-arrival. - 01-07-SUMMARY back-patch (Task 3 commit
3f8e31a): 5 stale 'deferred to Phase 5' framing lines (22, 47, 82, 135, 205) flipped to 'shipped opportunistically Plan 01-09 at recorder.ts:285; verified Phase 4 Plan 04-06'. 4 historical commit-description lines (40, 89, 109, 110) LEFT unchanged. - deferred-items.md mis-diagnosis correction (Task 3 commit
3f8e31a): the prior 'strict-meta-json fails on a clean tree' entry (commit6a989e8) rewritten to describe the real root cause — the 04-CONTEXT #9/#10 parallel-vitest ffprobe flake family. True clean baseline corrected to 184/184 GREEN. - Operator-empirical screenshot harness (commit
d66cbf6): scripts/04-06-welcome-hero-screenshots.mjs (194 lines) — reproducible Puppeteer script that captures/tmp/04-06-welcome-hero-{light,dark}.pngviaEmulation.setEmulatedMediaprefers-color-scheme: dark. Re-runnable for any future re-validation. - Operator-empirical Task 4 closure: TWEAK on 2026-05-26 (dark cascade flipped icon to ink-900 indigo on madder; lower contrast than light) -> /gsd-debug session with abstraction-error verdict -> fix commit
a8bcc17(decouple via --mks-mark-stroke token + A35.5 light+dark equality sub-check) -> operator re-empirical confirmation "Confirmed fixed — close Plan 04-06". - D-P4-03 (charter, locked) closed end-to-end: BOTH (a) cursor visibility AND (b) dark-surface logo contrast. (a) verified by tests/build/cursor-visibility.test.ts + 01-07-SUMMARY back-patch. (b) verified by 4-layer coverage: source-contract unit tests + A17.8 raw-source grep + A35 live-DOM (5 sub-checks including A35.5 decouple-proof) + operator-empirical screenshots.
Multi-Iteration Ceremony History
Plan 04-06 is the most ceremony-heavy plan in Phase 4 — full provenance traceability:
| Commit | Date | Stage |
|---|---|---|
6a989e8 |
2026-05-22 | Mis-diagnosed strict-meta-json deferred-items entry (logged out-of-scope from initial executor BLOCK on false jsdom premise) |
b59bd24 |
2026-05-22 | Re-plan iter-1 — correct false jsdom premise + stale back-patch lines + baseline |
deb68df |
2026-05-22 | Re-plan-checker iter-1 — ITERATE-NEEDED (2 BLOCKER: fictitious A17.8 delegation + wrong vitest baseline) |
f3baa3a |
2026-05-22 | Re-plan iter-2 — real inline-SVG coverage (NEW A35) + corrected 184/184 baseline |
48c7053 |
2026-05-22 | Re-plan-checker iter-2 — PASSED (0 BLOCKER + 0 WARNING + 3 cosmetic-advisories ADV-2A/B/C) |
f0b88d4 |
2026-05-26 | Task 1 — Wave 0 RED: inline-SVG source-contract + cursor-visibility regression pin |
c416143 |
2026-05-26 | Task 2 — Wave 1 GREEN: dark-logo currentColor + inline-SVG injection |
3f8e31a |
2026-05-26 | Task 3 — A35 live-DOM inline-SVG harness + A17.8 raw-source update + back-patch |
d66cbf6 |
2026-05-26 | Operator-empirical screenshot harness (Task 4 artifact) |
| (TWEAK) | 2026-05-26 | Task 4 operator-empirical UAT verdict: dark cascade flipped icon to ink-900 indigo |
a8bcc17 |
2026-05-26 | /gsd-debug fix: decouple via --mks-mark-stroke token + A35.5 light+dark equality |
| (CONFIRM) | 2026-05-26 | Operator re-empirical: "Confirmed fixed — close Plan 04-06" |
| (THIS) | 2026-05-26 | Closure commit (SUMMARY + STATE.md + ROADMAP.md + debug archive) |
Provenance lesson: when a checkpoint is operator-empirical, the planner should either (a) front-load the brand-component token into the plan, or (b) accept that a /gsd-debug fix cycle is part of the plan budget. Plan 04-06's checker iter-2 PASSED verdict was sound on the technical contract (the SVG/welcome.ts/globals.d.ts/A35 strategy was correct); the abstraction-error vector (semantic --mks-fg-inverse vs brand-component --mks-mark-stroke) only surfaced once the operator saw the dark theme. This is the inherent cost of operator-empirical aesthetics judgments — and is exactly why D-P4-03 was scoped as autonomous: false.
Task Commits
Each plan task was committed atomically with normal git commits + pre-commit hooks (sequential foreground mode; in-line with Plans 04-01 through 04-05 + 04-08 protocol):
- Task 1 — Wave 0 RED — inline-SVG source-contract + cursor-visibility regression pin —
f0b88d4(test). 2 files created: tests/welcome/inline-svg.test.ts (3 it() blocks; 70 lines); tests/build/cursor-visibility.test.ts (1 it() block; 45 lines). 3 RED + 1 GREEN-on-arrival. - Task 2 — Wave 1 GREEN — SVG stroke recolor + welcome.ts ?raw import + populateMark inline injection + globals.d.ts ambient decl —
c416143(feat). 3 files modified: src/shared/brand/mokosh-mark.svg (+1/-1); src/welcome/welcome.ts (+90/-25); globals.d.ts (+21). 3 source-contract RED flipped to 3 GREEN; vitest 184 -> 188 (full suite 187/188 with the 04-CONTEXT #9/#10 webm-remux flake; isolation 5/5 GREEN — tolerated per Task 2 VITEST GATE LOGIC). - Task 3 — A17.8 raw-source update + NEW A35 live-DOM harness assertion + 01-07-SUMMARY back-patch + deferred-items correction —
3f8e31a(feat). 5 files modified: tests/uat/extension-page-harness.ts (A17.8 narrowed honestly); tests/uat/lib/harness-page-driver.ts (driveA35 +247 lines; before debug-fix strengthening: 4 sub-checks); tests/uat/harness.test.ts (3-site wiring +28); .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md (5 lines flipped); .planning/phases/04-harden-clean-up-optional/deferred-items.md (mis-diagnosis correction). UAT 35/35 GREEN (A35 added + 4 sub-checks all PASS pre-debug). - Task 4 artifact — Operator-empirical screenshot harness —
d66cbf6(chore). 1 file created: scripts/04-06-welcome-hero-screenshots.mjs (194 lines). Reproducible Puppeteer script for the operator-empirical checkpoint. - Task 4 operator-empirical UAT — first verdict — TWEAK (no commit; verdict only). Dark cascade flipped the icon to ink-900 indigo on madder — lower contrast than light. Routed via /gsd-debug per
feedback-gsd-ceremony-for-fixes.md. - Debug-fix — decouple welcome-hero mark stroke via --mks-mark-stroke —
a8bcc17(fix). 6 files modified: src/shared/tokens.css (NEW --mks-mark-stroke token in :root; +15); src/welcome/welcome.css (rewire to new token; +11); src/welcome/welcome.ts (comment-only); tests/welcome/inline-svg.test.ts (comment-only); tests/uat/lib/harness-page-driver.ts (A35 strengthening + a35Probe helper extraction + A35.5 light+dark equality decouple-proof); tests/uat/harness.test.ts (comment-only). UAT 36/36 GREEN (A35 now has 5 sub-checks all PASS). - Task 4 operator-empirical UAT — re-verdict — CONFIRMED FIXED (no commit; verdict only). Operator response 2026-05-26: "Confirmed fixed — close Plan 04-06".
Plan metadata commit: to be committed atomically with this SUMMARY + STATE.md + ROADMAP.md + the archived debug session as docs(04-06): complete dark-logo contrast + cursor-visibility verification + D-P4-03 closure (4 tasks; UAT 36/36 GREEN; operator re-confirmed 2026-05-26).
Files Created/Modified
Created
tests/welcome/inline-svg.test.ts— CREATED (70 lines). 3 it() blocks indescribe('UI-SPEC dark-logo currentColor strategy — source-level contract', ...). Node-env file-read + string-assert pattern (precedent: tests/i18n/manifest-i18n.test.ts). Tests: A (mokosh-mark.svg recolor: containsstroke="currentColor"+viewBox="0 0 32 32"; does NOT containstroke="#181b2a"); B (welcome.ts ?raw + DOMParser + replaceChildren; does NOT contain?urlorinnerHTML); C (globals.d.ts ambient*.svg?rawdecl). NO DOM library import; NO@vitest-environment jsdomdirective (verified by grep -E "^import .*jsdom|@vitest-environment jsdom" returns 0).tests/build/cursor-visibility.test.ts— CREATED (45 lines). 1 it() block; node-env file-grep regression pin forcursor: 'always'literal at src/offscreen/recorder.ts:285 (shipped opportunistically by Plan 01-09).scripts/04-06-welcome-hero-screenshots.mjs— CREATED (194 lines). Puppeteer script; loads dist/ as unpacked extension; openschrome-extension://${extensionId}/src/welcome/welcome.html; captures/tmp/04-06-welcome-hero-light.png+/tmp/04-06-welcome-hero-dark.png(dark viaEmulation.setEmulatedMediaprefers-color-scheme: dark); prints diagnosticgetComputedStyle().strokefor each theme..planning/phases/04-harden-clean-up-optional/04-06-SUMMARY.md— CREATED (this file).
Modified
src/shared/brand/mokosh-mark.svg— MODIFIED. Net +1 / -1. Root<svg>(line 2)stroke="#181b2a"->stroke="currentColor". 12<line>+ 1<rect>children inherit stroke from the root and are UNCHANGED.src/welcome/welcome.ts— MODIFIED. Net +90 / -25. Line 46 import flipped from?url/markUrlto?raw/markSvg. populateMark body (lines 159-179) rewritten:<img>-> DOMParser + replaceChildren inline-SVG. NEVER uses innerHTML. Preserves function signature, slots query, altText resolution, filter-pipeline form, empty-slot logger.warn fallback. Docstring + comment block updated to describe inline-<svg>strategy.globals.d.ts— MODIFIED. Net +21. Appendeddeclare module '*.svg?raw' { const raw: string; export default raw; }block after the existing*.svg?urlblock. Explanatory comment per project docstring convention.src/shared/tokens.css— MODIFIED. Net +15. Added--mks-mark-stroke: var(--mks-linen-50);in the universal:rootblock (line ~131), adjacent to--mks-fg-inverse. NOT added to.dark, [data-theme="dark"]block. Comment cites the abstraction-error rationale + .planning/debug/04-06-dark-mode-mark-decouple.md. SCOPE EXPANSION (authorized by Task 4 TWEAK verdict).src/welcome/welcome.css— MODIFIED. Net +11. Line 72.welcome-hero__mark { color: var(--mks-fg-inverse); }->color: var(--mks-mark-stroke);. Comment block updated to cite the cascade chain. SCOPE EXPANSION (authorized by Task 4 TWEAK verdict).tests/uat/extension-page-harness.ts— MODIFIED. Net +25 / -45. A17.8 sub-check (lines ~2249-2298) narrowed honestly: replacedhasInlineDataUrl+ svgFileUrl regex with raw-source grep (stroke="currentColor"+viewBox="0 0 32 32"). Comment block disavows live-DOM coverage and points to A35.tests/uat/lib/harness-page-driver.ts— MODIFIED. Net +247 (Task 3 first land) + strengthening (debug-fix; A35.5 sub-check + a35Probe helper extraction). AppendeddriveA35host-side driver afterdriveA34. Opens welcome.html viabrowser.newPage()+page.goto(...)+waitForSelector('.welcome-hero__mark svg', ...). Extracteda35Probe(welcomePage, dark)helper togglesdata-theme="dark"on documentElement + reads live DOM. 5 CheckRecords: A35.1-A35.4 (live-DOM injection + currentColor cascade); A35.5 (light+dark equality decouple-proof). welcomePage.close() in finally; try/catch + result.error mirror driveA33/A34.tests/uat/harness.test.ts— MODIFIED. Net +28. 3-site lockstep wiring: import block addsdriveA35,; wrapped-driver block addsdriveA35Wrappedclosure (capturinghandles.browser+handles.extensionId); drivers-array push appends{ name: 'A35', drive: driveA35Wrapped }as the LAST entry. Architecture banner string at line 283 LEFT unchanged (cosmetic-advisory ADV-2A; auto-count viatotal = drivers.length + 1carries actual count)..planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md— MODIFIED. Net +10 / -10. 5 stale 'deferred to Phase 5' framing lines (22, 47, 82, 135, 205) flipped to 'shipped opportunistically Plan 01-09 at recorder.ts:285; verified Phase 4 Plan 04-06'. 4 historical commit-description lines (40, 89, 109, 110) LEFT unchanged. After the flips the SUMMARY narrative is internally consistent..planning/phases/04-harden-clean-up-optional/deferred-items.md— MODIFIED. Net +2 / -2. The prior 'strict-meta-json fails on a clean tree' entry rewritten to describe the real 04-CONTEXT #9/#10 parallel-vitest ffprobe flake family + correct the baseline to 184/184 GREEN..planning/ROADMAP.md— MODIFIED. Plan 04-06 row flipped[ ]->[x]with closure annotation. Phase 4 progress table cell updated from6/8 In Progress (Plan 04-05 closed ROADMAP SC #2)to7/8 In Progress (Plan 04-06 closed D-P4-03)..planning/STATE.md— MODIFIED. Current Position advanced. Stopped At updated. Last activity 2026-05-26. Plan 04-06 closure section added. Decisions appended. Performance metric row added. Progress 90% -> 93% (28/31 -> 29/31).
Archived
.planning/debug/04-06-dark-mode-mark-decouple.md->.planning/debug/resolved/04-06-dark-mode-mark-decouple.md— debug session moved at plan closure (resolved end-to-end via fix commita8bcc17+ operator re-empirical 2026-05-26).
Decisions Made
See key-decisions in frontmatter for the canonical list (10 decisions). Highlights:
- Multi-iteration ceremony was a necessary cost — 3 planner passes + 2 checker passes + 1 debug fix cycle. The /gsd-debug fix surfaced the brand-component vs semantic token abstraction error that the planner-checker couldn't catch (requires operator visual judgment). Lesson encoded.
- Brand-component token vs semantic token —
--mks-fg-inverseis for theme-flipping text foreground;--mks-mark-strokeis for theme-independent brand stroke. Pattern established and documented. - A35.5 decouple-proof sub-check — light.computedStroke === dark.computedStroke === 'rgb(250, 247, 241)'. Regression-proof for any future re-coupling.
- Honest A17.8 narrowing — SOURCE-BUNDLING only; live-DOM coverage lives in A35.
- Behavior-based vitest gate — hard-codes no test filename; isolation-passing single flake is tolerated; reproducible failure or 2+ failures FAIL the gate.
- 01-07-SUMMARY back-patch — 5 stale framing lines flipped; 4 historical commit-description lines left.
- Cosmetic advisories ADV-2A/B/C — banner string left for Plan 04-07 to opportunistically refresh; SKIP_PROD_REBUILD=0 prose accuracy noted; A35-appended-LAST safety documented in SUMMARY.
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 — Blocking] Welcome screenshot script created from scratch (Task 4 artifact)
- Found during: Task 4 (Operator empirical UAT — pre-checkpoint screenshot production)
- Issue: The plan instructed "Load dist/ as an unpacked extension and open the welcome page in a Puppeteer-driven Chrome. Capture LIGHT-surface screenshot. Capture DARK-surface screenshot." but did not provide a dedicated script — the operator-empirical checkpoint required a reproducible artifact.
- Fix: Created scripts/04-06-welcome-hero-screenshots.mjs (194 lines) — Puppeteer; loads dist/ as unpacked extension; opens welcome.html; captures /tmp/04-06-welcome-hero-{light,dark}.png via
Emulation.setEmulatedMediaprefers-color-scheme: dark; prints diagnosticgetComputedStyle().strokefor each theme. Reproducible for any future re-shoot. - Files modified: scripts/04-06-welcome-hero-screenshots.mjs (CREATED)
- Verification: Both screenshots produced + visually inspected by operator; diagnostic output confirms
computedStrokeresolved for each theme. - Committed in:
d66cbf6(separate chore commit; Task 4 artifact)
2. [Rule 1 — Bug] Dark-cascade abstraction error: --mks-fg-inverse couples stroke to theme
- Found during: Task 4 operator-empirical UAT (TWEAK verdict 2026-05-26)
- Issue:
.welcome-hero__mark { color: var(--mks-fg-inverse); }is theme-coupled (linen-50 in light; ink-900 in dark per tokens.css :root vs .dark blocks). The mark sits on a theme-INDEPENDENT madder-600 circle, so a theme-coupled stroke produced muddy ink-on-madder in dark mode. Wrong abstraction (semantic token applied to a non-flipping surface). - Fix: Introduced a NEW brand-component token
--mks-mark-stroke = var(--mks-linen-50)in the universal :root block of src/shared/tokens.css. CRUCIALLY NOT overridden in.dark, [data-theme="dark"]— stays linen-50 on every surface. Rewired.welcome-hero__mark { color: var(--mks-mark-stroke); }at src/welcome/welcome.css line 72. SVG remains untouched (stroke="currentColor" cascade plumbing identical; only the wrapper's color source changed). Routed via /gsd-debug perfeedback-gsd-ceremony-for-fixes.md— no inline hot-edit. - Files modified: src/shared/tokens.css (SCOPE EXPANSION; +15); src/welcome/welcome.css (SCOPE EXPANSION; +11); src/welcome/welcome.ts (comment-only); tests/welcome/inline-svg.test.ts (comment-only); tests/uat/lib/harness-page-driver.ts (A35 strengthening: a35Probe helper + A35.5 light+dark equality); tests/uat/harness.test.ts (comment-only); .planning/debug/04-06-dark-mode-mark-decouple.md (NEW debug note; later archived to resolved/).
- Verification: Re-shot screenshots show identical crisp linen-on-madder in both themes. A35.5 live-DOM probe: light.computedStroke === dark.computedStroke === "rgb(250, 247, 241)". UAT 36/36 GREEN. Operator re-empirical 2026-05-26: "Confirmed fixed — close Plan 04-06".
- Committed in:
a8bcc17(debug-fix commit; ceremony-routed per saved memory)
3. [Rule 1 — Bug] A35 sub-check strengthening: 4 -> 5 sub-checks (A35.5 decouple-proof added)
- Found during: /gsd-debug session for the dark-cascade abstraction error
- Issue: The original A35 (Task 3 first land) had 4 sub-checks (A35.1-A35.4) covering live-DOM injection + currentColor cascade resolution in LIGHT theme only. A future regression that re-couples the stroke to a theme-flipping token would still pass A35.1-A35.4 (the inline
<svg>is injected; the cascade resolves; just to the wrong color in dark). - Fix: Extracted the live-DOM probe into a helper
a35Probe(welcomePage, dark)that togglesdata-theme="dark"on documentElement (+ requestAnimationFrame wait for CSS recompute). Probe BOTH light + dark. Add A35.5: assertlight.computedStroke === dark.computedStroke === "rgb(250, 247, 241)"(linen-50). This is the canonical decouple-proof. - Files modified: tests/uat/lib/harness-page-driver.ts (folded into debug-fix commit
a8bcc17) - Verification: UAT 36/36 GREEN; A35.5 diagnostics show light + dark both rgb(250, 247, 241).
- Committed in:
a8bcc17(folded into the debug-fix commit; ceremony-routed)
Total deviations: 3 auto-fixed (1 blocking [screenshot script]; 2 bugs [abstraction error + A35 strengthening]). Impact on plan: All 3 deviations were essential for closure. The screenshot script was the operator-empirical artifact; the abstraction-error fix closed the TWEAK verdict; the A35 strengthening encodes the regression-proof. No scope creep — the SCOPE EXPANSIONS (tokens.css + welcome.css edits) were authorized by the Task 4 TWEAK verdict per the operator-empirical resume-signal contract.
Issues Encountered
Operator-empirical TWEAK verdict — abstraction error in dark cascade
Task 4's first operator-empirical verdict (2026-05-26) was TWEAK NEEDED. The light screenshot was crisp linen-on-madder (Plan 01-10 baseline preserved); the dark screenshot showed the mark stroke flipped to ink-900 (deep indigo) on the madder-600 wrapper — visually muddy, low contrast. Root cause: .welcome-hero__mark { color: var(--mks-fg-inverse); } couples the cascade to a semantic token that flips with theme. The mark wrapper is theme-independent (madder-600 in both themes), so the coupling was the wrong abstraction.
Resolution (per saved memory feedback-gsd-ceremony-for-fixes.md): routed via /gsd-debug rather than inline hot-edit. Debug session at .planning/debug/04-06-dark-mode-mark-decouple.md established root cause + fix; fix commit a8bcc17 decoupled via the NEW brand-component token --mks-mark-stroke. A35 strengthened with the A35.5 decouple-proof sub-check. Re-shot screenshots + UAT re-run produced identical crisp linen-on-madder in both themes. Operator re-empirical 2026-05-26: "Confirmed fixed — close Plan 04-06".
Parallel-vitest #9/#10 flake fired during Task 2 GREEN gate
Task 2's full vitest run produced 187 passed / 1 failed (188 total). The failing test was tests/background/webm-remux.test.ts > 'ffprobe -count_frames reports between 905 and 912 frames' (Error: Test timed out in 5000ms). Per the Task 2 VITEST GATE LOGIC behavior-based rule: re-run in isolation (npm test -- tests/background/webm-remux.test.ts --run) -> 5/5 GREEN. Tolerated as the known 04-CONTEXT #9/#10 parallel-vitest ffprobe-timeout flake family. NOT a Plan 04-06 regression (Plan 04-06 modifies no offscreen recorder code). Documented + tolerated per gate logic.
Pre-Checkpoint Bundle Gates (6/6 PASS)
Per saved memory feedback-pre-checkpoint-bundle-gates.md, all 6 gates run on npm run build production output before each operator-empirical checkpoint surface:
| Gate | Check | Result |
|---|---|---|
| 1 | npm run build exit 0; glob-existence ls dist/assets/index.ts-*.js | PASS |
| 2 | SW CSP-safety: new Function/eval in dist/assets/index.ts-*.js |
0 / 0 — PASS (Plan 04-02 effect preserved) |
| 3 | SW Node-globals: Buffer./process./require( in SW chunk |
1 (pre-existing JSZip polyfill typeof ArrayBuffer<"u" feature-detection) — PASS (baseline-identical to Plan 04-05 boundary; Plan 04-06 modifies no SW chunk source) |
| 4 | DOM-globals: window./document. in SW chunk |
third-party typeof window<"u"/typeof document<"u" guarded feature-detection — PASS (baseline-identical) |
| 5 | Tier-1 SW-bundle-import vitest gate | GREEN |
| 6 | Tier-1 FORBIDDEN_HOOK_STRINGS at 12 + Tier-2 production filename-leak gate (0 hits) | GREEN — PASS |
6/6 PASS at both the first Task 4 checkpoint AND the post-debug re-checkpoint. Tier-1 FORBIDDEN_HOOK_STRINGS unchanged at 12 (Plan 04-06 adds no __MOKOSH_UAT__-gated symbols; DOMParser is a standard web platform API; ?raw is a normal production Vite import; *.svg?raw ambient decl is type-only at build time). SW chunk is byte-identical at the Plan 04-05 boundary (Plan 04-06 modifies only src/welcome/* + src/shared/brand/mokosh-mark.svg + src/shared/tokens.css :root token + tests/* + globals.d.ts — none of these affect the SW chunk shape).
Test Baselines
- vitest: 188/188 GREEN on a fully-clean run (the +4 from Plan 04-06: 3 inline-svg.test.ts + 1 cursor-visibility.test.ts). Most recent full-suite run post-debug-fix: 187/188 with the 04-CONTEXT #9/#10 webm-remux flake fired (passes 5/5 in isolation; tolerated per Task 2 VITEST GATE LOGIC). Baseline contract held: 184 + 4 = 188.
- UAT harness: 36/36 GREEN (skip-mode
SKIP_LONG_UAT=1 npm run test:uat). Increments: 33 (post-04-03) -> 34 (04-08 A33) -> 35 (04-05 A34) -> 36 (04-06 A35). A35 itself contains 5 sub-checks all PASS (A35.1-A35.5). - Tier-1 FORBIDDEN_HOOK_STRINGS: 12 (unchanged from Plan 04-02 baseline; verified at
tests/background/no-test-hooks-in-prod-bundle.test.ts:108-126). - Tier-2 production-bundle filename-leak gate:
synthetic-display-source= 0 hits in dist/ (Plan 04-08 invariant preserved). - Pre-checkpoint bundle gates: 6/6 PASS (verified at both Task 4 checkpoint AND post-debug re-checkpoint).
tsc --noEmit: exits 0.npm run build: exits 0.npm run build:test: exits 0.
D-P4-03 Closure Evidence
D-P4-03 (charter, locked, 04-CONTEXT.md): BOTH (a) cursor visibility AND (b) dark-surface logo contrast.
(a) Cursor visibility — CLOSED
- Already shipped:
cursor: 'always'at src/offscreen/recorder.ts:285 (Plan 01-09 opportunistic co-land in commita2dfc8c2026-05-19; the original Phase 5 deferral framing in 01-07-SUMMARY was false — the constraint was shipped concurrently with the no-active-tab cleanup). - Verified by: tests/build/cursor-visibility.test.ts — node-env file-grep regression pin for the literal
cursor: 'always'. 1 GREEN-on-arrival. Any future regression that removes the literal will fail the gate. - 01-07-SUMMARY back-patch: 5 stale 'deferred to Phase 5' framing lines (22, 47, 82, 135, 205) flipped; 4 historical commit-description lines (40, 89, 109, 110) LEFT.
(b) Dark-surface logo contrast — CLOSED
4-layer coverage:
- Source-contract unit tests (Task 1): tests/welcome/inline-svg.test.ts (3 tests; node-env; file-read + string-assert). Pins SVG recolor + welcome.ts ?raw/DOMParser/replaceChildren/no-innerHTML + globals.d.ts ambient decl. RED -> GREEN after Task 2.
- A17.8 raw-source bundling check (Task 3): tests/uat/extension-page-harness.ts. Asserts welcome JS chunk contains the raw SVG source signature (stroke="currentColor" + viewBox="0 0 32 32"). Honestly narrowed to SOURCE-BUNDLING only; explanatory comment disavows live-DOM coverage and points to A35.
- A35 live-DOM host-side harness assertion (Task 3 + debug-fix strengthening): tests/uat/lib/harness-page-driver.ts driveA35. Opens welcome.html as a real Puppeteer tab; lets populateMark() run at DOMContentLoaded; reads the LIVE injected
.welcome-hero__mark svgelement + getComputedStyle().stroke. 5 sub-checks: A35.1 svg present; A35.2 stroke="currentColor"; A35.3 getComputedStyle().stroke resolved non-default (linen-50); A35.4 no<img>in slot; A35.5 light.computedStroke === dark.computedStroke === "rgb(250, 247, 241)" (linen-50 decouple-proof, NEW from debug-fix). UAT 36/36 GREEN — A35 is the genuine automated proof of the runtime injection + cascade + theme decoupling. - Operator-empirical Puppeteer screenshots (Task 4): scripts/04-06-welcome-hero-screenshots.mjs produces /tmp/04-06-welcome-hero-{light,dark}.png via prefers-color-scheme: dark emulation. First operator verdict: TWEAK (dark cascade flipped to ink-900). /gsd-debug session resolved via --mks-mark-stroke brand-component token. Re-shot screenshots + operator re-empirical 2026-05-26: "Confirmed fixed — close Plan 04-06".
TDD Gate Compliance
Plan 04-06 was a type: execute plan with tdd="true" on Tasks 1-2 (the source-contract + implementation pair). Gate sequence verified in git log:
- ✅ RED gate (test commit):
f0b88d4(test(04-06): Wave 0 — inline-SVG source-contract RED + cursor-visibility regression pin). 3 RED + 1 GREEN-on-arrival at commit time; isolatednpm test -- tests/welcome/ --runshowed 3/3 FAILED before Task 2 GREEN. - ✅ GREEN gate (feat commit):
c416143(feat(04-06): Wave 1 GREEN — dark-logo currentColor strategy + inline-SVG injection) after RED. 3 RED flipped to 3 GREEN; cursor-visibility test preserved 1 GREEN. - REFACTOR gate: N/A — no separate refactor commit needed; Task 2 implementation was minimal-to-pass.
TDD discipline preserved end-to-end.
Charter Linkage
Plan 04-06 has no REQ-* requirements (Phase 4 is optional hardening; all v1 REQs covered by Phases 1-3 per ROADMAP). Charter linkage is via the charter-d-p4-03 tag in frontmatter. D-P4-03 = visual polish (cursor visibility + dark-surface logo contrast) — both items closed by Plan 04-06.
Cross-Plan Provenance Note for Plan 04-07
Plan 04-07 (Phase 4 closure aggregator) is now the ONLY remaining Phase 4 plan. Inputs for 04-07:
- D-P4-03 closure: documented here (cursor + dark-logo both closed; A35 + cursor-visibility unit test pin live).
- D-P4-04 (alpha tester integration): honored out-of-band (per ROADMAP entry).
- ROADMAP success criteria status: SC #1 CLOSED (04-08); SC #2 CLOSED (04-05); SC #3 CLOSED (04-02); SC #4 CLOSED (04-02). All 4 ROADMAP success criteria are now CLOSED.
- Cosmetic-advisory ADV-2A (architecture banner string at harness.test.ts:283 missing A33/A34/A35): non-blocking; Plan 04-07 may opportunistically refresh the banner if it touches that file.
- 04-CONTEXT #9/#10 parallel-vitest flake family: documented in deferred-items.md (corrected this plan); routed for /gsd-debug or future-plan worker-isolation strategy (
poolOptions.threads.singleThread:trueortestTimeoutbump) if it becomes a blocker for Phase 4 closure.
Plan 04-07 should produce the Phase 4 aggregate verification (VERIFICATION.md gsd-verifier audit) + REQUIREMENTS/ROADMAP/STATE marker flips + v1 milestone close prep.
Saved-Memory Compliance Audit
- ✅
feedback-no-unilateral-scope-reduction.md— full plan scope executed (4 tasks; 11 files modified; SCOPE EXPANSIONS authorized by operator TWEAK verdict; no unilateral truncation). - ✅
feedback-trust-harness-over-manual-uat.md— operator only judged from screenshots (Task 4 artifact via scripts/04-06-welcome-hero-screenshots.mjs); A35.5 light+dark equality decouple-proof landed in the automated harness (not just the screenshot artifact). - ✅
feedback-gsd-ceremony-for-fixes.md— the dark-cascade abstraction error was routed via /gsd-debug (debug session at .planning/debug/04-06-dark-mode-mark-decouple.md; fix commita8bcc17is a separate atomic fix-commit, NOT a hot-edit folded into a task commit). Ceremony honored. - ✅
feedback-pre-checkpoint-bundle-gates.md— 6/6 bundle gates run + verified PASS at BOTH the first Task 4 checkpoint AND the post-debug re-checkpoint. Documented above.
Threat Surface Scan
Plan 04-06 introduces no new security-relevant surface:
- Inline-SVG injection: safe because the input is a Vite-bundled compile-time literal (no runtime untrusted input). DOMParser parse + replaceChildren preserves MV3 CSP discipline (T-04-06-01 mitigation; no innerHTML, no eval). The
?rawimport is processed at build time; the SVG source string is statically known. - NEW A35 harness tab: test-only; always closed in finally block (no production surface, no leaked tab); A35 is appended LAST in the drivers array (no pollution-of-future-drivers risk — there are no future drivers); A35 does NOT mutate chrome.storage (opening welcome.html does NOT trigger openWelcomeIfFirstInstall — that runs only on chrome.runtime.onInstalled). Verified safe at design time (cosmetic-advisory ADV-2C).
--mks-mark-strokebrand-component token: purely additive CSS custom property in :root; no override anywhere; no consumer surface change.
No ## Threat Flags section needed.
Self-Check: PASSED
Acceptance criterion by criterion (cross-referenced against the success_criteria list in the closure-work prompt):
- ✅ 04-06-SUMMARY.md written per canonical template + multi-iteration ceremony history: this file. Frontmatter complete (phase + plan + subsystem + tags + requires + provides + affects + tech-stack + key-files + key-decisions + metrics). Multi-Iteration Ceremony History section provides the 13-row provenance table.
- ✅ All 4 tasks documented: Tasks 1-3 autonomous (commits
f0b88d4+c416143+3f8e31a) + Task 4 operator empirical with TWEAK → fixa8bcc17→ CONFIRMED arc. - ✅ All key deliverables enumerated: currentColor + --mks-mark-stroke decoupling + A35 (5 sub-checks) + back-patch + screenshot script + 01-07-SUMMARY 5-line flip + deferred-items correction.
- ✅ 3 re-plan-checker iter-2 cosmetic advisories documented: ADV-2A (banner: LEAVE per decision), ADV-2B (SKIP_PROD_REBUILD=0 rationale corrected in SUMMARY), ADV-2C (A35-appended-LAST safety noted in Threat Surface Scan).
- ✅ Pre-checkpoint bundle gates 6/6 PASS reported: dedicated section above with table.
- ✅ Test baselines documented: vitest 188/188 GREEN (most recent 187/188 with tolerated webm-remux flake); UAT 36/36 GREEN; FORBIDDEN_HOOK_STRINGS 12.
- ✅ D-P4-03 closure evidence: dedicated section with (a) cursor visibility + (b) dark-surface logo contrast 4-layer coverage.
- ✅ Self-Check section: this section, criterion by criterion.
Created files exist:
- ✅ tests/welcome/inline-svg.test.ts — FOUND
- ✅ tests/build/cursor-visibility.test.ts — FOUND
- ✅ scripts/04-06-welcome-hero-screenshots.mjs — FOUND
- ✅ .planning/phases/04-harden-clean-up-optional/04-06-SUMMARY.md — FOUND (this file)
Commits exist (verified via git log --oneline):
- ✅
f0b88d4(Task 1 RED) — FOUND - ✅
c416143(Task 2 GREEN) — FOUND - ✅
3f8e31a(Task 3 A35 + back-patch) — FOUND - ✅
d66cbf6(Task 4 screenshot harness) — FOUND - ✅
a8bcc17(debug-fix --mks-mark-stroke) — FOUND
No missing items. Plan 04-06 closes cleanly.
Phase: 04-harden-clean-up-optional Plan: 06 Completed: 2026-05-26