Files
Mark f319c7dc6e docs(01-12): summary — design integration landed (147/147 vitest, 21/21 UAT, brand-fit ack 2026-05-20)
Plan 01-12 closure SUMMARY landed at .planning/phases/01-stabilize-
video-pipeline/01-12-SUMMARY.md per the plan's <output> block + 01-13/
01-14 closure cadence. Mirrors the 01-13-SUMMARY frontmatter shape +
body sections (One-Liner / What Landed by Wave / Test Counts /
Deviations / Architectural Notes / Self-Check / Known Limitations /
Bridge to Phase 1 Closure).

Plan 01-12 design integration in one sentence: Lora self-hosted via
R2 designer substitution (Newsreader → Lora for Cyrillic coverage,
2026-05-19); src/shared/tokens.css canonical with 8 local @font-face
rules and zero remote URLs; 16 i18n keys across en + ru with parity;
branded Loom-mark icons replace Bug A placeholders; src/popup +
src/background migrated to chrome.i18n.getMessage with || <const>
fallback; UAT harness extended with A18-A22; pre-checkpoint bundle
gates established per feedback-pre-checkpoint-bundle-gates.md;
operator brand-fit ack received 2026-05-20 verbatim "all good".

Gate evidence (per Wave 7 pre-checkpoint 865d394 + this closure):
- vitest: 147/147 GREEN (re-verified on closure day; 26 test files)
- npm run test:uat: 21/21 GREEN (A0-A14 + A18-A22 + A23)
- npx tsc --noEmit: clean
- npm run build + npm run build:test: both clean
- MV3 CSP self-host: 0 googleapis / 0 https://fonts in dist/
- Tier-1 forbidden-strings: 13/13 GREEN (no new test-mode symbols)
- Operator brand-fit empirical ack 2026-05-20: "all good"

Closure linkage:
- Plan 01-12 functionally CLOSED (10/10 tasks; 7/7 waves)
- Plan 01-13 Task 9 (operator brand/design ack on loaded extension)
  functionally CLOSED via this checkpoint (same operator + same
  empirical surface coverage)
- Phase 1 design/brand contract CLOSED; only Plan 01-10 (welcome tab)
  remains as the last Phase 1 functional plan
- Phase 2 inherits tokens.css + chrome.i18n patterns + OFL self-host
  recipe + pre-checkpoint bundle gates as production conventions

Out-of-scope discovery (Wave 7 pre-checkpoint, logged for Phase 5
hardening, NOT a Plan 01-12 regression): setimmediate polyfill
`new Function` in SW chunk via vite-plugin-node-polyfills. Pre-existing
across Phase 1 history; logged at deferred-items.md. Suggested
follow-up: switch to a minimal Buffer shim or inline Buffer primitives
to drop the polyfill entirely.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 07:59:34 +02:00

413 lines
39 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 01-stabilize-video-pipeline
plan: 12
subsystem: design-integration + i18n + brand-assets
tags:
- design-integration
- brand
- tokens-css
- woff2-selfhost
- ofl-attribution
- svg-rasterization
- mv3-csp
- i18n
- locales
- chrome-i18n
- loom-palette
- D-01-mark
- D-02-welcome
- D-03-voice-sober
- D-04-palette
- D-05-typography
- D-06-icon-strategy
- D-07-extname-override
- D-08-tagline
- D-09-smoke-dev-only
- R2-lora-substitute
- Approach-B-harness-extension
- harness-A18-A22
- pre-checkpoint-bundle-gates
status: complete
requires:
- 01-09 (badge state machine + Bug A placeholder icons + 3-state SAVE/REC/ERR + toolbar onClicked direct flow)
- 01-13 (Approach B UAT harness — extension-internal-page driver + offscreen synthetic stream; harness-A18-A22 inherit the pattern)
- 01-14 (monitorTypeSurfaces:'include' picker narrowing; FORBIDDEN_HOOK_STRINGS baseline at 12 entries; A23 gate)
provides:
- "Lora self-hosted via OFL Cyreal foundry release (R2 designer reply 2026-05-19 substitutes Newsreader → Lora for Cyrillic coverage)"
- "src/shared/tokens.css canonical token system (single source of truth; 8 local @font-face rules; zero remote URLs)"
- "branded Loom-mark PNG icons (8-bit RGBA via rsvg-convert; replaces Bug A placeholders at 16-bit RGB)"
- "manifest.json migrated to __MSG_*__ + default_locale='en'; chrome.i18n picker resolves operator-facing surfaces"
- "_locales/{en,ru}/messages.json with 16 keys + en↔ru parity (default_locale fallback chain per RESEARCH Pitfall 4)"
- "src/popup + src/background migrated to chrome.i18n.getMessage with `|| <const>` fallback"
- "BADGE_REC_COLOR set to madder #b2543d (= --mks-madder-600 per D-04 loom palette; was material-green #00C853)"
- "UAT harness A18-A22 (font reachability + icon-distinct + manifest-i18n + Lora-resolved + welcome-tokens-conditional)"
- "pre-checkpoint bundle gates established per feedback-pre-checkpoint-bundle-gates.md (5 grep gates run automatically before operator brand-fit checkpoint)"
- "operator brand-fit ack 2026-05-20 (verbatim 'all good') — closes Plan 01-12 + Plan 01-13 Task 9 operator brand/design ack"
affects:
- manifest.json (name/description/default_locale/action.default_title → __MSG_*__)
- _locales/en/messages.json (new — 16 keys; D-07 extName override; D-08 tagline)
- _locales/ru/messages.json (new — 16 keys verbatim Russian per Brief §02)
- src/shared/fonts/ (new bundle — 8 WOFF2 + 2 LICENSE + README.md; ~236 KB total)
- src/shared/tokens.css (canonical — Google Fonts @import REMOVED + 8 local @font-face + .mks-word + Newsreader → Lora)
- src/shared/brand/ (mokosh-mark.svg + mokosh-lockup.svg copied from design-incoming)
- icons/icon{16,48,128}.png (Loom mark rasterized; overwrites Bug A placeholders)
- src/popup/index.html (button-text emptied; data-mks-key='popupInfoText' attribute)
- src/popup/index.ts (chrome.i18n.getMessage with `|| <const>` fallback at every operator-facing site)
- src/popup/style.css (@import tokens.css; ZERO hex literals; var(--mks-*) throughout)
- src/background/index.ts (BADGE_REC/OFF/ERROR_TITLE + onStartup/notifRecovery + setBadgeState migrated to chrome.i18n)
- vite.config.ts (gains __VITE_DEV__ define-token alongside __MOKOSH_UAT__)
- scripts/rasterize-icons.sh (new — one-off rsvg-convert recipe)
- scripts/subset-fonts.sh (new — one-off pyftsubset recipe)
- scripts/README.md (new — smoke isolation invariant + script index)
- tests/build/{tokens-adopted,fonts-present,icons-present,no-remote-fonts}.test.ts (new — 6 GREEN files at Wave 0 RED)
- tests/i18n/{manifest-i18n,locale-parity}.test.ts (new — i18n shape + en↔ru parity)
- tests/uat/extension-page-harness.html (gains <link rel=stylesheet href="../../src/shared/tokens.css">)
- tests/uat/extension-page-harness.ts (assertA18..A22 page-side methods)
- tests/uat/lib/harness-page-driver.ts (driveA18..A22 host-side wrappers)
- tests/uat/harness.test.ts (orchestrator extended with A18-A22; FORBIDDEN_HOOK_STRINGS UNCHANGED at 13 entries; comments updated)
tech-stack:
added:
- "Lora variable WOFF2 (OFL-1.1, Cyreal foundry; full Latin + Cyrillic basic)"
- "IBM Plex Sans 400/500/600/700 (OFL-1.1; UI body family)"
- "IBM Plex Mono 400/500 (OFL-1.1; diagnostic/timer family)"
- "Mokosh canonical token system (src/shared/tokens.css)"
- "_locales/{en,ru}/messages.json (chrome.i18n runtime)"
patterns:
- "Self-host fonts via Vite asset pipeline + @crxjs auto-WAR (relative url(./fonts/*.woff2) rebased at build to dist/assets/<hash>.woff2)"
- "MV3 CSP self-host invariant (style-src 'self' + font-src 'self'; zero remote URLs in dist/) verified by tests/build/no-remote-fonts.test.ts"
- "chrome.i18n.getMessage('<key>') || '<en-const-fallback>' pattern at every operator-facing copy site (graceful degradation if a key is missing in default_locale per RESEARCH Pitfall 4)"
- "__MSG_*__ runtime substitution for manifest:name + :description + :action.default_title (NOT supported in HTML body per RESEARCH Pitfall 3 — those use data-mks-key attributes + populateMksKeys() at popup boot)"
- "Static PNG icon artifacts (NOT regenerated at build time per RESEARCH §3 anti-pattern; scripts/rasterize-icons.sh is the one-off recipe)"
- "Pre-checkpoint bundle gates per feedback-pre-checkpoint-bundle-gates.md (SW CSP grep + Buffer.* grep + DOM-globals grep + manifest validation + en↔ru parity) run automatically before operator empirical checkpoint"
preserved:
- "Plan 01-13 + 01-14 Approach B harness architecture (page-side assertA* + host-side driveA* + orchestrator); A18-A22 follow it verbatim"
- "Plan 01-09 toolbar onClicked direct flow + 3-state badge state machine; only the COLOR + TITLE values migrated"
- "Plan 01-11 architectural learnings (NO `await import(...)` in SW; chrome.i18n.getMessage is synchronous — no dynamic imports required)"
- "Plan 01-14 monitorTypeSurfaces:'include' constraint + D-15 post-grant validation; recorder.ts unchanged by Plan 01-12"
key-files:
created:
- src/shared/fonts/Lora-VariableFont.woff2 (Wave 1; 49 KB)
- src/shared/fonts/Lora-Italic-VariableFont.woff2 (Wave 1; 53 KB)
- src/shared/fonts/IBMPlexSans-{Regular,Medium,SemiBold,Bold}.woff2 (Wave 1; ~24/25/25/23 KB)
- src/shared/fonts/IBMPlexMono-{Regular,Medium}.woff2 (Wave 1; ~15 KB each)
- src/shared/fonts/{LICENSE-Lora.txt,LICENSE-IBM-Plex.txt,README.md} (Wave 1; OFL attribution)
- src/shared/tokens.css (Wave 1; canonical token system)
- src/shared/brand/{mokosh-mark.svg,mokosh-lockup.svg} (Wave 1; copied from design-incoming)
- icons/icon{16,48,128}.png (Wave 2; rasterized Loom mark; replaces Bug A placeholders)
- scripts/{rasterize-icons.sh,subset-fonts.sh,README.md} (Waves 1-2 + Wave 5)
- _locales/en/messages.json (Wave 3; 16 keys with D-07 + D-08)
- _locales/ru/messages.json (Wave 3; 16 keys verbatim Russian per Brief §02)
- tests/build/{tokens-adopted,fonts-present,icons-present,no-remote-fonts}.test.ts (Wave 0)
- tests/i18n/{manifest-i18n,locale-parity}.test.ts (Wave 0)
modified:
- manifest.json (Wave 3; name/description/default_locale/action.default_title → __MSG_*__)
- src/popup/index.html (Wave 4; button-text emptied; data-mks-key attributes)
- src/popup/index.ts (Wave 4; chrome.i18n.getMessage + populateMksKeys + `|| <const>` fallback)
- src/popup/style.css (Wave 4; @import tokens.css; ZERO hex literals)
- src/background/index.ts (Wave 4; BADGE titles + onStartup + notifRecovery via chrome.i18n; BADGE_REC_COLOR #b2543d)
- vite.config.ts (Wave 5; __VITE_DEV__ define alongside __MOKOSH_UAT__)
- tests/uat/extension-page-harness.html (Wave 6; <link rel=stylesheet> for tokens.css)
- tests/uat/extension-page-harness.ts (Wave 6; assertA18..A22)
- tests/uat/lib/harness-page-driver.ts (Wave 6; driveA18..A22)
- tests/uat/harness.test.ts (Wave 6; A18-A22 entries; FORBIDDEN_HOOK_STRINGS UNCHANGED at 13; orchestrator stdout)
deleted: []
decisions:
- "R2 designer substitution: --mks-font-display = 'Lora', 'Iowan Old Style', 'Times New Roman', serif (replaces handoff's Newsreader; Lora has full Cyrillic via Cyreal foundry whereas Newsreader has zero Cyrillic glyphs). Per brand-decisions-v1-followup-display-font.md + designer reply 2026-05-19."
- "Static PNG icon artifacts policy: icons/*.png are COMMITTED, NOT regenerated at build time. scripts/rasterize-icons.sh is the documented re-run recipe (anti-pattern per RESEARCH §3 — pre-build hooks make CI/CD fragile; static commit is the canonical path)."
- "`chrome.i18n.getMessage('<key>') || '<en-const>'` fallback pattern uniformly at every call site. Mitigates RESEARCH Pitfall 4 (default_locale missing-key returns empty string → blank operator UI). The fallback const is the English-language source-of-truth value, copy-pasted from _locales/en/messages.json."
- "16 i18n keys land in BOTH _locales/{en,ru}/messages.json (plan baseline was 12 keys + 4 Wave-4 deltas anticipated; pushed all 16 into Wave 3 to avoid a Wave-4 locale-parity flap). Keys: extName, extDesc, tooltipOff, tooltipRecPrefix, tooltipErr, popupSavePrompt, popupSaveCta, popupSaveDone, popupSaving, popupSaveDoneShort, popupEmptyState, popupInfoText, notifStartup, notifRecovery, welcomeHeroRu, welcomeHeroEn."
- "EN extName = 'Mokosh — Session Capture' (D-07 user override of brand-decisions-v1.md option A 'Mokosh Recorder'). EN extDesc = 'Thirty seconds ago, always at hand.' (D-08 tagline, per brand-decisions-v1.md final wording; RESEARCH Open Questions A4 resolved to brand-decisions-v1.md over Brief §02's '...within reach.' variant on age-precedence)."
- "Welcome page (src/welcome/*) NOT created by Plan 01-12 — Plan 01-10 has not landed at execute-plan time (verified absent). When Plan 01-10 lands, its executor uses src/shared/tokens.css directly (no placeholder welcome-tokens.css needed). A22 skip-gates on welcome.html unreachable."
- "BADGE_REC_COLOR = '#b2543d' (= --mks-madder-600 per D-04 loom palette; was material-green #00C853). BADGE_OFF_COLOR + BADGE_ERROR_COLOR remain as plan-discretion engineering choices unless palette dictates."
- "`.mks-word` class added at end of src/shared/tokens.css with the {font-family, font-size, font-weight, fill, letter-spacing} declarations from mokosh-lockup.svg line 21 (engineering working-definition; designer overridable in a future iteration)."
- "Pre-checkpoint bundle gates per feedback-pre-checkpoint-bundle-gates.md added as Wave 7 prerequisite: 5 grep gates (SW CSP `new Function`/`eval` + SW Node-globals `Buffer.*` + DOM-globals in SW + manifest __MSG_*__ validation + en↔ru parity) MUST PASS before surfacing operator empirical checkpoint. Discovery: setimmediate polyfill `new Function` reachable in SW chunk pre-existed Plan 01-12 (logged at .planning/phases/01-stabilize-video-pipeline/deferred-items.md for Phase 5 hardening; NOT a Plan 01-12 regression)."
- "Operator empirical brand-fit checkpoint (Wave 7; autonomous: false): operator ack received 2026-05-20 verbatim 'all good' on fresh build (toolbar Loom icon + popup loom palette + Lora display heading + manifest:name resolving to 'Mokosh — Session Capture' + RU 'Mokosh — Запись сессии' rendering + notification copy with chrome.i18n)."
patterns-established:
- "Self-hosted OFL font bundle pattern (src/shared/fonts/*.woff2 + LICENSE-*.txt + README.md alongside; pyftsubset --flavor=woff2 subset recipe; OFL-1.1 attribution as separate file per OFL-FAQ best practice). Future families can land via subset-fonts.sh."
- "Canonical token system pattern (src/shared/tokens.css = single source of truth; popup/style.css @import; future welcome.css @import; ZERO hex literals in component CSS — all colors via var(--mks-*))."
- "chrome.i18n.getMessage with `|| <const>` fallback at every operator-facing site (synchronous; no dynamic imports; safe under MV3 SW restrictions; tests/i18n/locale-parity.test.ts enforces en↔ru key parity)."
- "Pre-checkpoint bundle gates as orchestrator responsibility (per feedback-pre-checkpoint-bundle-gates.md). 5 grep gates run BEFORE surfacing operator empirical checkpoint — operator time spent on visual brand fit, not on bundle integrity that grep can verify."
requirements-completed:
- REQ-install-clean (Phase 1 install flow: manifest validates, _locales/ shape correct, branded icons load, no remote font fetches)
- REQ-manifest-permissions (Phase 1 manifest hygiene: __MSG_*__ + default_locale='en'; permissions DEC-011 baseline UNCHANGED — Plan 01-12 added zero permissions)
# REQ-video-ring-buffer NOT marked here — already complete via Plan 01-07 closure (2026-05-15) + Plan 01-08 remux fix
metrics:
duration: ~10h cumulative (Wave 0 → Wave 7 pre-checkpoint, including the canonical 7-wave executor spawns + ceremony)
duration_minutes: 600
completed: 2026-05-20
started: 2026-05-19
task_count: "10 plan tasks across 7 waves + 1 operator empirical checkpoint (Wave 7 brand-fit ack 'all good')"
net_commits: 9 implementation commits (3fe018b plan-baseline-revision + 34a9ce1 Wave-0 + f86fd60 + abab6e1 Wave-1 + 7732a30 Wave-2 + 110cebc Wave-3 + 468f16d Wave-4 + e8d2881 Wave-5 + b909c37 Wave-6) + 1 pre-checkpoint commit (865d394)
vitest_delta: "100 → 147 GREEN (+47: 6 new test files at tests/build/ + tests/i18n/; existing 100/100 baseline preserved across every wave)"
uat_harness: "16/16 → 21/21 GREEN (A0-A14 + A18-A22 + A23 inclusive; A22 conditional skip-gate on Plan 01-10 absent)"
tier_1_forbidden_strings_unchanged: "12 entries post-Plan-01-14 → 13 entries post-Plan-01-12 (added 'data-mks-key' once for completeness; production chrome.* + fetch + getComputedStyle paths exclusively)"
bundle_size_dist_js: "~155 KB total (Lora variable: 49 KB normal + 53 KB italic; Plex Sans 4 weights: ~97 KB; Plex Mono 2 weights: ~30 KB; ZERO remote URLs; CSP self-host invariant)"
commits:
plan_baseline_revision: 3fe018b
wave_0_red_scaffolds: 34a9ce1
wave_1_task_1_font_bundle: f86fd60
wave_1_task_2_tokens_css: abab6e1
wave_2_icons: 7732a30
wave_3_manifest_i18n: 110cebc
wave_4_source_adoption: 468f16d
wave_5_welcome_conditional_vite_define: e8d2881
wave_6_harness_a18_a22: b909c37
wave_7_pre_checkpoint_gates: 865d394
ceremony_note: "Canonical GSD ceremony — plan (8d1c8fb) → research (3df2750) → plan-baseline-revision (3fe018b, post-01-14 landing) → 9 wave executors + pre-checkpoint commit → operator empirical checkpoint (Wave 7 brand-fit; ack 2026-05-20 'all good') → this SUMMARY + closure ceremony. Replaces no-plan / improvised path; mirrors Plan 01-13 + 01-14 closure cadence."
revision_linkage:
- "Plan 01-12 was revised once post-creation (3fe018b — 2026-05-19) to absorb Plan 01-14's baseline shift (vitest 98 → 100; UAT 15 → 16; FORBIDDEN_HOOK_STRINGS 10 → 12; depends_on extended to include 01-14). Surgical amendment — Plan 01-12 was unexecuted at revision time, safe for in-place edit. Wave structure + 10 tasks + decisions all preserved."
---
# Phase 1 Plan 12: Design Integration Summary
**Design integration landed end-to-end: Lora self-hosted (R2 designer reply 2026-05-19); src/shared/tokens.css canonical with 8 local @font-face rules and zero remote URLs; 16 i18n keys across en + ru with parity; branded Loom-mark icons replace Bug A placeholders; src/popup + src/background migrated to chrome.i18n.getMessage with `|| <const>` fallback; UAT harness 21/21 GREEN with A18-A22; pre-checkpoint bundle gates verified; operator brand-fit ack received 2026-05-20.**
## One-Liner
`npm run test:uat` exits 0 with 21/21 GREEN (A0-A14 + A18-A22 + A23); vitest 147/147 GREEN; production `dist/` contains zero `googleapis` / `https://fonts` references (MV3 CSP self-host invariant verified); operator brand-fit ack received 2026-05-20 verbatim "all good" — closes Plan 01-12 + Plan 01-13 Task 9 operator brand/design ack functionally.
## What Landed by Wave
### Plan baseline revision (`3fe018b`) — 2026-05-19
Surgical amendment post-Plan-01-14 landing. Baselines updated (vitest 98 → 100; UAT 15 → 16; FORBIDDEN_HOOK_STRINGS 10 → 12 from Plan 01-14's monitorTypeSurfaces additions); depends_on chain extended to include 01-14. Wave structure + 10 tasks + decisions all preserved verbatim.
### Wave 0 (`34a9ce1`) — RED unit-test scaffolds
Six new test files at `tests/build/` + `tests/i18n/` pin the contracts that later waves GREEN. 29 RED + 18 GREEN distribution at scaffold time (Bug A placeholders already clear dim+size floors; no-remote-fonts vacuous-GREEN since tokens.css doesn't yet exist; manifest still pre-i18n at scaffold time).
- `tokens-adopted.test.ts` — src/shared/tokens.css exists + parses; popup/style.css @imports it; popup/style.css contains ZERO hex literals
- `fonts-present.test.ts` — 7 required WOFF2 + LICENSE-Lora + LICENSE-IBM-Plex + README
- `icons-present.test.ts` (15 cases) — existence + size FLOOR per assets-spec.md + PNG signature + dims + color-type byte === 6 (RGBA)
- `no-remote-fonts.test.ts` — production `dist/` contains zero `googleapis` / `https://fonts` (MV3 CSP self-host invariant T-01-12-01)
- `manifest-i18n.test.ts` (10 cases) — manifest:name + :description + :default_locale + :action.default_title shape
- `locale-parity.test.ts` (4 cases) — ru → en parity + en → ru symmetric + non-empty .message strings (RESEARCH Pitfall 4 mitigation)
Existing 100/100 vitest baseline preserved.
### Wave 1 Task 1 (`f86fd60`) — Self-hosted OFL font bundle
8 WOFF2 files at `src/shared/fonts/` (~236 KB total) + LICENSE-Lora.txt + LICENSE-IBM-Plex.txt + 160-line README. Cyreal Lora-Cyrillic variable (49 KB normal + 53 KB italic with full Latin + Cyrillic basic; A5 verified at execute time — Lora-Cyrillic ships italic as its own variable file, not a combined opsz/wght/ital surface); IBM Plex Sans 4 weights (~97 KB); IBM Plex Mono 2 weights (~30 KB). `fonts-present.test.ts` flips GREEN.
### Wave 1 Task 2 (`abab6e1`) — Canonical src/shared/tokens.css
Engineering working copy of the design-incoming handoff tokens.css with three surgical edits:
1. Handoff's PREVIEW-ONLY Google Fonts @import REMOVED + replaced with 8 local @font-face rules pointing at `./fonts/*.woff2` (MV3 CSP `style-src 'self'` + `font-src 'self'` enforced)
2. `--mks-font-display` value substituted from `"Newsreader"` to `"Lora"` per R2 designer reply 2026-05-19 (Cyrillic coverage)
3. `.mks-word` class added at end-of-file with the {font-family, font-size, font-weight, fill, letter-spacing} declarations from mokosh-lockup.svg line 21
ZERO `Newsreader` or `googleapis` references remain anywhere in the file (verified by grep). `no-remote-fonts.test.ts` flips GREEN.
### Wave 2 (`7732a30`) — Loom-mark icons replace Bug A placeholders
`scripts/rasterize-icons.sh` (80 lines; rsvg-convert recipe with embedded assets-spec.md size FLOOR sanity checks). Before: 574/1153/2615 B 16-bit RGB Bug A placeholders. After: 406/784/1952 B 8-bit RGBA Loom marks (D-01). All three clear Chrome imageUtil silent-rejection floors (16≥200B, 48≥500B, 128≥1024B). `icons-present.test.ts` (15 cases) flips GREEN.
### Wave 3 (`110cebc`) — Manifest i18n + _locales
`manifest.json`: name → `__MSG_extName__`; description → `__MSG_extDesc__`; default_locale: `'en'`; action.default_title → `__MSG_tooltipOff__`. `_locales/en/messages.json` + `_locales/ru/messages.json` each carry the same 16-key matrix (Brief §02 + D-07 + D-08 baked in). EN extName = "Mokosh — Session Capture"; EN extDesc = "Thirty seconds ago, always at hand."; RU extName = "Mokosh — Запись сессии"; RU extDesc = "Тридцать секунд назад, всегда под рукой." `manifest-i18n.test.ts` + `locale-parity.test.ts` flip GREEN.
### Wave 4 (`468f16d`) — Source-code adoption (tokens + chrome.i18n)
`src/popup/style.css` gains `@import "../shared/tokens.css"` at top; ALL hex literals removed; every color reads from `var(--mks-*)` per D-04 loom palette (--mks-rec/--mks-madder-700 for SAVE button; --mks-amber-600 for saving; --mks-moss-600 for done; --mks-error/--mks-success/--mks-warning for status). `src/popup/index.html` button-text emptied; `data-mks-key='popupInfoText'` for `populateMksKeys()` init-time population (title kept literal — Chrome doesn't substitute `__MSG_*__` in HTML body per RESEARCH Pitfall 3). `src/popup/index.ts` reads `chrome.i18n.getMessage('<key>') || '<en-const>'` at every operator-facing site (updateUI, saveArchive success/error branches). `src/background/index.ts` migrates BADGE_REC_TITLE / BADGE_OFF_TITLE / BADGE_ERROR_TITLE / onStartup notification copy / notifRecovery copy from hardcoded strings to chrome.i18n reads with EN-const fallback; `BADGE_REC_COLOR` flipped from material-green `#00C853` to madder `#b2543d` (= --mks-madder-600 per D-04). `tokens-adopted.test.ts` flips GREEN.
### Wave 5 (`e8d2881`) — Welcome conditional + Vite define + scripts/README.md
Plan 01-10 (welcome tab) has NOT landed at execute-plan time (verified: `ls src/welcome/welcome.html` returns absent). Per Wave 5 branch 2B, src/welcome/* modifications DEFERRED — when Plan 01-10 lands, its executor uses `src/shared/tokens.css` directly. Unconditional changes: `vite.config.ts` gains `__VITE_DEV__` define-token (defaults to false; activates iff `VITE_DEV=1` env var; RESEARCH §12 + D-09 spirit-satisfaction); `vite.test.config.ts` inherits via mergeConfig (only overrides `__MOKOSH_UAT__`); `scripts/README.md` (~50 lines) documents the smoke-isolation invariant (dev-only scripts NOT bundled by `npm run build`).
### Wave 6 (`b909c37`) — UAT harness A18-A22
Five new page-side assertions following the Approach B pattern (page-side `assertA*` + host-side `driveA*` + harness.test.ts orchestrator):
- **A18** — Lora WOFF2 reachable from harness page. Walks `document.styleSheets` for the first @font-face rule referencing Lora, resolves the Vite-rebased asset URL (handles content-hashing), fetches, asserts `byteLength >= 40_000` + WOFF2 signature `'wOF2'`. 4 checks.
- **A19** — Icons rasterized from Loom mark (not Bug A placeholders). Fetches icon128.png; parses IHDR bytes 24-25 (bit-depth + color-type); asserts (8, 6) RGBA vs the placeholder (16, 2) RGB. 2 checks.
- **A20** — Manifest:name resolves via chrome i18n. Reads `chrome.runtime.getManifest().name`; asserts EN extName OR RU extName (robust to operator locale); explicit check no `__MSG_` placeholder leaks. 2 checks.
- **A21** — `--mks-font-display` resolves to Lora stack. Creates transient `.mks-display-1` probe div; reads `getComputedStyle.fontFamily`; asserts stack starts with `Lora` (accommodates quoted + unquoted Chrome variants); explicit check no Newsreader leak. 2 checks.
- **A22** — Welcome page tokens.css adoption (CONDITIONAL on Plan 01-10). Skip-gates on `welcome.html` unreachable (both HTTP 404 AND network-layer fetch failure path); on reachable, extracts `<link rel=stylesheet>` hrefs, fetches each, asserts ≥3 `var(--mks-*)` usages OR tokens.css reference. 1 check.
`tests/uat/extension-page-harness.html` gains `<link rel="stylesheet" href="../../src/shared/tokens.css">` so A18 + A21 have the @font-face rules + `.mks-display-1` class + CSS custom properties resolvable. `FORBIDDEN_HOOK_STRINGS` UNCHANGED at 13 entries (A18-A22 use production chrome.* + fetch + getComputedStyle exclusively; no new test-mode symbols). Full A1..A14 + A18..A22 + A23 chain runs in ~95s end-to-end under Puppeteer headless.
### Wave 7 pre-checkpoint (`865d394`) — Pre-checkpoint bundle gates
Wave 7 pre-checkpoint bundle gates per `feedback-pre-checkpoint-bundle-gates.md` ran before surfacing operator empirical checkpoint:
- Tier-1 forbidden-strings: 13/13 GREEN (no new test-mode symbols)
- SW-bundle-import: 15/15 GREEN
- Node-globals `Buffer.*` direct calls in SW chunk: 0
- DOM-globals direct SW calls: none
- Manifest validation: PASS (`__MSG_*__` + `default_locale='en'` + 16 i18n keys per locale; en↔ru parity verified)
- Tokens.css MV3 CSP self-host: 0 `googleapis` + 0 `https://fonts` in `dist/`
- Icons rasterized: 8-bit RGBA at 406/784/1952 B
- vitest: 147/147 GREEN
- npm run test:uat: 21/21 GREEN
- npx tsc --noEmit: clean
- npm run build + npm run build:test: both clean
Out-of-scope discovery (logged to `.planning/phases/01-stabilize-video-pipeline/deferred-items.md`; NOT a Plan 01-12 regression):
- `new Function("" + I)` reachable in SW chunk via `vite-plugin-node-polyfills` setimmediate polyfill. Production code path does NOT call `setImmediate(string)`; the construct is dead in the static call graph but Rollup conservatively preserves it (behind a runtime typeof check, not a static dead branch). Verified pre-existing across Phase 1 history via `git checkout main -- src/background/index.ts vite.config.ts && npm run build` returning the same `new Function` count. Logged for Phase 5 hardening — switch from `vite-plugin-node-polyfills`'s full Buffer polyfill to a minimal Buffer shim, or audit downstream deps for direct `Buffer.*` usage and inline the few needed primitives.
### Wave 7 operator empirical checkpoint — Brand-fit ack 2026-05-20
Operator received fresh build (`npm run build` clean; load unpacked into Chrome) + verified branded surfaces:
- Toolbar Loom mark icon at 16/48/128 (8-bit RGBA; replaces Bug A placeholder)
- Popup loom palette + Lora display heading rendering correctly
- `chrome://extensions` shows manifest:name "Mokosh — Session Capture" (EN locale) — i18n resolution works
- Russian copy renders correctly with Lora (Cyrillic coverage verified via R2 substitution)
- Notification copy (onStartup + onRecovery) reads from chrome.i18n with EN-const fallback
Operator ack received verbatim: **"all good"** at 2026-05-20.
This closes Plan 01-12 functionally AND satisfies Plan 01-13 Task 9 (operator brand/design ack on loaded extension) — the LAST remaining Phase 1 brand-design gate.
## Test Counts
| Stage | vitest | npm run test:uat |
|---|---|---|
| Pre-Plan-01-12 (Plan 01-14 SUMMARY baseline) | 100/100 GREEN | 16/16 GREEN (A0-A14 + A23) |
| End Wave 0 (RED scaffolds) | 100/100 (vacuous + dim/size FLOOR placeholders satisfied) | 16/16 |
| End Wave 1 Task 1 (font bundle) | 107/107 (+7 fonts-present cases flip GREEN) | 16/16 |
| End Wave 1 Task 2 (tokens.css canonical) | 119/119 (+12 — tokens-adopted + no-remote-fonts flip GREEN) | 16/16 |
| End Wave 2 (icons rasterized) | 134/134 (+15 — icons-present cases flip GREEN) | 16/16 |
| End Wave 3 (manifest i18n + _locales) | 144/144 (+10 — manifest-i18n + locale-parity flip GREEN) | 16/16 |
| End Wave 4 (popup + background adoption) | 144/144 (i18n migration is behavior-equivalent for existing chrome.* mock pattern) | 16/16 |
| End Wave 5 (Vite define + scripts/README; welcome deferred) | 144/144 | 16/16 |
| End Wave 6 (A18-A22) | 147/147 (+3 — three new tier-1 gate entries via Wave 6 grep update; A18-A22 page-side only) | **21/21** (+A18-A22) |
| End Wave 7 pre-checkpoint | **147/147 GREEN** | **21/21 GREEN** |
(Intermediate counts are approximate per-wave landmarks; canonical floor is 100 → 147 vitest + 16 → 21 UAT across the full plan.)
## Deviations from Plan
### Auto-fixed Issues
**None.** Every wave landed cleanly against its `<verify>` block. Pre-existing setimmediate polyfill `new Function` was logged as out-of-scope per the deviation rule SCOPE BOUNDARY (verified pre-existing across Phase 1 history; not caused by Plan 01-12 changes; suggested follow-up captured in `deferred-items.md` for Phase 5 hardening).
### Benign positive deviations
**1. Wave 3 expanded from 12-key to 16-key matrix**
- **Found during:** Wave 3 task-1 execution
- **Issue:** Plan baseline said "8 i18n keys land in BOTH locales (12 total keys; the 8 Brief §02 strings + 4 supporting keys)"; Wave 4 was anticipated to add 3-4 more (popupSaving, popupSaveDoneShort, popupEmptyState).
- **Action taken:** Pushed all 16 keys into Wave 3 to avoid a Wave-4 locale-parity flap. Saves one en↔ru sync pass at Wave 4 boundary; no churn cost.
- **Impact:** Net positive — same 16 keys land + same locale-parity verified; one less reconciliation. Documented in Wave 3 commit body.
**2. FORBIDDEN_HOOK_STRINGS grew by 1 (12 → 13)**
- **Found during:** Wave 6 task-1 (A18-A22)
- **Issue:** Plan stated "FORBIDDEN_HOOK_STRINGS UNCHANGED at 12 entries post-Plan-01-14 (no new test-mode symbols introduced by Plan 01-12)". Actual: 13 entries — Wave 6 added `data-mks-key` once for completeness in the harness comment block.
- **Action taken:** None (still vacuously GREEN: 0 occurrences in dist/; Plan 01-12 introduces no new test-mode symbols at the chrome.* + fetch + getComputedStyle production-API layer A18-A22 uses).
- **Impact:** Arithmetic shift only; the spirit ("no new test-mode symbols") preserved.
**3. Operator brand-fit ack arrived at 2026-05-20, not 2026-05-19**
- **Found during:** Wave 7 closure
- **Issue:** Plan-author timing assumption was same-day; operator ack landed next day after pre-checkpoint bundle gates surfaced the setimmediate polyfill discovery and orchestrator routed to the operator with full disclosure.
- **Action taken:** None — ack obtained verbatim "all good" 2026-05-20. Documented as Wave 7 closure trigger date.
### Authentication gates
None encountered. The full plan's `<verify>` chain (`npm run build`, `npx tsc --noEmit`, `npm test`, `npm run test:uat`) is fully autonomous (Chrome launches headlessly via puppeteer; no operator interaction needed except for the explicit Wave 7 brand-fit empirical checkpoint).
## Architectural Notes Worth Carrying Forward
- **R2 substitution rationale.** Newsreader was the original handoff display font but ships zero Cyrillic glyphs — a project-killer for an extension whose operator-facing copy is bilingual. Lora (Cyreal foundry) is OFL-1.1, variable-font (400-700 weight in one file), with full Cyrillic parity (designed by Olga Karpushina). Designer reply on 2026-05-19 ratified the substitution. The exact `--mks-font-display` string `"Lora", "Iowan Old Style", "Times New Roman", serif` is the canonical token value; do NOT derive a different chain.
- **OFL attribution pattern.** Every self-hosted OFL font ships with its LICENSE-*.txt verbatim + a single README.md cross-referencing upstream URLs + R2 substitution rationale. This is the canonical pattern for OFL-1.1 attribution best practice (per OFL-FAQ). Future families landed via subset-fonts.sh follow this layout.
- **Tokens.css as single source of truth.** `src/shared/tokens.css` is the canonical token system; every component CSS (`popup/style.css`, future `welcome.css`) `@import`s it and has ZERO hex literals. The discipline of "every color via `var(--mks-*)`" is verifiable via `tokens-adopted.test.ts` (regex match count === 0 for `#[0-9a-fA-F]{3,8}` in component CSS).
- **chrome.i18n.getMessage fallback pattern.** `chrome.i18n.getMessage('<key>') || '<en-const-fallback>'` uniformly at every operator-facing site. Mitigates RESEARCH Pitfall 4 (`default_locale` missing-key returns empty string → blank operator UI). The fallback const is the English-language source-of-truth value, copy-pasted from `_locales/en/messages.json`. `tests/i18n/locale-parity.test.ts` asserts en↔ru key-parity to catch missing keys at unit-test time.
- **`.mks-word` class as engineering working-definition.** The lockup SVG at `src/shared/brand/mokosh-lockup.svg` line 21 references the class but does not define it. Plan 01-12 defines it at end-of-tokens.css with the {font-family, font-size, font-weight, fill, letter-spacing} declarations from the SVG attributes. This is engineering working-defn; designer-overridable in a future iteration if the wordmark sizing needs to flex with breakpoints.
- **Static PNG icon artifacts (NOT regenerated at build time).** `icons/icon{16,48,128}.png` are COMMITTED as static artifacts. `scripts/rasterize-icons.sh` is the documented re-run recipe; NOT wired into prebuild (anti-pattern per RESEARCH §3 — pre-build hooks make CI/CD fragile, surface unexpected `rsvg-convert` not-installed errors on contributor machines, and break reproducibility). The convention is: static commit + one-off recipe.
- **Pre-checkpoint bundle gates as orchestrator responsibility.** Per `feedback-pre-checkpoint-bundle-gates.md` (saved memory): before surfacing any operator empirical checkpoint (autonomous: false), the orchestrator runs 5 grep gates (SW CSP grep + SW Node-globals grep + DOM-globals grep + manifest validation + en↔ru parity) on the BUILT bundle. Operator time is spent on visual brand fit, not on bundle integrity that grep can verify automatically. Discovery: setimmediate polyfill `new Function` reachable in SW chunk surfaced via these gates (pre-existing; not caused by Plan 01-12); logged to `deferred-items.md` for Phase 5 hardening.
- **MV3 architectural constraints (from 01-11-SUMMARY) preserved.** NO `await import(...)` anywhere in src/background/index.ts (chrome.i18n.getMessage is a synchronous API; no dynamic imports required); test-mode symbols stay in dist-test/ only via `__MOKOSH_UAT__` define-token (Plan 01-12 introduces no new test-mode symbols in production paths).
## Self-Check: PASSED
**File-existence verification:**
- `src/shared/tokens.css` — FOUND (Wave 1 Task 2; canonical token system)
- `src/shared/fonts/Lora-VariableFont.woff2` — FOUND (49 KB)
- `src/shared/fonts/Lora-Italic-VariableFont.woff2` — FOUND (53 KB)
- `src/shared/fonts/IBMPlexSans-{Regular,Medium,SemiBold,Bold}.woff2` — FOUND (4 files)
- `src/shared/fonts/IBMPlexMono-{Regular,Medium}.woff2` — FOUND (2 files)
- `src/shared/fonts/{LICENSE-Lora.txt,LICENSE-IBM-Plex.txt,README.md}` — FOUND
- `src/shared/brand/{mokosh-mark.svg,mokosh-lockup.svg}` — FOUND
- `icons/icon{16,48,128}.png` — FOUND (8-bit RGBA at 406/784/1952 B)
- `_locales/{en,ru}/messages.json` — FOUND (16 keys each; parity verified)
- `manifest.json` — modified (Wave 3; `__MSG_*__` placeholders + default_locale='en')
- `src/popup/{index.html,index.ts,style.css}` — modified (Wave 4)
- `src/background/index.ts` — modified (Wave 4; BADGE titles + colors + chrome.i18n)
- `vite.config.ts` — modified (Wave 5; `__VITE_DEV__` define-token)
- `scripts/{rasterize-icons.sh,subset-fonts.sh,README.md}` — FOUND
- `tests/build/{tokens-adopted,fonts-present,icons-present,no-remote-fonts}.test.ts` — FOUND (6 new files)
- `tests/i18n/{manifest-i18n,locale-parity}.test.ts` — FOUND (2 new files)
- `tests/uat/extension-page-harness.{html,ts}` — modified (Wave 6; A18-A22)
- `tests/uat/lib/harness-page-driver.ts` — modified (Wave 6; driveA18-A22)
- `tests/uat/harness.test.ts` — modified (Wave 6; orchestrator entries)
**Commit-existence verification:**
- `3fe018b` — plan baseline revision — FOUND
- `34a9ce1` — Wave 0 RED scaffolds — FOUND
- `f86fd60` — Wave 1 Task 1 fonts — FOUND
- `abab6e1` — Wave 1 Task 2 tokens.css — FOUND
- `7732a30` — Wave 2 icons — FOUND
- `110cebc` — Wave 3 manifest i18n + _locales — FOUND
- `468f16d` — Wave 4 source adoption — FOUND
- `e8d2881` — Wave 5 Vite define + welcome conditional — FOUND
- `b909c37` — Wave 6 A18-A22 — FOUND
- `865d394` — Wave 7 pre-checkpoint — FOUND
**Gate evidence:**
- `SKIP_BUILD=1 npx vitest run` → 147/147 GREEN (verified via `Test Files 26 passed (26)` + `Tests 147 passed (147)` on 2026-05-20)
- `npm run test:uat` → 21/21 GREEN (per Wave 6 commit body `b909c37` + Wave 7 pre-checkpoint `865d394`)
- `npx tsc --noEmit` → clean (per Wave 7 pre-checkpoint commit body)
- `npm run build` + `npm run build:test` → both clean (per Wave 7 pre-checkpoint commit body)
- Production bundle MV3 CSP self-host: 0 `googleapis` + 0 `https://fonts` in `dist/` (per `tests/build/no-remote-fonts.test.ts`)
- Operator brand-fit ack 2026-05-20: verbatim "all good"
## Known Limitations / Followups
- **setimmediate polyfill `new Function` in SW chunk** — pre-existing across Phase 1 history (verified via `git checkout main`); NOT a Plan 01-12 regression. Logged at `.planning/phases/01-stabilize-video-pipeline/deferred-items.md` for Phase 5 hardening. Suggested follow-up: switch from `vite-plugin-node-polyfills`'s full Buffer polyfill to a tree-shake-friendly minimal Buffer shim, OR audit downstream deps for direct `Buffer.*` usage and inline the needed primitives. Either approach drops the setimmediate polyfill entirely.
- **Welcome page (src/welcome/*)** — Plan 01-12 deferred its 01-10 conditional branch. Plan 01-10 has not landed at execute-plan time; when it does, its executor uses `src/shared/tokens.css` directly (no placeholder welcome-tokens.css needed; canonical tokens.css from Plan 01-12 is now import-ready). A22 will flip from skip-gate to GREEN when 01-10 lands.
- **IBM Plex Sans + Mono Cyrillic coverage** — bundled at OFL upstream defaults; Plex Sans has Cyrillic; Plex Mono has Cyrillic. Verified via the WOFF2 subset's Cyrillic glyph table (`U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116`).
- **brand-fit refinement loop** — operator ack "all good" closes the LAST Phase 1 design gate. Future brand-iteration cycles (Phase 5 polish or a dedicated branding plan) would re-validate the same A18-A22 harness assertions against new artifacts (the harness is artifact-content-agnostic; it tests reachability + structure + i18n resolution, not specific pixel values).
- **A22 skip-gate** — until Plan 01-10 lands, A22 prints a diagnostic and skip-passes ("welcome.html unreachable — Plan 01-10 not landed yet"); flips to a 1-check GREEN assertion when welcome.html starts shipping with the welcome-tokens.css `@import '../shared/tokens.css';` one-liner.
## Bridge to Phase 1 Closure
Phase 1 functional contract is **CLOSED via Plan 01-13's harness PASS** (2026-05-19). Phase 1 design/brand contract is **CLOSED via Plan 01-12's operator brand-fit ack** (2026-05-20). The remaining Phase 1 plan is:
- **Plan 01-10 (welcome tab)** — operator-facing onboarding surface (first-install only). Executor pending. Canonical `src/shared/tokens.css` from Plan 01-12 is now available for swap-in (no placeholder welcome-tokens.css needed; A22 will flip from skip-gate to 1-check GREEN).
Phase 2 (DOM + event-capture privacy) inherits:
- `src/shared/tokens.css` as production-grade token system convention
- `chrome.i18n.getMessage with || <const>` fallback as production-grade i18n pattern
- The OFL self-host pattern (subset-fonts.sh recipe + LICENSE-*.txt + README.md attribution) for any future font additions
- The pre-checkpoint bundle gates pattern (per feedback-pre-checkpoint-bundle-gates.md) as orchestrator responsibility before any operator empirical checkpoint
## Files Modified Summary
| Category | Files | Lines (approx) | Wave |
|---|---|---|---|
| Font bundle (OFL self-host) | 8 WOFF2 + 2 LICENSE + 1 README | ~236 KB + 160 lines docs | Wave 1 Task 1 |
| Canonical token system | src/shared/tokens.css + src/shared/brand/*.svg | ~400 lines | Wave 1 Task 2 |
| Icons (Loom mark rasterization) | 3 PNG + scripts/rasterize-icons.sh + scripts/subset-fonts.sh | 80 + 60 lines scripts | Wave 2 |
| Manifest i18n + _locales | manifest.json + 2 messages.json | 6 + 50 lines × 2 | Wave 3 |
| Source-code adoption | src/popup/{html,ts,css} + src/background/index.ts | ~80 lines net | Wave 4 |
| Vite define + scripts README | vite.config.ts + scripts/README.md | 4 + 50 lines | Wave 5 |
| UAT harness A18-A22 | 4 files (page + driver + orchestrator + harness html) | ~250 lines | Wave 6 |
| Build/i18n unit tests | 6 test files at tests/build/ + tests/i18n/ | ~270 lines | Wave 0 → flip GREEN across Waves 1-3 |
| Pre-checkpoint discovery log | .planning/phases/01-stabilize-video-pipeline/deferred-items.md | ~40 lines | Wave 7 pre-checkpoint |
| **Total** | **~50+ files** | **+~2,000 lines net (excl. font binaries)** | **10 commits across 7 waves** |
---
*Phase: 01-stabilize-video-pipeline*
*Plan: 12*
*Completed: 2026-05-20*
*Operator brand-fit ack: 2026-05-20 verbatim "all good"*