--- phase: 01-stabilize-video-pipeline plan: 10 subsystem: onboarding + welcome-tab + first-install-activation status: complete tags: - onboarding - welcome - chrome.runtime.onInstalled - chrome.storage - web_accessible_resources - chrome.i18n-welcomeHero - canonical-tokens-import - mokosh-mark-bundling - vite-svg-url-import - harness-A15-A17 - D-02-welcome-layout - D-08-tagline - D-17-onboarding - D-16-toolbar-charter - post-01-12-baseline - post-01-14-baseline - post-01-13-baseline - five-cycle-debug-closure - brand-polish-mokosh-rename requires: - 01-09 (toolbar onClicked direct flow + D-01 getDisplayMedia + onStartup notification scaffold + 3-state badge state machine) - 01-12 (canonical src/shared/tokens.css + 16 i18n keys with en↔ru parity + chrome.i18n.getMessage fallback pattern + manifest __MSG_*__ + default_locale='en') - 01-13 (Approach B UAT harness — extension-internal-page driver + offscreen synthetic stream + chrome.runtime.sendMessage bridge + driveA* host-side wrapper pattern) - 01-14 (FORBIDDEN_HOOK_STRINGS baseline at 12 entries; vitest 100 → 147 baseline; UAT 16/16 → 21/21 baseline pre-Plan-01-12-Wave-6 floor) provides: - "First-install operator-friendly activation: chrome.runtime.onInstalled('install') with empty chrome.storage.local → exactly ONE welcome tab at chrome-extension:///src/welcome/welcome.html + flag persistence (onboarding-completed:true + installed-at:)" - "Subsequent installs/updates ('update' / 'chrome_update' reasons OR already-completed flag) → NO welcome tab re-open" - "src/welcome/* page bundle (4 files: welcome.html + welcome.ts + welcome.css + copy.ts) honoring Plan 01-12 must_have #9 path-B contract (canonical tokens.css @import; no placeholder welcome-tokens.css)" - "chrome.i18n.getMessage adoption for welcomeHeroRu + welcomeHeroEn (parallel-text RU+EN hero layout per D-08; Plan 01-12 fallback pattern preserved)" - "Canonical mokosh-mark.svg woven-square mark inlined via Vite `?url` import (replaces text placeholder per closure-cycle debug 01-10-welcome-page-missing-mark.md)" - "Harness A15-A17 extension (24/24 UAT GREEN; A17 grew to 8 sub-checks incl. A17.7 --mks-rec getComputedStyle probe + A17.8 mark-bundling invariant)" - "Plan 01-09 follow-up fixes landed inline: notifStartupCta key split (4bba679) + startVideoCapture no-tab dependency removed (a2dfc8c — D-01 cleanup gap)" - "Plan 01-12 follow-up brand polish: 'AI Call Recorder' → 'Mokosh' across 4 content surfaces (d21ed17)" - "Welcome page contains NO REQUEST_PERMISSIONS / chrome.runtime.sendMessage start path (D-16-toolbar charter preserved — toolbar owns start; welcome is informational)" affects: - src/welcome/welcome.html (new; lang='ru'; semantic hero+body+footer; data-mokosh-key + data-mokosh-i18n-key + data-mokosh-slot attributes) - src/welcome/welcome.ts (new; populateMark + populateCopy + populateI18n pipelines) - src/welcome/welcome.css (new; @import canonical tokens.css; zero hex literals; var(--mks-*) throughout) - src/welcome/copy.ts (new; COPY map for non-tagline strings + WELCOME_HERO_*_FALLBACK constants) - src/background/index.ts (openWelcomeIfFirstInstall helper at line ~186; onInstalled wiring; later startVideoCapture no-tab cleanup at a2dfc8c) - manifest.json (web_accessible_resources for welcome.html) - vite.config.ts (rollupOptions.input welcome entry) - vite.test.config.ts (mirror welcome entry for dist-test/) - globals.d.ts (ambient module decl for `*.svg?url` Vite imports — added during welcome-mark debug) - tests/background/onboarding.test.ts (new; 3 RED→GREEN tests pinning install/update/flag + storage-key contract) - tests/background/start-video-capture-no-tab.test.ts (new — added during a2dfc8c debug session; 3 tests pinning no-active-tab contract) - tests/uat/extension-page-harness.ts (assertA15 + assertA16 + assertA17 with 8 sub-checks) - tests/uat/lib/harness-page-driver.ts (driveA15 + driveA16 + driveA17 host-side wrappers) - tests/uat/harness.test.ts (drivers list extended with A15-A17 interleaved between A14 and A18) - tests/background/no-test-hooks-in-prod-bundle.test.ts (it() timeout bumped 5s → 30s during debug 01-10-vitest-build-test-timeout — orthogonal to plan but landed in this closure cycle) - _locales/en/messages.json (Plan 01-09 follow-up: notifStartup split → notifStartupCta + notifRecordingStarted; 16 → 17 keys; 4bba679) - _locales/ru/messages.json (Plan 01-09 follow-up: notifStartup split → notifStartupCta + notifRecordingStarted; 4bba679) - README.md (Plan 01-12 follow-up brand polish: H1 "AI Call Recorder" → "Mokosh — Session Capture"; d21ed17) - package.json (Plan 01-12 follow-up brand polish: name "ai-call-extension" → "mokosh-session-capture"; d21ed17) - tests/i18n/manifest-i18n.test.ts (Plan 01-12 follow-up brand polish: header comment refresh; d21ed17) tech-stack: added: - "Vite `?url` import idiom (`import markUrl from '../shared/brand/mokosh-mark.svg?url'`) — small-asset default inlining as data URL in the welcome chunk; @crxjs auto-WAR for resources transitively reachable from extension pages" patterns: - "First-install activation pattern (chrome.runtime.onInstalled('install') + chrome.storage.local flag-gating + chrome.tabs.create + fire-and-forget invocation with .catch defense-in-depth)" - "data-mokosh-slot='mark' design-swap landmark — wrapper div remains in HTML for forward-compat; populateMark() pipeline injects bundled SVG at populate time; placeholder text span = graceful-degradation fallback when JS fails" - "Three-pipeline DOM population (populateMark + populateCopy + populateI18n) — each walks a distinct attribute namespace; filter-pipeline form (no continue statements per project style); ordered so mark renders before text strings populate" - "Per-it() vitest timeout for build-touching tests (added 30s ceiling at no-test-hooks-in-prod-bundle.test.ts:247 during debug; preserves 5s default for pure-CPU tests)" preserved: - "Plan 01-12 path-B end-to-end contract (canonical src/shared/tokens.css @import + chrome.i18n.getMessage for welcomeHero keys + NO placeholder welcome-tokens.css file)" - "Plan 01-13 Approach B harness architecture (page-side assertA* + host-side driveA* + harness.test.ts orchestrator); A15-A17 follow it verbatim" - "Plan 01-09 D-16-toolbar charter (toolbar onClicked owns start path; welcome page is informational + read-only; NO REQUEST_PERMISSIONS in welcome page)" - "Plan 01-12 chrome.i18n.getMessage with `|| ` fallback pattern (verbatim at every operator-facing site; mitigates RESEARCH Pitfall 4)" - "Plan 01-12 Wave 4 BADGE_REC_COLOR madder #b2543d (= --mks-madder-600 per D-04); welcome --mks-rec resolves to same canonical value via A17.7 probe" - "Plan 01-11 architectural learnings (NO `await import(...)` in SW; helper is async-IIFE-style; chrome.i18n.getMessage is synchronous — no dynamic imports required)" - "Plan 01-13 + 01-14 FORBIDDEN_HOOK_STRINGS inventory at 12 entries (Plan 01-10 introduces ZERO new test-mode symbols at the chrome.* + fetch + getComputedStyle + DOMParser production-API layer A15-A17 uses)" key-files: created: - src/welcome/welcome.html (Wave 1 49f087f; later extended Wave-4-debug d48a715 with mark-bundling comment) - src/welcome/welcome.ts (Wave 1 49f087f; later extended Wave-4-debug d48a715 with populateMark + ?url import) - src/welcome/welcome.css (Wave 1 49f087f; later extended Wave-4-debug d48a715 with .welcome-hero__mark-img rule) - src/welcome/copy.ts (Wave 1 49f087f; later extended Wave-4-debug d48a715 with welcome.hero.mark.alt key) - tests/background/onboarding.test.ts (Wave 0 89e1e09; 3 tests A/B/C) - tests/background/start-video-capture-no-tab.test.ts (closure-cycle debug a2dfc8c; 3 tests A/B/C — no-active-tab + url-less + regression-guard) - globals.d.ts (closure-cycle debug d48a715; ambient module declaration for `*.svg?url`) modified: - src/background/index.ts (Wave 2 8f329d8 — openWelcomeIfFirstInstall helper + onInstalled wiring; closure-cycle debug a2dfc8c — startVideoCapture no-tab cleanup) - manifest.json (Wave 1 49f087f — web_accessible_resources entry for welcome.html) - vite.config.ts (Wave 1 49f087f — rollupOptions.input welcome entry) - vite.test.config.ts (Wave 1 49f087f — mirror welcome entry) - tests/uat/extension-page-harness.ts (Wave 3 b112cb7 — assertA15/A16/A17 with 7 sub-checks; later extended d48a715 with A17.8 mark-bundling sub-check) - tests/uat/lib/harness-page-driver.ts (Wave 3 b112cb7 — driveA15/A16/A17 wrappers) - tests/uat/harness.test.ts (Wave 3 b112cb7 — drivers list extension) - tests/background/no-test-hooks-in-prod-bundle.test.ts (closure-cycle debug 0854baf — it() timeout 5s → 30s) - _locales/{en,ru}/messages.json (closure-cycle debug 4bba679 — notifStartup split into notifStartupCta + notifRecordingStarted; 16 → 17 keys) - README.md + package.json + tests/i18n/manifest-i18n.test.ts (closure-cycle debug d21ed17 — brand polish "AI Call Recorder" → "Mokosh") deleted: [] decisions: - "Plan 01-12 must_have #9 path-B contract honored end-to-end. welcome.css opens with `@import '../shared/tokens.css';` (canonical tokens import); NO placeholder welcome-tokens.css file is created. Canonical Lora (display) + IBM Plex Sans (UI) + D-04 Loom palette cascades into welcome.css scope at build time." - "chrome.i18n.getMessage('welcomeHeroRu') + chrome.i18n.getMessage('welcomeHeroEn') reads at populate time (data-mokosh-i18n-key attribute namespace); both keys exist in _locales/{en,ru}/messages.json from Plan 01-12 Wave 3. Parallel-text RU+EN hero layout regardless of operator locale per D-08." - "Non-tagline copy (page title + explainer lines + CTA + footer privacy) lives in src/welcome/copy.ts COPY map keyed by stable identifiers (data-mokosh-key namespace). These engineering-grade placeholder strings (D-03 Sober voice register) remain outside _locales/ pending a future copy-iteration plan." - "D-17-onboarding suffix in JSDoc disambiguates from D-17-port-lifecycle (per CONTEXT.md lines 540-545)." - "Welcome page is informational + read-only (D-16-toolbar charter). NO REQUEST_PERMISSIONS message type, NO chrome.runtime.sendMessage start path, NO duplicate getDisplayMedia trigger. CTA copy directs operator at the toolbar icon." - "Fire-and-forget invocation of openWelcomeIfFirstInstall(details).catch(...) preserves the synchronous chrome.runtime.onInstalled listener boundary while allowing async chrome.storage.local + chrome.tabs.create work inside the helper." - "data-mokosh-slot='mark' attribute preserved as design-swap landmark after the welcome-mark debug session — the wrapper div + placeholder span remain on the div for forward-compat; the slot becomes a no-op slot under normal operation but stays as the design-swap landmark." - "Option B (Vite `?url` import + populateMark) chosen over Option A (manual WAR + chrome.runtime.getURL) and Option C (inline SVG in HTML) for the mark-bundling fix — idiomatic for the codebase (font asset pipeline precedent), minimum-coupling, auto-WAR via crxjs, hashed-asset cache-busting, default-inlining of small SVGs as data URL." - "Plan 01-09 notifStartup key split into notifStartupCta + notifRecordingStarted (debug 01-09-startup-notification-misleading-text) — the original phrasing 'Recording started. I'm watching the last 30 seconds.' conflated a CTA-with-gesture invite with a post-start confirmation. New CTA text 'Mokosh ready. Click to start a recording.' truthfully describes pre-recording state." - "Plan 01-09 startVideoCapture dead tab query removed (debug 01-09-notification-start-no-active-tab) — pre-D-01 chrome.tabCapture-era code; the notifications.onClicked path now reaches sendMessage(START_RECORDING) without throwing 'No active tab found'." - "Plan 01-12 brand polish (debug 01-12-stale-ai-call-recorder-references) — 4 trailing 'AI Call Recorder' references in welcome copy CTA + README + package.json + test header comment renamed to 'Mokosh'. _locales/ + manifest.json were already canonical post-D-07." - "vitest it() timeout bump to 30s (debug 01-10-vitest-build-test-timeout) — surgical per-it() timeout for the build-touching test that now exceeds vitest's 5s default after the welcome-page Vite processing slowed `npm run build` from ~2.88s to ~5.28s. Per-it() ceiling is the vitest idiom; global testTimeout would over-relax 95% of pure-CPU tests." patterns-established: - "Welcome-page DOM population three-pipeline pattern — populateMark() walks [data-mokosh-slot='mark'] and injects bundled SVG; populateCopy() walks [data-mokosh-key] and writes textContent from COPY map; populateI18n() walks [data-mokosh-i18n-key] and writes textContent from chrome.i18n.getMessage with `|| ` fallback. Init order: populateMark → populateCopy → populateI18n (mark renders before text)." - "Vite `?url` import + auto-WAR (via @crxjs/vite-plugin) — minimum-coupling pattern for bundling extension assets that need to be referenced by URL at runtime. Vite default-inlines small assets (≤4096 B) as data URLs; transparently switches to hashed-asset filenames for larger assets. Future asset additions follow this idiom." - "First-install vs subsequent-install gating via chrome.storage.local — onboarding-completed boolean + installed-at timestamp (~80 B total; well under 10 MB storage.local quota). Defense-in-depth try/catch wraps the helper body; helper logs its own errors via logger.warn; fire-and-forget invocation cannot escape the synchronous listener boundary." - "Pre-checkpoint bundle gates per feedback-pre-checkpoint-bundle-gates.md saved memory — applied before surfacing the operator empirical UAT (Tier-1 hook-string grep + SW CSP-safety + Node-globals + DOM-globals + manifest validation + en↔ru parity). Operator time spent on visual/UX verification, not bundle integrity that grep can verify." metrics: duration: "~5h cumulative (Wave 0 → Wave 3 autonomous + Wave 4 cycle-1 operator UAT + 5 inter-cycle debug sessions + Wave 4 cycle-2 operator ack + Wave 4 follow-up brand-rename ack)" duration_minutes: 300 started: 2026-05-20 completed: 2026-05-20 task_count: "5 plan tasks (4 autonomous + 1 operator empirical) across 4 waves + 5 inter-cycle debug sessions" net_commits: "9 (4 plan-wave commits + 5 debug commits — see commits map below)" vitest_delta: "147 → 153 GREEN (+6: +3 onboarding tests from Wave 0 + 3 start-video-capture-no-tab tests from a2dfc8c debug; pre-existing 147 baseline preserved across every wave)" uat_harness: "21/21 → 24/24 GREEN (+3 A15/A16/A17; preserved across all 5 inter-cycle fixes)" tier_1_forbidden_strings_unchanged: "12 entries pre-Plan-01-10 → 12 entries post-Plan-01-10 (Plan 01-10 introduces ZERO new test-mode symbols; A15-A17 use chrome.tabs.query + chrome.storage.local.get + fetch + DOMParser + getComputedStyle production APIs exclusively)" build_time: "~2.88s pre-Plan-01-10 → ~5.28s post-Plan-01-10 (welcome page Vite processing + 8 WOFF2 fonts shipped in d48a715 / 49f087f; mitigated via per-it() 30s timeout bump in 0854baf)" commits: wave_0_red_onboarding_tests: 89e1e09 wave_1_welcome_bundle_vite_manifest: 49f087f wave_2_openwelcome_helper_oninstalled: 8f329d8 wave_3_harness_a15_a16_a17: b112cb7 cycle1_debug_01_09_notifStartup_split: 4bba679 cycle1_debug_01_10_welcome_page_missing_mark: d48a715 cycle1_debug_01_10_vitest_build_test_timeout: 0854baf cycle1_debug_01_09_notification_start_no_active_tab: a2dfc8c cycle2_followup_debug_01_12_brand_polish: d21ed17 ceremony_note: "Canonical GSD ceremony — plan revision 7f58e0a (post-01-12 + 01-14 baselines) → 4 wave executor commits → Wave 4 operator empirical UAT cycle-1 rejection 2026-05-20 ~08:56 → 4 inter-cycle debug sessions (notifStartup text + welcome mark + vitest timeout + startVideoCapture no-tab) → Wave 4 cycle-2 operator ack 'All good' → Wave 4 follow-up brand-rename ack + 5th debug session (brand polish) → this SUMMARY + closure ceremony. Mirrors Plan 01-12 + 01-13 + 01-14 closure cadence." revision_linkage: - "Plan 01-10 was rewritten in place 2026-05-19 (3a530c2) to absorb Plan 01-12 + 01-14 baseline shifts (vitest 98 → 147; UAT 15/15 → 21/21; FORBIDDEN_HOOK_STRINGS 10 → 12) BEFORE execution started. welcome.css @imports canonical tokens (Plan 01-12 path-B); welcomeHero keys read from chrome.i18n (not COPY map); copy.ts retains non-tagline keys + WELCOME_HERO_*_FALLBACK constants. Final revision 7f58e0a 2026-05-20 brought vitest baseline to 147 + UAT baseline to 21 + FORBIDDEN baseline to 12; ready-to-execute at Wave 0 land." --- # Phase 1 Plan 10: Welcome Tab Summary **First-install operator-friendly activation landed: chrome.runtime.onInstalled('install') opens the welcome tab automatically on first install only (flag-gated via chrome.storage.local); the welcome page renders with canonical Plan 01-12 brand assets (Lora display + IBM Plex Sans UI + D-04 Loom palette via tokens.css @import) and parallel-text RU+EN tagline via chrome.i18n; harness A15-A17 (24/24 UAT GREEN); 5 inter-cycle debug sessions resolved cycle-1 rejection concerns + cycle-2 brand-rename ask; operator brand-fit ack 2026-05-20 verbatim "All good".** ## One-Liner `npm run test:uat` exits 0 with 24/24 GREEN (A0-A14 + A15-A17 + A18-A22 + A23); vitest 153/153 GREEN; welcome tab opens on fresh-install Chrome with canonical Mokosh mark + Lora-rendered "Mokosh — Session Capture" + Cyrillic tagline "Тридцать секунд назад, всегда под рукой." + EN tagline "Thirty seconds ago, always at hand."; reload does NOT re-open; operator brand-fit ack 2026-05-20 "All good"; Phase 1 final functional plan delivered. ## What Landed by Wave ### Plan baseline revision (`7f58e0a`) — 2026-05-20 08:48 Surgical amendment post-Plan-01-12 + Plan-01-14 landing. Baselines updated (vitest 98 → 147; UAT 15/15 → 21/21; FORBIDDEN_HOOK_STRINGS 10 → 12 from Plan 01-14's monitorTypeSurfaces additions). welcome.css contract revised to `@import '../shared/tokens.css';` directly (Plan 01-12 path-B); welcomeHero keys read via chrome.i18n.getMessage (not COPY map); copy.ts retains non-tagline keys + WELCOME_HERO_*_FALLBACK constants. Wave structure + 5 tasks + decisions all preserved verbatim. ### Wave 0 (`89e1e09`) — RED onboarding tests Three RED unit tests at `tests/background/onboarding.test.ts` pinning the onInstalled contract: - **Test A** — first install + empty storage → exactly ONE chrome.tabs.create with welcome URL + chrome.storage.local.set with `{onboarding-completed: true, installed-at: }` + chrome.storage.local.get called with EXACT key 'onboarding-completed' (storage-schema cross-version-compat pin). - **Test B** — subsequent install (reason='update') → zero chrome.tabs.create + zero chrome.storage.local.set. - **Test C** — install + flag already set → zero chrome.tabs.create. buildBgStub factory copied from `tests/background/onstartup-notification.test.ts` lines 59-102; extended with chrome.tabs.create + chrome.storage.local.{get,set} + chrome.runtime.onInstalled._callbacks rig (same idiom as onStartup at line 24). vitest baseline preserved at 147 GREEN (+3 RED = 150 total at scaffold time). ### Wave 1 (`49f087f`) — Welcome page bundle + Vite entries + manifest Four new files under `src/welcome/`: - **welcome.html** — semantic `