--- slug: 01-09-startup-notification-misleading-text status: resolved goal: find_and_fix trigger: "it fires the notification when starts the browser if installed --- even though the recording is not going." phase: 01-stabilize-video-pipeline plan: 01-09 opened: 2026-05-20 closed: 2026-05-20 orchestrator_diagnosed: true --- # Debug session 01-09-startup-notification-misleading-text — i18n key conflated CTA + post-start confirmation ## Problem statement During the Plan 01-09 closure UAT 2026-05-20, the operator reported the verbatim symptom: "it fires the notification when starts the browser if installed --- even though the recording is not going." On browser start with the extension installed (no active recording), the OS notification displayed "Recording started. I'm watching the last 30 seconds." Operator empirical evidence (orchestrator-inspected `~/Downloads/session_report_2026-05-20_09-51-45.zip`): `meta.json` shows `totalEvents: 0`, `events.json` empty — operator never actually recorded; they only saw the misleading notification on browser start, were confused, and saved an empty archive in an attempt to verify state. ## Root cause `_locales/{en,ru}/messages.json` defined a single key `notifStartup` whose text leaned toward a post-start confirmation phrasing ("Recording started. I'm watching the last 30 seconds."). The key's own `.description` field acknowledged the conflation: *"Notification body for the onStartup + manual-start flow."* In reality only the **onStartup** path consumes the key (`src/background/index.ts:1023`) — and on that path the recording has **not** started; per Phase 1 always-on charter recording does not auto-start, and the notification itself IS the gesture surface (clicking it triggers `notifications.onClicked` → `startVideoCapture` at line 1038–1050). The operator was reading a confirmation message in a pre-recording context. The fallback constant `NOTIF_STARTUP_FALLBACK` ("Recording started. Click here to start a recording.") was a hybrid that still led with the misleading "Recording started." phrase. ## Fix design Per orchestrator charter, split the conflated key into two: 1. **`notifStartupCta`** (the path actually wired today) - EN: `"Mokosh ready. Click to start a recording."` - RU: `"Mokosh готов. Нажмите, чтобы начать запись."` - Description (en): *"Notification body for the onStartup flow — CTA-with-gesture invite. Notification title is extName. Per Phase 1 always-on charter: recording does NOT auto-start; this notification is the gesture surface."* - Description (ru): equivalent Russian description. 2. **`notifRecordingStarted`** (reserved for future post-manual-start confirmation flow) - EN: `"Recording started. I'm watching the last 30 seconds."` (preserves original text for future re-use) - RU: `"Запись запущена. Я слежу за последними 30 секундами."` (preserves original text) - Description: clearly scoped to "AFTER recording successfully starts via startVideoCapture." 3. Renamed fallback constant `NOTIF_STARTUP_FALLBACK` → `NOTIF_STARTUP_CTA_FALLBACK` with new EN value `"Mokosh ready. Click to start a recording."` to match the new CTA text. 4. Updated single SW call site (`src/background/index.ts:1023`): `i18nMessage('notifStartup', NOTIF_STARTUP_FALLBACK)` → `i18nMessage('notifStartupCta', NOTIF_STARTUP_CTA_FALLBACK)`. 5. Updated inline test comment in `tests/background/onstartup-notification.test.ts` to reflect the new key + fallback. The assertion regex `/recording|recor|click/i` matches the new CTA text via the `click` alternation, so no test logic change was needed — the regex deliberately covers both fallback and resolved locale variants. ### Migration alias decision **No alias retained.** `notifStartup` was referenced in exactly one code call site (line 1023) and only as an inline comment in one test (line 164). Both updated in this patch. The single-cycle rename is cleaner than carrying a deprecation alias. ## Files modified - `_locales/en/messages.json` — key split + new descriptions - `_locales/ru/messages.json` — key split + new descriptions - `src/background/index.ts` — fallback rename + call site update - `tests/background/onstartup-notification.test.ts` — inline comment refresh ## Behavior preservation - Same `chrome.notifications.create` call signature, same id prefix `mokosh-startup-`, same `priority: 1`, same icon path. - Same `chrome.notifications.onClicked` handler at line 1038–1052 invokes `startVideoCapture` on click. The notification's CTA-with-gesture role is unchanged — only the displayed text now truthfully describes pre-recording state. - No new test-mode symbols. `FORBIDDEN_HOOK_STRINGS` inventory stays at 12. ## Acceptance gates - ✅ `_locales/en/messages.json` + `_locales/ru/messages.json` both contain `notifStartupCta` AND `notifRecordingStarted` with the orchestrator-specified texts; locale-parity test passes (4/4 GREEN). - ✅ `src/background/index.ts` onStartup handler reads `notifStartupCta` with `NOTIF_STARTUP_CTA_FALLBACK`. - ✅ `npx vitest run --exclude tests/build/** --exclude tests/background/no-test-hooks-in-prod-bundle.test.ts`: **104/104 GREEN** (excludes 2 build-dependent gates blocked by the parallel Plan 01-10 mark-bundling debug session's uncommitted SVG assets — orthogonal to this fix; orchestrator coordinates the merged re-verification). - ✅ `npx vitest run tests/i18n/ tests/background/onstartup-notification.test.ts`: **18/18 GREEN** (locale-parity 4/4 + onstartup-notification 14/14). - ✅ `npx tsc --noEmit` clean on `src/background/index.ts` (the single SW file modified by this patch). - ⏸ `npm run build` + `npm run test:uat` + Tier-1 grep-on-built-bundle deferred to orchestrator-level re-verification after the parallel Plan 01-10 mark-bundling fix also lands (operator-UAT re-spawn coordinated by orchestrator). ## Empirical re-verification (operator-side, post-orchestrator merge) After both this fix and the parallel Plan 01-10 mark-bundling fix land: 1. Reload the extension. 2. Close + reopen Chrome. 3. Observe the OS notification on startup. Expected text: "Mokosh ready. Click to start a recording." (NOT "Recording started…"). 4. Click the notification → `startVideoCapture` runs → operator selects screen → recording starts (gesture surface preserved). ## Noteworthy - **Two parallel debug sessions touched the same working tree.** The Plan 01-10 mark-bundling session edited `src/welcome/welcome.ts` + `welcome.css` + `welcome.html` concurrently with this session's edits to background + locales. The vitest gates for `tests/build/no-remote-fonts.test.ts` and `tests/background/no-test-hooks-in-prod-bundle.test.ts` both require `npm run build` to succeed, which currently fails because the parallel session's `welcome.ts` imports `mokosh-mark.svg?url` before that asset exists in the tree. Those 2 failures are entirely owned by the parallel session — explicitly excluded from this session's verification. Full build + UAT will be re-run by the orchestrator after both sessions merge. - **Operator-facing key names follow a discoverable pattern.** `notifStartupCta` (action invite) vs `notifRecordingStarted` (state confirmation) — future i18n contributors get unambiguous semantic hints from the key alone. The `.description` field reinforces this with explicit scoping ("Per Phase 1 always-on charter: recording does NOT auto-start").