66678798f1ccd377d05223e4aaecf449e6da06ac
239 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| a440c7d76b |
wip: phase-02 paused — 4 plans validated, ready for execution
Phase 2 (Stabilize export pipeline) planning ceremony complete: - /gsd-discuss-phase 2 → 02-CONTEXT.md ( |
|||
| df8c086ff0 |
docs(02): sync CON-manifest-permissions constraint with DEC-011 Amendment 1
Plan-checker iteration 2 surfaced an informational stale-reference: the
Constraints section's CON-manifest-permissions bullet still listed the
original SPEC §7 5-permission set (`tabCapture`, `activeTab`, `downloads`,
`scripting`, `storage`). This was superseded by:
- Phase 01 DEC-003 Amendment (retired `tabCapture`; added `desktopCapture`
+ `offscreen` + `notifications`)
- Phase 02 DEC-011 Amendment 1 (added `tabs` for D-P2-02 meta.urls feature
at commit
|
|||
| 9dcfcf0793 |
fix(02): revise plans per checker (B1 + 4 flags) — add tabs permission for D-P2-02
- BLOCKER B1: add `tabs` to manifest.json permissions (DEC-011 Amendment 1 cites Phase 2 D-P2-02 meta.urls feature as justification). Honors D-P2-02 "all tabs visible" wording verbatim. Updates manifest-i18n test expected permission list lockstep. - F1: add A28 harness assertion for REQ-archive-layout strict zip-layout verification (5 entries, no extras). - F2: createArchive empty-tracker fallback removed; logs warn + sets urls:[] instead of fake [extension-origin URL]. 02-01 RED test pins empty-tracker → urls:[]. - F3: 02-02 Task 3 prose deliberation struck; typed `blob-url-mint-failed` throw is the resolved-only contract. - F4: 02-02 Task 3 verify block adds full-suite `npm test` after focused test runs. - A27 strict-mode (Plan 02-04): REQUIRES both URLs in meta.urls; FAILS on length < 2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 0608b22427 |
feat(02): plans 01-04 — Phase 2 export pipeline closure (Blob URL + meta.urls + schema + harness)
Wave structure (4 plans, 3 waves): - 02-01 (Wave 1 RED): 15 RED tests pinning D-P2-01 (blob: URL contract), D-P2-02 (meta.urls schema + dedup + filter), D-P2-03 (strict 8-field validation + schemaVersion '2' cutover marker). - 02-02 (Wave 2): Offscreen-minted Blob URL pipeline — extends PortMessageType with CREATE/REVOKE messages; SW downloadArchive rewrite (data: → blob: via base64-on-wire to offscreen + URL.createObjectURL + chrome.downloads.onChanged revoke lifecycle). Closes audit P0-6; unblocks >2 MB archives. - 02-03 (Wave 2): meta.urls schema migration + tab-url-tracker module (chrome.tabs.onActivated + onUpdated → deduplicated, filtered, first-seen- ordered string[]); SessionMetadata 7→8 fields with schemaVersion + urls; REQUIREMENTS.md REQ-meta-json-schema amendment. Closes P1 #10. - 02-04 (Wave 3): UAT harness A24+A25+A26+A27 — blob: URL prefix, <5s SAVE→zip latency, meta.json 8-field shape, multi-tab dedup; pre-checkpoint bundle gates per saved memory + operator empirical UAT cycle 1. Tier-1 FORBIDDEN_HOOK_STRINGS inventory stays at 12 (no new hook symbols — chrome.* monkey-patches + JSZip + production APIs only). Locked decisions honored (per 02-CONTEXT.md): - D-P2-01: offscreen-minted Blob URL via existing keepalivePort + base64 wire format (reuses D-12 precedent at src/shared/binary.ts). - D-P2-02: meta.json url:string → urls:string[]; URL filter per CONTEXT.md <specifics> (include https://, chrome-extension://; exclude chrome://, about:, devtools://, file://); dedup + first-seen ordering. - D-P2-03: full scope; 8-field strict schema validation with schemaVersion='2' as the 8th field (planner-resolved tentative pick; revisable by plan-checker). Architectural constraints preserved: - Always-on charter (Plan 01-09 Amendment 3): no finally-block in saveArchive; no clearTabUrlsSeen on SAVE. - Tier-1 FORBIDDEN_HOOK_STRINGS = 12 (no new test-hook symbols). - Never await import(...) in src/background/index.ts (Plan 01-11 SUMMARY). - Pre-checkpoint bundle gates per feedback-pre-checkpoint-bundle-gates.md (run in 02-04 Task 4 before operator surface). Plan validation: gsd-sdk frontmatter.validate + verify.plan-structure GREEN for all 4 plans. ROADMAP updated: Phase 2 Plans list + Goal/Success Criteria block annotated with D-P2-02/D-P2-03 amendments + 5th success criterion (Blob URL + revoke lifecycle for >2 MB archives); Progress table 0/TBD → 0/4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| cc042a5583 |
docs(02): capture phase context — discuss-phase complete
Phase 2 (Stabilize export pipeline) discuss-phase landed via inline canonical workflow execution. 02-CONTEXT.md captures 3 locked decisions: - **D-P2-01:** Offscreen-minted Blob URL pipeline replaces base64 data: URL download path (src/background/index.ts:709-710). SW Blob → offscreen URL.createObjectURL → SW chrome.downloads.download → URL.revokeObjectURL on onChanged. Closes audit P0-6. Unblocks real-archive size (>2 MB). - **D-P2-02:** meta.json schema migrates singular `url: string` to plural `urls: string[]` capturing all tabs visible during the 30s recording window. Schema-breaking change requires REQUIREMENTS.md REQ-meta-json-schema amendment + SessionMetadata type update. Closes audit P1 #10 captured-URL bug. - **D-P2-03:** Full Phase 2 scope = Blob URL migration + meta.urls schema migration + strict meta.json schema validation test + UAT harness A24+ <5s latency assertion. ~3-4 plans expected. Decision provenance: - D-P2-01 rationale: user "up to you. If you think we need to migrate — good let's do it." Plus analysis: real archives EXCEED base64 cap. - D-P2-02 rationale: user picked "All tabs' URLs as an array (meta.json.urls)" — highest informational fidelity for multi-tab bug reproduction. Privacy acceptable per "log is internal" v1 charter. - D-P2-03 rationale: user picked "Full scope: bug fixes + schema + harness latency assertion". Canonical references + code context + deferred items captured in CONTEXT.md. Phase boundary explicit: not from-scratch; closes residual gaps after Plans 01-08/01-09/01-10/01-12 substantively shipped REQ-popup-ui + REQ-archive- layout + REQ-screenshot-on-export. Next: /gsd-plan-phase 2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 6dbed91efd |
docs(roadmap): re-phase milestone — remove Phase 2 (DOM/event-capture privacy)
Per operator charter shift 2026-05-20: "we don't care about privacy hardening.
At least here." Archive flow is internal-only (no external transmission),
which reframes the password-masking P0-5 defect from privacy-regulation
gravity to operator-hygiene polish.
Re-phasing applied across 4 planning artifacts:
ROADMAP.md:
- Original Phase 2 ("Stabilize DOM + event-capture privacy") REMOVED entirely
(summary list + Phase Details section + Progress table row).
- DOM + event-log VERIFICATION (REQ-rrweb-dom-buffer + REQ-user-event-log)
ABSORBED by new Phase 3 (SPEC §10 smoke verification).
- Phase numbering: old 3 → new 2 (export), old 4 → new 3 (smoke), old 5 → new 4
(optional harden). Dependency chains updated accordingly.
- Overview blurb + journey narrative + success criteria refreshed.
- Phase 3 (smoke) explicitly NOT-in-scope: P0-5 password masking dropped.
REQUIREMENTS.md traceability:
- REQ-rrweb-dom-buffer: Phase 2 → Phase 3 (verification scope; UAT harness
A24+ extension planned).
- REQ-user-event-log: Phase 2 → Phase 3 (same context).
- REQ-password-confidentiality: Phase 2 → Out of Scope (v1) — DEFERRED per
charter shift.
- REQ-popup-ui, REQ-screenshot-on-export, REQ-archive-layout,
REQ-meta-json-schema, REQ-archive-export-latency: Phase 3 → Phase 2
(renumbered; substantively shipped via Plans 01-08 + 01-09 + 01-12;
residual gaps in Phase 2).
- Coverage: 10 mapped + 1 out-of-scope (was 11 mapped).
PROJECT.md:
- CON-sensitive-data-masking: DEFERRED 2026-05-20 (preserves audit trail
via strikethrough; rationale documented).
- DEC-004 amendment: rrweb 5000-event cap retained; masking deferred. Cites
rrweb 2.0.0-alpha.4 maskInputSelector→maskInputFn API change.
STATE.md:
- frontmatter total_phases: 5 → 4.
- stopped_at narrative captures the re-phasing event.
CLI bug note: this re-phasing was attempted via `gsd-sdk query phase.remove 2`
+ canonical `/gsd-remove-phase 2` Skill invocation, but BOTH paths produced
corrupted output (cascading rename via reverse-iteration loop at
phase.cjs:670-679 collapsed all subsequent phases to "Phase 2", plus a
mysterious "2026"→"2002" date corruption). Recovery applied as manual edits
in this commit. CLI bug logged as upstream GSD-framework concern; not a
Mokosh-side issue.
Plan: next is `/gsd-discuss-phase 2` (new Phase 2 = export pipeline; narrowed
scope per re-phasing — ~2-3 plans expected since Plans 01-08 + 01-09 + 01-10
+ 01-12 already shipped most surface).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 586836f8a0 |
docs(01): VERIFICATION + Phase 1 closure markers — goal-backward audit GREEN
gsd-verifier goal-backward audit (2026-05-20) returned GREEN verdict on
Phase 1 (Stabilize Video Pipeline + whole-desktop capture + as-automatic-
as-platform-allows recording start):
- 17/17 must-haves verified: 11 REQs/charters + 6 cross-cutting gates
- 14/14 plans complete (01-01..01-09 + 01-11 spike-pivot + 01-12 + 01-13
+ 01-14 + 01-10)
- 5 operator empirical acks: Plan 01-07 (Chrome playback 2026-05-15) +
Plan 01-13 (harness 2026-05-19) + Plan 01-12 (brand-fit 2026-05-20) +
Plan 01-10 cycle-2 ("All good" 2026-05-20) + Plan 01-10 brand-rename
follow-up (2026-05-20)
- Test gates: vitest 153/153 GREEN; UAT harness 24/24 GREEN; Tier-1 grep
gate 12 FORBIDDEN_HOOK_STRINGS; pre-checkpoint bundle gates PASS
- 7 P0 audit defects: 6 closed in-Phase-1-scope; P0 #6 (data-sensitive
masking) properly deferred to Phase 2
Marker flips landed:
- STATE.md status reflects Phase 1 COMPLETE; completed_phases 0 → 1
- ROADMAP.md Phase 1 row [ ] → [x] with closure-arc summary
- REQUIREMENTS.md REQ-video-ring-buffer In-progress → Complete 2026-05-20
- VERIFICATION.md committed (orchestrator-bundle pattern per verifier
protocol)
Forward-looking deferred (NOT gaps):
- Phase 2: REQ-rrweb-dom-buffer + REQ-user-event-log +
REQ-password-confidentiality (audit P0 #6)
- Phase 5 hardening: getDisplayMedia cursor visibility; setimmediate
polyfill new Function pre-existing; tabs permission gap; dark-surface
logo contrast; 2 ffprobe/ffmpeg test flakes
Phase 2 (Stabilize DOM + event-capture privacy) kickoff pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| d1ef77a7d1 |
docs(01-10): state + roadmap + requirements — Plan 01-10 closure
State markers sync after Plan 01-10 closure: STATE.md: - progress.completed_plans: 13 → 14 - progress.percent: 93 → 100 (all 14 Phase 1 functional plans complete) - status: executing (Phase 1 final-closure marker flip pending) - stopped_at + Last session timestamps refreshed - Current Position bumped to 14/14 plans complete - Outstanding Phase 1 gates: Plan 01-10 row marked CLOSED; Phase 1 final-closure marker flip listed as remaining work - Plan 01-10 closure section added mirroring existing 01-12/01-13/01-14 patterns: 4 wave commits + 5 inter-cycle debug commits + cycle-2 ack - Performance Metrics: Phase 01 P10 row added (5h, 5 tasks, 14 files) - Decisions: 3 Plan 01-10 architectural decisions added (first-install activation; D-16-toolbar charter preservation; three-pipeline DOM population pattern; startVideoCapture D-01 cleanup gap closure) ROADMAP.md: - Plan 01-10 row flipped to [x] with full closure annotations (commit chain + harness counts + operator ack date) - Plan 01-09 row annotated with closure-cycle follow-up debug commits ( |
|||
| 52dc2e6a6e |
docs(01-10): summary — welcome tab + 5-cycle debug closure + brand polish (153/153 vitest, 24/24 UAT GREEN, ack 2026-05-20)
Plan 01-10 (welcome tab) full SUMMARY landing closure of Phase 1's final
functional plan. Welcome tab landed end-to-end across 4 waves + Wave 4
operator empirical UAT cycle 2 ack "All good" 2026-05-20.
Architecture:
- chrome.runtime.onInstalled('install') + chrome.storage.local
flag-gating (onboarding-completed:true + installed-at:<Date.now()>)
+ chrome.tabs.create + fire-and-forget .catch defense-in-depth.
- Plan 01-12 must_have #9 path-B contract honored: welcome.css opens
with `@import '../shared/tokens.css';` (canonical Lora display +
IBM Plex Sans UI + D-04 Loom palette); NO placeholder
welcome-tokens.css file.
- chrome.i18n.getMessage for welcomeHeroRu + welcomeHeroEn with
`|| <en-const>` fallback (Plan 01-12 fallback pattern preserved).
- Vite `?url` import + auto-WAR idiom bundles canonical
mokosh-mark.svg as inline data URL in welcome chunk
(closure-cycle debug 01-10-welcome-page-missing-mark).
- Three-pipeline DOM population: populateMark walks
[data-mokosh-slot='mark']; populateCopy walks [data-mokosh-key]
from in-file COPY map; populateI18n walks [data-mokosh-i18n-key]
from chrome.i18n.getMessage.
- D-16-toolbar charter preserved: welcome page is informational +
read-only; NO REQUEST_PERMISSIONS / chrome.runtime.sendMessage
start path. CTA copy directs operator at toolbar icon.
Wave structure (4 plan-wave commits):
-
|
|||
| d21ed17310 |
fix(01-12): brand polish — replace stale 'AI Call Recorder' refs with Mokosh (4 files)
Plan 01-12 D-07 (commit |
|||
| a2dfc8cb9b |
fix(01-09): startVideoCapture — remove stale active-tab dependency (D-01 cleanup gap)
The legacy chrome.tabs.query({ active: true, currentWindow: true }) +
"No active tab found" validation inside startVideoCapture were load-
bearing in the pre-D-01 chrome.tabCapture era but became functionally
dead after Plan 01-09's D-01 conversion to getDisplayMedia-in-offscreen.
The only post-D-01 consumer was a log line at index.ts:521.
The dead validation caused an activeTab-permission-scope asymmetry
between callers: chrome.action.onClicked grants activeTab on the click
gesture (so tab.url was readable → toolbar path worked silently) but
chrome.notifications.onClicked does NOT grant activeTab and the extension
has no `tabs` permission, so notifications.onClicked → startVideoCapture
threw "No active tab found" before reaching ensureOffscreen. Operator
2026-05-20 UAT against the new notifStartupCta CTA copy ("Mokosh ready.
Click to start a recording.", commit
|
|||
| 0854baf66c |
fix(01-10): vitest build-test it() timeout — bump to 30s for slower welcome-page build
The build-completes Tier-1 gate at tests/background/no-test-hooks-in-prod-bundle.test.ts:247 was racing vitest's default 5000ms it() ceiling. Plan 01-10 closure shipped the welcome page (commits |
|||
| d48a715da5 |
fix(01-10): welcome page mark — bundle canonical mokosh-mark.svg + replace placeholder
Plan 01-10 must_have #9 path-A swap-in (landed 2026-05-20 per debug session 01-10-welcome-page-missing-mark). Closes the planning-coverage gap where Plan 01-12 path-B (canonical tokens import) ran ahead of 01-10, leaving the welcome hero with a text placeholder 'Mokosh' inside the rec-bg circle instead of the canonical 2×2 woven-square mark from src/shared/brand/mokosh-mark.svg. Why Option B (Vite ?url import) over manual WAR (A) or inline SVG (C): - @crxjs/vite-plugin ^2.0.0-beta.25 auto-WARs transitively-reachable resources from extension pages — no manifest.json edit needed. - Vite default-inlines small SVGs (~600 bytes < 4096 byte default assetsInlineLimit) as data:image/svg+xml URLs in the welcome chunk — no extra HTTP request, no extra WAR entry. - Hashed asset fallback works automatically if the SVG grows past the inline limit in future revisions. - Existing font-bundling precedent (dist/assets/Lora-*.woff2 + IBMPlex*.woff2) proves the Vite + crxjs pipeline. Files modified: - src/welcome/welcome.ts — added markUrl import + populateMark() that walks [data-mokosh-slot='mark'] and injects an <img>. - src/welcome/welcome.html — added explanatory comment block; preserved the data-mokosh-slot wrapper for forward-compat (the placeholder span remains as the JS-fail-gracefully fallback). - src/welcome/welcome.css — added .welcome-hero__mark-img rule (60% sizing inside the existing styled circle wrapper). - src/welcome/copy.ts — added 'welcome.hero.mark.alt' COPY key (Russian per D-03 Sober voice). - globals.d.ts — added *.svg?url ambient module declaration (Vite recommended pattern; keeps tsconfig.json types: ['chrome'] clean by not requiring vite/client triple-slash directives). - tests/uat/extension-page-harness.ts — extended A17 with A17.8 sub-check verifying the canonical mark SVG is bundled into the welcome chunk (data URL OR file URL form) AND that the canonical viewBox='0 0 32 32' is preserved through bundling. Acceptance gates passed: - npx tsc --noEmit exit 0 - npm run build exit 0 - SKIP_BUILD=1 npm test → 150/150 GREEN - npm run test:uat → 24/24 GREEN including A17.8 - Tier-1 hook-string grep gate PASS (no FORBIDDEN_HOOK_STRINGS in production bundle). - Manifest valid JSON; web_accessible_resources auto-bundled. - Pre-checkpoint bundle gates 1/2/3: vendor pre-existing hits (JSZip + ts-ebml) confirmed identical pre-change via git stash baseline; not caused by this fix. Forward-looking deferred (out of scope): - Issue 2 dark-surface contrast (e.g. chrome.notifications icon128 may need a light-stroke variant). The welcome hero's rec-orange BG already provides high contrast with the dark ink stroke — this is correct design. Per the orchestrator's explicit constraint, light-variant mark for dark notification panels is deferred to Phase 5. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 4bba679e39 |
fix(01-09): notifStartup text split — notifStartupCta for onStartup; notifRecordingStarted for manual-start
Operator UAT 2026-05-20 rejected the build because the OS notification fired
on `chrome.runtime.onStartup` ("Recording started. I'm watching the last 30
seconds.") implied recording had auto-started when in fact recording was
not running. Per Phase 1 always-on charter recording does NOT auto-start;
the notification is the gesture surface that invites the operator to start
one (notifications.onClicked → startVideoCapture, src/background/index.ts:1038).
Root cause: a single i18n key `notifStartup` conflated the pre-recording
CTA-with-gesture path (the only path actually wired today) and a future
post-manual-start confirmation path. The key's own `.description` field
acknowledged the conflation. Operator-facing text leaned toward the
confirmation phrasing.
Fix (key split, no behavior change):
- `notifStartupCta` — EN: "Mokosh ready. Click to start a recording." /
RU: "Mokosh готов. Нажмите, чтобы начать запись." — wired into the
onStartup handler.
- `notifRecordingStarted` — preserves the original text ("Recording
started. I'm watching the last 30 seconds." / "Запись запущена…") for
a future post-manual-start confirmation flow.
- Fallback constant renamed `NOTIF_STARTUP_FALLBACK` →
`NOTIF_STARTUP_CTA_FALLBACK`; value updated to match the new CTA text.
- Inline test comment in tests/background/onstartup-notification.test.ts
refreshed to reference the new key + fallback. Assertion regex
/recording|recor|click/i covers both fallback + resolved locale variants,
no logic change.
Notification behavior preserved: same id prefix `mokosh-startup-`, same
priority, same icon, same onClicked → startVideoCapture wiring. No new
test-mode symbols (FORBIDDEN_HOOK_STRINGS inventory stays at 12).
Files modified:
- _locales/en/messages.json
- _locales/ru/messages.json
- src/background/index.ts
- tests/background/onstartup-notification.test.ts
Verification:
- npx vitest run --exclude tests/build/** --exclude tests/background/no-test-hooks-in-prod-bundle.test.ts: 104/104 GREEN
- 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 2 build-dependent vitest gates (tests/build/no-remote-fonts.test.ts +
tests/background/no-test-hooks-in-prod-bundle.test.ts) and npm run test:uat
are deferred to orchestrator-level re-verification after the parallel
Plan 01-10 mark-bundling fix also lands (operator-UAT re-spawn coordinated
by orchestrator).
Debug record: .planning/debug/resolved/01-09-startup-notification-misleading-text.md
Operator UAT rejection event: 2026-05-20
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| b112cb7861 |
test(01-10): wave-3 task-4 — harness A15+A16+A17 (onboarding flag observability + no-re-open settle + design-swap-readiness with @import probe); 24/24 GREEN
Plan 01-10 Wave 3: extends the UAT harness with three new page-side
assertions covering the onboarding contract + the canonical-tokens
design-swap-readiness invariant. UAT baseline 21 → 24 GREEN.
tests/uat/extension-page-harness.ts (page-side):
- assertA15 — chrome.storage.local 'onboarding-completed' === true +
'installed-at' is number. Verifies SW's openWelcomeIfFirstInstall
side-effects.
- assertA16 — 2s settle window; chrome.tabs.query welcome-tab count
delta === 0. Verifies flag-gating across SW respawns.
- assertA17 — 7 sub-checks covering: welcome.html parse + .welcome-hero
+ >=7 mokosh-keyed attrs + welcome.css canonical @import literal OR
inlined --mks-* evidence + (zero hex OR canonical resolved) + >=5
var(--mks-*) refs + bundled JS preserves populate plumbing +
getComputedStyle --mks-rec → rgb(178, 84, 61) (canonical D-04 Loom).
- window.__mokoshHarness surface extended with the three new methods;
type declaration + assignment + page-ready status text updated.
tests/uat/lib/harness-page-driver.ts (host-side):
- driveA15, driveA16, driveA17 — standard page.evaluate wrappers
matching driveA14 / driveA18..A22 idiom. driveA16 dominates the
new wall-clock budget (~2.1s for the settle window).
tests/uat/harness.test.ts (orchestrator):
- Drivers array interleaves A15/A16/A17 AFTER A14 + BEFORE A18.
A22's skip-gate no longer triggers (Plan 01-10 lands welcome.html;
A22 now exercises the substantive token-usage path).
- FORBIDDEN_HOOK_STRINGS unchanged at 12 entries (A15-A17 use only
chrome.tabs.query / chrome.storage.local.get / fetch / DOMParser /
getComputedStyle — all production-API surfaces).
DEVIATION (Rule 1 — auto-fix bug in plan-supplied check):
The plan's A17.6 spec used literal substring checks 'COPY[' and
'chrome.i18n.getMessage(' which fail against minified production
output. Vite/Rollup terser renames `COPY` → `f` (local variable
mangling) and welcome.ts's source uses optional chaining
`chrome?.i18n?.getMessage?.(` which doesn't match the verbatim
literal. Replaced with two minification-survivable witnesses:
1. 'welcome.page.title' — literal Object.freeze key (terser
preserves object-literal keys verbatim).
2. 'i18n' + 'getMessage' + 'welcomeHero' substring conjunction —
chrome global + property access + fallback key literal; all
three survive minification regardless of optional-chaining
insertion or rename.
Both witnesses prove the populate plumbing survives the build (the
ground-truth contract A17.6 enforces). The relaxed contract is
semantically equivalent — neither substring is load-bearing on its
own; both witness the same underlying invariant.
Verify (all GREEN):
- npm run test:uat: 24/24 assertions passed (A0 grep gate + A1..A14
+ A15..A17 + A18..A22 + A23).
- npx tsc --noEmit: clean.
- npm run build:test: clean; dist-test/assets/welcome-wB0e_R_n.js
bundled; harness page bundle includes new asserts.
- SKIP_BUILD=1 npx vitest run tests/background/no-test-hooks-in-prod-bundle.test.ts:
13/13 GREEN (Tier-1 grep gate; FORBIDDEN_HOOK_STRINGS at 12).
- Full vitest baseline preserved: 137 ex-grep-gate + 13 grep-gate
= 150 GREEN (Plan 01-10 target).
A17.7 canonical proof: getComputedStyle.color = 'rgb(178, 84, 61)' —
the @import '../shared/tokens.css' directive resolves through to the
canonical D-04 Loom palette --mks-madder-600 = #b2543d at runtime, as
the empirical proof Plan 01-12 must_have #9 path-B contract demands.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 8f329d8b74 |
feat(01-10): wave-2 task-3 — openWelcomeIfFirstInstall helper + onInstalled wiring (D-17-onboarding) — 3 RED → GREEN
Plan 01-10 Wave 2: SW handler extension flips Task 1's 3 RED onboarding
tests GREEN.
src/background/index.ts changes:
1. Three top-level constants added near the badge/notification block:
- ONBOARDING_FLAG = 'onboarding-completed'
- ONBOARDING_INSTALLED_AT = 'installed-at'
- WELCOME_PATH = 'src/welcome/welcome.html'
SCREAMING_SNAKE per project naming standard for true constants.
2. openWelcomeIfFirstInstall helper added below ensureOffscreen
(interfaces §1 placement). JSDoc cites D-17-onboarding (CONTEXT.md
line 537+; SUFFIX disambiguates from D-17-port-lifecycle per
CONTEXT.md lines 540-545). Body:
- Early return on details.reason !== 'install' (subsequent
installs / updates / chrome_update / shared_module_update do
NOT open a welcome tab — Test B's contract).
- chrome.storage.local.get(ONBOARDING_FLAG) read with the EXACT
single-key string (storage-schema cross-version-compat pin;
Test A.3's contract).
- Early return if stored[ONBOARDING_FLAG] === true — Test C's
contract (already-onboarded suppression).
- chrome.tabs.create + chrome.storage.local.set with both the
flag and Date.now() installed-at — Test A.1 + A.2's contract.
- Defense-in-depth try/catch wraps the whole body; any thrown
chrome.* call is logged via logger.warn but does not propagate
(D-16-toolbar start path remains independent).
3. onInstalled listener extended: fire-and-forget call to
openWelcomeIfFirstInstall(details) AFTER initialize(); .catch()
boundary so rejected promises cannot escape the synchronous
listener. The existing IDB cleanup + initialize() call sequence
stays unchanged.
Architectural compliance:
- NO `await import(...)` added (01-11-SUMMARY architectural constraint
preserved; the three matches in lines 14-28 are documentation
comments about Plan 01-11's falsification).
- NO `as any` (chrome.runtime.InstalledDetails ambient typing covers
the parameter).
- NO `continue` (if-else early-return only).
- No new dependencies.
Verify (all GREEN):
- npx vitest run tests/background/onboarding.test.ts: 3 passed (Test
A flipped RED → GREEN; B + C continue passing as load-bearing
guards).
- Full vitest baseline 147 → 150 (137 ex-build-gated + 13 in build-
gated = 150 GREEN total).
- npx tsc --noEmit: clean.
- npm run build: clean; openWelcomeIfFirstInstall + D-17-onboarding
references survive into dist/assets/index.ts-*.js.
- Tier-1 FORBIDDEN_HOOK_STRINGS unchanged at 12 entries; gate GREEN.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 49f087fe40 |
feat(01-10): wave-1 task-2 — welcome page bundle + Vite entries + web_accessible_resources
Plan 01-10 Wave 1: welcome page bundle staged with canonical Plan 01-12
tokens.css @import + chrome.i18n for D-08 tagline (Plan 01-12 path-B
contract).
Files created:
- src/welcome/copy.ts (Russian non-tagline COPY map per D-03 Sober
voice; WELCOME_HERO_RU_FALLBACK + WELCOME_HERO_EN_FALLBACK exported
for the welcome.ts `|| <en-const>` fallback chain).
- src/welcome/welcome.html (lang='ru'; data-mokosh-key + data-mokosh-
i18n-key attribute conventions; SINGLE stylesheet link; D-02 Hero +
body + footer structure; 10 keyed attrs total; <title> populated
via populateCopy).
- src/welcome/welcome.css (FIRST LINE `@import '../shared/tokens.css';`;
ZERO hex literals in source; 65 var(--mks-*) refs; D-02 layout +
--mks-welcome-max-w=720px; --mks-rec madder for recording-related
accents per D-04 Loom palette).
- src/welcome/welcome.ts (vanilla DOM; populateCopy + populateI18n
filter-pipeline form per project rule "no `continue`"; no `as any`;
Logger from src/shared; document.readyState guard; no event
handlers per D-16-toolbar informational charter).
Files modified:
- vite.config.ts: rollupOptions.input gains `welcome:
'src/welcome/welcome.html'`; __VITE_DEV__ + __MOKOSH_UAT__ defines
untouched (Plan 01-12 Wave 5 baseline preserved verbatim).
- vite.test.config.ts: mirror entry in dist-test/; mergeConfig pattern
untouched.
- manifest.json: web_accessible_resources block added after
host_permissions, before background; storage permission preserved
in permissions array; default_locale='en' + __MSG_*__ placeholders
from Plan 01-12 Wave 3 preserved verbatim.
NO src/welcome/welcome-tokens.css file is created — Plan 01-12 must_have
#9 path-B contract: Plan 01-12 landed FIRST (
|
|||
| 89e1e09d60 |
test(01-10): wave-0 task-1 — RED onboarding tests (3 tests pin install/update/flag + storage-key)
Three tests in tests/background/onboarding.test.ts pinning the
Plan 01-10 D-17-onboarding contract:
Test A (RED): first install + empty storage opens exactly ONE welcome
tab whose URL contains 'src/welcome/welcome.html', sets
chrome.storage.local.set({'onboarding-completed':true,
'installed-at':<number>}), AND calls chrome.storage.local.get with
EXACT key 'onboarding-completed' (storage-schema cross-version-compat
pin; preserves I-02 lesson from prior draft).
Test B (vacuous-GREEN, becomes load-bearing post-Task-3): reason='update'
→ chrome.tabs.create NOT called.
Test C (vacuous-GREEN, becomes load-bearing post-Task-3): flag already
true → chrome.tabs.create NOT called.
Tests B and C pass vacuously until Task 3 lands openWelcomeIfFirstInstall;
they remain load-bearing AFTER Task 3 as no-tab-open guards for the
update/already-onboarded branches. Test A flips RED → GREEN at Task 3.
Stub scaffold inherits buildBgStub from onstartup-notification.test.ts;
extended with chrome.tabs.create + chrome.storage.local.{get,set} +
chrome.runtime.onInstalled._callbacks (addListener.mockImplementation
pattern to capture the SW's registered listener).
DEVIATION NOTE: plan's <verify> expected `3 failed` but only Test A
(positive contract) goes RED pre-Task-3; Tests B+C are negative-path
guards that pass trivially when the helper is absent. This is standard
TDD (positive test fails RED; negative tests stay GREEN through GREEN→
REFACTOR). No code change needed — Task 3's GREEN gate is "all 3 GREEN".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 7f58e0ae31 |
fix(01-10): revise plan per 01-12 + 01-14 baselines (vitest 98→147, UAT 15→21, FORBIDDEN 10→12, welcome.css @imports canonical tokens, welcomeHero keys read from chrome.i18n)
Surgical amendment to unexecuted Plan 01-10 absorbing the post-draft landing of Plan 01-12 (canonical src/shared/tokens.css + 16 i18n keys including welcomeHeroRu/welcomeHeroEn; 2026-05-20 operator brand-fit ack) and Plan 01-14 (vitest +2 + UAT +1 + FORBIDDEN_HOOK_STRINGS +2). Baseline shifts: - vitest 98 → 147 GREEN (post Plan 01-12 + 01-14); plan close target 150. - UAT 15 → 21 GREEN (A0-A14 + A18-A22 + A23); plan close target 24 (A0-A14 + A15-A17 + A18-A22 + A23). - FORBIDDEN_HOOK_STRINGS 10 → 12 (Plan 01-14: lastGetDisplayMediaConstraints + get-last-getDisplayMedia-constraints); Plan 01-10 introduces no new test-mode symbols; inventory unchanged at 12. Plan 01-12 must_have #9 path-B contract honored end-to-end (Plan 01-12 landed FIRST, so the welcome page adopts canonical assets directly): - welcome.css opens with `@import '../shared/tokens.css';` (NO placeholder welcome-tokens.css; removed from files_modified). - D-08 hero tagline elements use data-mokosh-i18n-key='welcomeHeroRu' + data-mokosh-i18n-key='welcomeHeroEn'; welcome.ts reads via chrome.i18n.getMessage with `|| <en-const>` fallback per Plan 01-12 fallback pattern. WELCOME_HERO_RU_FALLBACK + WELCOME_HERO_EN_FALLBACK constants exported from copy.ts for the degradation path. - copy.ts COPY map retains non-tagline keys only (page title + explainer lines + CTA + footer privacy; engineering placeholders per D-03). - A17 design-swap-readiness invariant extended with: - A17.5: welcome.css contains canonical @import directive OR inlined `--mks-rec:` evidence; - A17.6: bundled JS contains COPY[ OR chrome.i18n.getMessage('welcomeHero; - A17.7 NEW: getComputedStyle probe on var(--mks-rec) returns non-default value (canonical rgb(178, 84, 61) = #b2543d = --mks-madder-600 per Plan 01-12 Wave 4 D-04 Loom palette adoption). - depends_on extended to [01-09, 01-13, 01-14, 01-12]. Preserved verbatim: 5-task structure, A15/A16 contracts, D-02/D-08/D-09 references, threat model + STRIDE register, operator empirical checkpoint shape, Plan 01-12 default_locale='en' + __MSG_*__ + __VITE_DEV__ + __MOKOSH_UAT__ + src/shared/tokens.css. Validated: gsd-sdk frontmatter.validate + verify.plan-structure both PASS; task_count=5, all tasks complete with files/action/verify/done. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 66e6f503a4 |
docs(01-12): state + roadmap + requirements — Plan 01-12 closure
Plan 01-12 closure documentation sync per the plan's Wave 7 Task 1 spec. Three docs land together as one atomic closure commit per the Plan 01-13 + 01-14 closure cadence convention. STATE.md changes: - status: verifying → executing (Plan 01-10 welcome tab still pending) - stopped_at: Plan 01-14 → Plan 01-12 closed via Wave 7 brand-fit ack 2026-05-20 'all good'; Plan 01-10 remains as final Phase 1 functional plan - last_updated + last_activity: 2026-05-19 → 2026-05-20 - progress.completed_plans: 12 → 13 (13 plans: 01-01..01-09 + 01-11 spike + 01-12 + 01-13 + 01-14; Plan 01-10 pending) - progress.percent: 86 → 93 - "Plan 01-13 closure" header annotated: brand/design ack subsequently closed via Plan 01-12 Wave 7 2026-05-20 - New "Plan 01-12 closure (2026-05-20)" section: 7-wave execution arc with all 10 commit hashes ( |
|||
| 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
|
|||
| 865d394ae0 |
docs(01-12): wave-7 pre-checkpoint — log out-of-scope discovery (setimmediate polyfill new Function pre-existing)
Wave 7 pre-checkpoint bundle gates per feedback-pre-checkpoint-bundle-gates.md
revealed a pre-existing benign concern in the SW production bundle:
`vite-plugin-node-polyfills` (configured for Buffer in vite.config.ts)
bundles the upstream `setimmediate` package which contains a fallback
`new Function("" + I)` evaluated when setImmediate is called with a
non-function argument. Production source code does NOT call
setImmediate(string); the construct is dead at the runtime call-graph
level 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` — same
`new Function` count. Plan 01-12 made NO changes to the polyfill
configuration; this is logged for future tightening (Phase 5
hardening or a dedicated MV3 CSP audit plan), NOT for fix in this
plan per the deviation-rule SCOPE BOUNDARY.
All other pre-checkpoint bundle gates PASS:
- Tier-1 forbidden-strings: 13/13 GREEN (no new test-mode symbols)
- SW-bundle-import: 15/15 GREEN
- Node-globals (Buffer.*) 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 (A1..A14 regression-free + A18..A22
new + A23 from 01-14)
- npx tsc --noEmit: clean
- npm run build + npm run build:test: clean
Surfacing Wave 7 operator brand-fit checkpoint to orchestrator next.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| b909c374cc |
feat(01-12): wave-6 task-1 — harness A18-A22 (font reachability + icon-distinct + manifest-i18n + Lora-resolved + welcome-tokens)
UAT harness extended with 5 new page-side assertions following the 01-13 Approach B pattern (page-side assertA* + host-side driveA* wrapper + harness.test.ts orchestrator entry): A18 — Lora WOFF2 reachable from harness page (font self-host MV3 CSP invariant). Walks document.styleSheets for the first @font-face rule referencing Lora, resolves the rebased asset URL (handles Vite's content-hashing), fetches, asserts byteLength >= 40_000 (subset Lora is ~49 KB) + 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 it matches EN extName 'Mokosh — Session Capture' OR RU 'Mokosh — Запись сессии' (robust to whatever locale Chrome uses); explicitly checks 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 the stack starts with 'Lora' or '"Lora"' (accommodates both quoted + unquoted forms across Chrome versions); explicitly checks no Newsreader leak (R2 substitution complete). 2 checks. A22 — welcome page tokens.css adoption (CONDITIONAL on Plan 01-10). Skip-gate on missing welcome.html: catches both HTTP 404 AND network-layer fetch failure (Chrome extensions throw TypeError 'Failed to fetch' for non-web_accessible_resources paths). On reachable: extracts <link rel=stylesheet> hrefs, fetches each, asserts >= 3 var(--mks-*) usages OR tokens.css reference. 1 check. Companion changes: - 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 via document.styleSheets + getComputedStyle. Vite's crxjs plugin handles the asset path rebasing at build:test time. - tests/uat/lib/harness-page-driver.ts: driveA18..driveA22 wrappers following the established driveA8 pattern (page.evaluate → window. __mokoshHarness.assertXX). No new host-side fs/ffprobe primitives; all A18-A22 work is page-side. - tests/uat/harness.test.ts: orchestrator drivers list extended with A18-A22 between A14 and A23. FORBIDDEN_HOOK_STRINGS UNCHANGED at 12 entries post-Plan-01-14 (A18-A22 use production chrome.* + fetch + getComputedStyle exclusively; no new test-mode symbols). Verification (this commit): - npm run test:uat: 21/21 GREEN (was 16/16 post-01-14) - SKIP_BUILD=1 npm test: 147/147 GREEN - Tier-1 grep gate: 13/13 GREEN (no FORBIDDEN_HOOK_STRINGS growth) - npx tsc --noEmit: clean - npm run build + npm run build:test: both succeed The chain of A1..A14 + A18..A22 + A23 runs in ~95 seconds end-to-end under Puppeteer headless mode against the bundled Chrome at ~/.cache/puppeteer/chrome/linux-148.0.7778.167. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| e8d2881874 |
feat(01-12): wave-5 task-1 — welcome i18n migration (conditional on 01-10) + __VITE_DEV__ define + scripts/README.md
Plan 01-10 (welcome tab) has NOT yet landed at execute-plan time
(verified: ls src/welcome/welcome.html returns absent). Per Wave 5
branch 2B, src/welcome/* file modifications are DEFERRED — when Plan
01-10 lands, its executor will use src/shared/tokens.css directly
(skipping the placeholder welcome-tokens.css step entirely; the
canonical tokens.css is already import-ready from src/shared/).
Unconditional changes in this wave:
1. vite.config.ts gains __VITE_DEV__ define-token (RESEARCH §12 +
D-09 spirit-satisfaction). Defaults to false; activates iff env
var VITE_DEV=1 is set. Reserved for any future inline smoke-mode
check. Currently smoke.sh lives entirely outside Vite's input set
so the gate is a defensive no-op:
define: { __MOKOSH_UAT__: 'false', __VITE_DEV__: JSON.stringify(...) }
2. vite.test.config.ts inherits __VITE_DEV__ via mergeConfig (the
test config only overrides __MOKOSH_UAT__: 'true'; __VITE_DEV__
from base flows through untouched).
3. scripts/README.md (NEW, ~50 lines): documents the smoke-isolation
invariant — dev-only scripts in scripts/ are NOT bundled by
`npm run build`; the production dist/ contains zero smoke
artifacts (verified by RESEARCH §12 grep gate). Provides usage
example for VITE_DEV env override + cross-references RESEARCH §12
and brand-decisions-v1.md D-09. Index lists subset-fonts.sh,
rasterize-icons.sh, and smoke.sh (if present).
Note on Plan 01-10 deferral: when Plan 01-10 executes after this
plan closes, the welcome page src/welcome/welcome.css can either
@import '../shared/tokens.css' directly OR a thin welcome-tokens.css
re-export — both paths are supported by the canonical tokens.css
landed in Wave 1. Plan 01-10's executor must adopt chrome.i18n.getMessage
for any welcome copy strings using the 16-key matrix in _locales/
(welcomeHeroRu + welcomeHeroEn already defined; additional keys
added per Plan 01-10's own artifact list).
Verification:
- vitest baseline 147/147 GREEN (no change from Wave 4 close)
- npm run build clean (no warnings; __VITE_DEV__ propagates through
define static replacement)
- scripts/README.md exists with the smoke-isolation paragraph
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 468f16d7e7 |
feat(01-12): wave-4 task-1 — adopt tokens.css + chrome.i18n.getMessage in src/popup/ + src/background/ (loom palette + RU i18n + en fallback)
src/popup/style.css:
- Adds @import "../shared/tokens.css" at top
- All hex literals removed; every color reads from var(--mks-*) per
D-04 loom palette: --mks-surface body bg; --mks-rec/--mks-madder-700
for SAVE button (default/hover); --mks-amber-600 for saving;
--mks-moss-600 for done; --mks-error/--mks-success/--mks-warning for
status messages; --mks-fg-disabled for disabled button
- Font families read from --mks-font-ui (IBM Plex Sans stack)
- Spacing/radius/shadows all token-driven
src/popup/index.html:
- <span class="button-text"> emptied (populated by JS via i18n)
- <p class="info-text" data-mks-key="popupInfoText"> attribute-marked
for populateMksKeys() init-time population
- <title> kept as literal English (chrome doesn't substitute __MSG_*__
in HTML body per RESEARCH Pitfall 3)
src/popup/index.ts:
- New `i18n(key, fallback)` helper: chrome.i18n.getMessage with explicit
`|| <fallback>` for unit-test contexts without chrome.i18n stub
- New `populateMksKeys()` helper: walks [data-mks-key] elements at init
and sets each textContent from i18n
- updateUI() reads popupSaveCta/popupSaving/popupSaveDoneShort at each
state branch (idle/saving/done) with Russian fallbacks
- saveArchive() success branch reads popupSaveDone
- Empty-state path reads popupEmptyState
src/background/index.ts:
- BADGE_REC_COLOR: '#00C853' → '#b2543d' (= --mks-madder-600 per D-04;
RESEARCH §10 Open Question A7 default-action)
- BADGE_OFF_COLOR + BADGE_ERROR_COLOR retained as engineering choices
(no loom-palette token for material-red/amber-700 equivalents)
- BADGE_REC_TITLE/BADGE_OFF_TITLE/BADGE_ERROR_TITLE renamed to
..._FALLBACK and only referenced at the chrome.i18n.getMessage call
sites inside setBadgeState (i18nMessage('tooltipRecPrefix' etc.))
- New `i18nMessage(key, fallback)` helper mirroring popup's i18n()
- Recovery notification: title=i18nMessage('extName',...); message=
i18nMessage('notifRecovery',...)
- Startup notification: title=i18nMessage('extName',...); message=
i18nMessage('notifStartup',...)
- NOTIF_EXTNAME_FALLBACK/NOTIF_STARTUP_FALLBACK/NOTIF_RECOVERY_FALLBACK
module-level constants for the |||| chain (degrade gracefully in
test contexts without chrome.i18n stub)
- NO `await import(...)` added (MV3 SW dynamic-import constraint per
01-11-SUMMARY preserved)
Test-contract updates (3 tests; assertion-shape only — no semantic
regression):
- tests/background/badge-state-machine.test.ts: greenCalls→recColorCalls
regex updated from /^#00[Cc]853$/ to /^#b2543d$/i lockstep with
BADGE_REC_COLOR change; title-substring assertion widened to
/Recording|recording/i to cover both EN locale + fallback
- tests/background/onstartup-notification.test.ts: title equality
('Mokosh ready') replaced with /Mokosh/i substring assertion
(survives both the 'Mokosh' fallback + 'Mokosh — Session Capture'
resolved EN); message regex widened to /recording|recor|click/i
- tests/background/toolbar-action.test.ts: DocumentStub gains
querySelectorAll: () => [] so the new populateMksKeys() init path
doesn't throw under the popup's no-DOM unit-test environment
Verification:
- tests/build/tokens-adopted.test.ts: 4/4 GREEN (was 2 RED + 2 GREEN)
- tests/build/no-remote-fonts.test.ts: 4/4 GREEN after fresh build
(Vite emits the WOFF2 files as content-hashed dist/assets/*.woff2;
tokens.css references resolve through the asset pipeline; no
remote-font URLs anywhere in dist/)
- Full vitest sweep: 147/147 GREEN (was 145/147)
- npx tsc --noEmit: clean
- Tier-1 grep gate: 13/13 GREEN (no new test-mode symbols)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 110cebc50d |
feat(01-12): wave-3 task-1 — manifest i18n (__MSG_*__ + default_locale='en') + _locales/{en,ru}/messages.json (16 keys; D-07 + D-08 baked in)
manifest.json migrated to chrome i18n placeholders: - name: 'AI Call Recorder' → '__MSG_extName__' - description: 'Запись сессий операторов для диагностики ошибок' → '__MSG_extDesc__' - default_locale: 'en' (new field per RESEARCH §11 + Pitfall 4 fallback chain) - action.default_title: '__MSG_tooltipOff__' (new field per Brief §02 string #1) _locales/en/messages.json + _locales/ru/messages.json each carry the same 16-key matrix per RESEARCH §10 + Brief §02 verbatim + D-07 user override + D-08 tagline: extName, extDesc, tooltipOff, tooltipRecPrefix, tooltipErr, popupSavePrompt, popupSaveCta, popupSaveDone, popupSaving, popupSaveDoneShort, popupEmptyState, popupInfoText, notifStartup, notifRecovery, welcomeHeroRu, welcomeHeroEn Canonical values (per brand-decisions-v1.md + RESEARCH §10 Brief §02): - EN extName = 'Mokosh — Session Capture' (D-07 user override of A) - EN extDesc = 'Thirty seconds ago, always at hand.' (D-08 tagline) - RU extName = 'Mokosh — Запись сессии' - RU extDesc = 'Тридцать секунд назад, всегда под рукой.' NB on key count: the artifact-table 12-key baseline excluded the three Wave-4 deltas (popupSaving, popupSaveDoneShort, popupEmptyState) which are introduced now to avoid a re-locale-parity flap when Wave 4 lands. popupInfoText is present in BOTH locales (the plan-checker flag 1 informational scope-slip is corrected here — see deviation block below). Plan success-criteria reading: ≥12 keys with Wave 4 additive deltas accounted for. Each key carries both `message` and `description` per Chrome i18n schema. EN descriptions are translator-facing; RU descriptions are plain-Russian context for native operators. npm run build emits: - dist/manifest.json carries the i18n shape verbatim (crxjs preserves __MSG_* placeholders; Chrome's manifest loader resolves them at install) - dist/_locales/en/messages.json (3.30 KB) - dist/_locales/ru/messages.json (3.81 KB) Verification: - tests/i18n/manifest-i18n.test.ts: 10/10 GREEN - tests/i18n/locale-parity.test.ts: 4/4 GREEN (en↔ru parity, non-empty messages, 16 keys each) - tests/build/no-remote-fonts.test.ts: 4/4 GREEN (post-build dist/ has zero remote font URLs) - Full vitest sweep: 145/147 GREEN (2 RED remaining are popup/style.css tokens-adopted cases — Wave 4 work) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 7732a302cd |
feat(01-12): wave-2 task-1 — rasterize Loom mark to icons/icon{16,48,128}.png (overwrites Bug A placeholders)
scripts/rasterize-icons.sh (80 lines) is the one-off rasterization recipe. It reads src/shared/brand/mokosh-mark.svg and emits the three toolbar icon sizes via rsvg-convert, with assets-spec.md size FLOOR sanity checks embedded. Before: icons/icon16.png 574 B 16-bit/color RGB (Bug A placeholder) icons/icon48.png 1153 B 16-bit/color RGB (Bug A placeholder) icons/icon128.png 2615 B 16-bit/color RGB (Bug A placeholder) After: icons/icon16.png 406 B 8-bit/color RGBA (Loom mark — D-01) icons/icon48.png 784 B 8-bit/color RGBA (Loom mark — D-01) icons/icon128.png 1952 B 8-bit/color RGBA (Loom mark — D-01) All three clear assets-spec.md Chrome imageUtil silent-rejection floors (16≥200B, 48≥500B, 128≥1024B). Sizes match RESEARCH §3 verification. Per D-06 (Neutral mark + dynamic badge): single neutral mark per size; no per-state PNG sets. The dynamic chrome.action.setBadgeBackgroundColor in src/background/index.ts does state communication via colored badge. Per RESEARCH §3 anti-pattern: PNGs are COMMITTED as static artifacts; not regenerated at build time. scripts/rasterize-icons.sh is the documented re-run recipe (for when src/shared/brand/mokosh-mark.svg changes). Verification: - tests/build/icons-present.test.ts: 15/15 GREEN (existence, FLOOR, PNG signature, dimensions, color-type byte === 6 RGBA) - Bug A regression check: file content differs from prior placeholders (verified via git diff binary status); the placeholder fingerprint used by harness A19 (Wave 6) will distinguish on first 32 bytes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| abab6e1f59 |
feat(01-12): wave-1 task-2 — canonical src/shared/tokens.css (R2 Lora substitution + .mks-word + local @font-face block)
src/shared/tokens.css lands as the canonical token system — engineering
working copy of .planning/intel/design-incoming/system/bundle/mokosh-handoff/
tokens.css with three surgical edits per Plan 01-12:
1. Handoff's PREVIEW-ONLY Google Fonts @import (line 12) REMOVED +
replaced with 8 local @font-face rules pointing at ./fonts/*.woff2
(Lora normal + italic variable; Plex Sans Regular/Medium/SemiBold/Bold;
Plex Mono Regular/Medium). MV3 CSP self-host enforced
(style-src 'self' + font-src 'self').
2. --mks-font-display VALUE substituted from "Newsreader" to "Lora" per
R2 designer reply 2026-05-19 (Cyrillic coverage; brand-decisions-v1-
followup-display-font.md). The Lora foundry note is preserved in the
Type-section comment. ZERO Newsreader references remain anywhere in
the file (verified by grep).
3. .mks-word class added at end-of-file with the {font-family,font-size,
font-weight,letter-spacing,fill} declarations from the lockup SVG's
class="mks-word" usage. Required by mokosh-lockup.svg line 21 per
RESEARCH §8.
src/shared/brand/ engineering working copies of:
- mokosh-mark.svg (Loom 2×2 weave intersection at 32×32 viewBox)
- mokosh-lockup.svg (mark + Mokosh wordmark at 240×56 viewBox)
The intel/ design-incoming/ copies remain unchanged as the original
handoff source-of-truth.
Verification:
- googleapis count: 0 (MV3 CSP self-host invariant)
- Newsreader count: 0 (R2 substitution complete)
- @font-face count: 8 (Lora normal + italic + Plex Sans ×4 + Plex Mono ×2)
- .mks-word: 1 definition (referenced by mokosh-lockup.svg)
- Lora references: 13 (font-family stack + @font-face + comments)
- tests/build/fonts-present.test.ts: 10/10 GREEN
- tests/build/tokens-adopted.test.ts case (a): GREEN (tokens.css exists)
- tests/build/tokens-adopted.test.ts cases (b)(c): still RED (Wave 4 work)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| f86fd60d4a |
feat(01-12): wave-1 task-1 — self-host OFL font bundle (Lora + Plex Sans + Plex Mono; R2 designer reply 2026-05-19)
Self-hosted WOFF2 bundle lands at src/shared/fonts/ per D-05 typography
pairing with R2 Newsreader→Lora substitution (designer reply 2026-05-19).
Bundle composition (8 WOFF2 files; ~236 KB total):
- Lora-VariableFont.woff2 (49 KB) — display family, normal style, wght
axis 400-700; Cyreal foundry (cyrealtype/Lora-Cyrillic main branch)
- Lora-Italic-VariableFont.woff2 (53 KB) — display family italic, wght
400-700; separate variable file per upstream layout (A5 verified at
execute time: Lora-Cyrillic ships italic as its own variable file).
- IBMPlexSans-{Regular,Medium,SemiBold,Bold}.woff2 (24/25/25/23 KB) —
UI body family with Latin + Cyrillic basic
- IBMPlexMono-{Regular,Medium}.woff2 (15 KB each) — diagnostic / timer
family
Companion artifacts:
- LICENSE-Lora.txt — verbatim OFL.txt + Lora Project Authors copyright
- LICENSE-IBM-Plex.txt — verbatim LICENSE.txt + IBM Corp. copyright
- README.md — substantive (160 lines): bundle table, R2 rationale,
subset coverage (Cyrillic basic + supplements + №), regeneration recipe
with one-off curl commands, MV3 CSP self-host rationale.
scripts/subset-fonts.sh (130 lines):
- One-off subsetting recipe; takes a scratch dir of upstream TTFs.
- UNICODES range: U+0020-007E + U+00A0-00FF + Cyrillic basic
(U+0400-045F) + Ukrainian (Ґґ) + Kazakh (Ұұ) + № sign.
- Common pyftsubset flags shared across faces; per-face subset_face
helper. Documents source URLs in usage block.
Bundle is sufficient for the 12 i18n keys (Wave 3) + welcome hero
(Plan 01-10 conditional) per the Brief §02 Russian copy specified in
.planning/intel/brand-decisions-v1.md.
Verification: tests/build/fonts-present.test.ts is 9/10 GREEN (1 RED
remaining is the tokens.css existence check, which is Wave 1 Task 2's
job). Existing 100/100 vitest baseline preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 34a9ce10d4 |
test(01-12): wave-0 — scaffold RED unit tests (tokens / fonts / icons / no-remote-fonts / manifest-i18n / locale-parity)
Wave 0 of the design-integration plan. Six new test files at tests/build/ and tests/i18n/ pin the contracts that later waves will GREEN: - tokens-adopted.test.ts (4 cases): src/shared/tokens.css exists + parses; src/popup/style.css @imports it; popup/style.css has zero hex literals; welcome.css conditional check. - fonts-present.test.ts: 7 required WOFF2 faces (Lora normal + Plex Sans ×4 + Plex Mono ×2) + LICENSE-Lora + LICENSE-IBM-Plex + README + optional Lora-Italic (A5 verify-at-execute). - icons-present.test.ts (15 cases across 3 sizes): existence, size FLOOR per assets-spec.md, PNG signature, dimensions, color-type byte === 6 (RGBA — RED until Wave 2 rsvg-convert overwrites the 16-bit-RGB placeholders). - no-remote-fonts.test.ts: production dist/ contains zero fonts.googleapis.com / https://fonts / googleapis substrings (MV3 CSP self-host invariant T-01-12-01). - manifest-i18n.test.ts (10 cases): manifest:name === '__MSG_extName__', :description === '__MSG_extDesc__', :default_locale === 'en', :action.default_title === '__MSG_tooltipOff__'; _locales/{en,ru}/ messages.json carry D-07 + D-08 canonical strings. - locale-parity.test.ts (4 cases): ru→en parity, en→ru symmetric, non-empty .message strings (RESEARCH Pitfall 4 mitigation). Current polarity: 29 RED + 18 GREEN across the 6 new files (placeholders already clear dim+size floors; no-remote-fonts vacuous-GREEN since tokens.css doesn't yet exist with remote URLs). Existing 100/100 vitest baseline preserved (verified SKIP_BUILD=1 npx vitest run). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 3fe018beb9 |
fix(01-12): revise plan baselines per Plan 01-14 landing (vitest 98→100, UAT 15→16, FORBIDDEN_HOOK_STRINGS 10→12)
Surgical amendment — Plan 01-12 is unexecuted; safe for in-place revision. Plan 01-14 landed ( |
|||
| 9792c0f6c3 |
docs(01-14): state + roadmap + requirements — Plan 01-14 closure
- STATE.md: advance plan counter, update progress (12/14 = 86%), record metric (Plan 01-14: 49m, 1 task, 7 files), add decision, record session. - ROADMAP.md: update phase-01 progress table (plan_count=14, summary_count=12). - REQUIREMENTS.md: mark REQ-video-ring-buffer complete (final closure for the Phase-01 video-pipeline charter). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 52541452e0 |
docs(01-14): summary — picker enhancement landed (16/16 UAT GREEN, 100/100 vitest GREEN)
Canonical closure artifact for Plan 01-14: - monitorTypeSurfaces:'include' shipped (W3C spec §6.1; Chrome >= 119 picker narrowing) - A23 harness regression gate wired into the UAT orchestrator (post-A14 chain) - Tier-1 grep gate inventory extended in lockstep (12 forbidden strings; was 10) Single atomic feat commit ( |
|||
| b467123578 |
feat(01-14): monitorTypeSurfaces:'include' — narrow picker to monitor surfaces only
[per Plan 01-14; closes B-01-14-01 via Step 1b lockstep]
- src/offscreen/recorder.ts: add monitorTypeSurfaces:'include' as top-level
DisplayMediaStreamOptions sibling of video: (W3C Screen Capture spec §6.1;
Chrome >= 119; removes tab/window panes from the operator's picker per
Plan 01-10 RESEARCH §5 + §Pitfall-5 recommendation). Typed widening cast
extended in lockstep to keep the explicit-typing contract (no `as any`).
D-15 post-grant validation block at recorder.ts:294 UNCHANGED — belt
(picker narrowing) + suspenders (post-grant tear-down) chain preserved.
- tests/offscreen/display-surface-constraint.test.ts: lockstep update of
the strict-deep-equality assertion at lines 223-226 with the same key
ordering as the source change (video -> monitorTypeSurfaces -> audio).
toHaveBeenCalledWith contract preserved (NO expect.objectContaining —
the test author's "catches future drops of ANY field" discipline is
honored). This edit + the source change land in the SAME commit so the
98/98 baseline never crosses a commit boundary in RED state.
- src/test-hooks/offscreen-hooks.ts: capture last constraints object in
module-scoped `lastGetDisplayMediaConstraints` cell (was `_constraints`
received-but-unused; renamed to `constraints`); add `get-last-getDisplayMedia-constraints`
bridge op to the __mokoshOffscreenQuery dispatcher between
get-display-surface and get-segment-count. Defensive try/catch mirrors
the existing dispatcher pattern; the cell is module-internal so the
MokoshTestSurface cross-cast in types.ts requires NO change (decision
documented inline in offscreen-hooks.ts).
- tests/uat/extension-page-harness.ts: add `assertA23` mirroring `assertA3`
(bridge query → 2-check AssertionResult: non-null constraints + value).
Extend the `Window.__mokoshHarness` declaration + runtime export + status
bar text + console.log to reference A23.
- tests/uat/lib/harness-page-driver.ts: export `driveA23(page)` mirroring
the `driveA14` page.evaluate wrapper shape. Standard read-only driver.
- tests/uat/harness.test.ts: extend FORBIDDEN_HOOK_STRINGS (line 85) with
`lastGetDisplayMediaConstraints` and `get-last-getDisplayMedia-constraints`.
Import driveA23. Append `{ name: 'A23', drive: driveA23 }` to the drivers
array after the A14 entry. Update header comment + orchestrator stdout
to reflect A14 + A23 chain. The `Total = drivers.length + 1` arithmetic
adapts automatically: 14 + 1 = 15 → 15 + 1 = 16.
- tests/background/no-test-hooks-in-prod-bundle.test.ts: lockstep
extension of FORBIDDEN_HOOK_STRINGS (line 105) with the same 2 strings.
Header comment updated to "Total: 12 surface strings." (was 10).
Confirms production `dist/` has ZERO occurrences after `npm run build`
via the `__MOKOSH_UAT__` dead-branch tree-shake (T-01-14-04 mitigation).
D-01 (whole-desktop only via getDisplayMedia; reject window/tab surfaces) is
the design intent that monitorTypeSurfaces:'include' realizes at the picker-
UI level. D-15 post-grant validation (recorder.ts:294-307) remains the
actual enforcement against managed-policy/DevTools/older-Chrome overrides.
Verification chain (per Plan 01-14 §verify; clean post-commit):
- `npx tsc --noEmit` exit 0
- `npm run build` exit 0; dist/ produced, monitorTypeSurfaces ships in
the offscreen chunk as the operator-facing picker hint
- `npm run build:test` exit 0; dist-test/ produced with the harness
hooks intact (gated)
- `npm test` 100/100 GREEN (was 98/98; +2 via the 2 new FORBIDDEN_HOOK_STRINGS
parametrized tests — both PASS, production bundle hook-free)
- `npm run test:uat` 16/16 GREEN (15 → 16 via A23). A23 reads constraints
`{video: {...}, monitorTypeSurfaces: 'include', audio: false}` from the
fakeGetDisplayMedia capture cell — round-trips through the full call site.
- Production bundle spot-check:
`grep -rc 'lastGetDisplayMediaConstraints\|get-last-getDisplayMedia-constraints' dist/ | grep -v ':0$'`
→ empty (all `:0` filtered) → ZERO leakage.
References:
- W3C Screen Capture §6.1 DisplayMediaStreamOptions:
https://www.w3.org/TR/screen-capture/#dom-displaymediastreamoptions-monitortypesurfaces
- Chrome screen-sharing-controls (Chrome 119+):
https://developer.chrome.com/docs/web-platform/screen-sharing-controls
- Plan 01-10 RESEARCH §5 + §Pitfall-5 (recommendation provenance):
.planning/phases/01-stabilize-video-pipeline/01-10-RESEARCH.md
- Architectural-note (replaces retired AMENDMENT-A.md improvisation per
01-11-SUMMARY): canonical GSD ceremony — plan → checker (B-01-14-01)
→ executor → SUMMARY (this commit).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 433ee280f3 |
fix(01-14): revise plan per checker — include test-expectation update (B-01-14-01)
Plan-checker BLOCKER B-01-14-01: original plan's must_haves truth #5 understated baseline regression risk. Adding `monitorTypeSurfaces: 'include'` as a sibling constraint in src/offscreen/recorder.ts would have dropped vitest from 98/98 GREEN to 97/98 RED because tests/offscreen/display-surface-constraint.test.ts Test 1 (line 223-226) uses strict deep-equality (toHaveBeenCalledWith, NOT expect.objectContaining) on the constraints object — the test author's intent (comment at line 221-222) is to catch future drops of ANY field. Surgical revision per references/planner-revision.md (surgeon-not-architect): - Frontmatter: add tests/offscreen/display-surface-constraint.test.ts to files_modified list. - must_haves truth #5: replace the "no existing unit test references the constraints object" claim with a positive statement that the strict-deep- equality assertion at lines 223-226 is updated in lockstep; preserves the test author's "no objectContaining" discipline; explicit no-transient-RED guarantee across commit boundaries. - must_haves artifacts: new entry for the test file documenting the in-place edit shape and the preserved test author comment. - must_haves key_links: new link entry pairing the test assertion with the source call site under the lockstep contract. - Interfaces block: add the explicit "test-expectation lockstep update" code fragment with the chosen key ordering (video → monitorTypeSurfaces → audio) so the executor lands the source change and the test update with matching shapes. - Task 1 <files>: add tests/offscreen/display-surface-constraint.test.ts. - Task 1 <action>: insert new Step 1b between Step 1 (source change) and Step 2 (offscreen-hooks bridge) — full single-line edit spec at lines 223-226, preserve toHaveBeenCalledWith contract, preserve comment block, same-commit guarantee. - Task 1 verify-block expected outputs: explicitly call out that 98/98 GREEN is preserved BECAUSE Step 1b lands (without it, 97/98 RED on the strict- deep-equality assertion). - Task 1 <done>: add line covering the lockstep test update + the no-transient- RED guarantee. - <verification> phase gate: add new check #2 (test-expectation lockstep) between source-line correctness and A23 round-trip. - <success_criteria>: add bullet for the lockstep test-expectation update. - <output> SUMMARY contract: add "Revision linkage" bullet documenting that the plan was revised once after the plan-checker flagged B-01-14-01. Untouched (per checker's preserve-verbatim list): - Source-line target (src/offscreen/recorder.ts:270) - Harness wiring references (assertA3 686, driveA14 987, __mokoshHarness 1922+1942, drivers array 289-312, Total comment 354) - FORBIDDEN_HOOK_STRINGS lockstep contract (both inventories) - `_constraints` capture path - Scope discipline (still 1 task, autonomous, no checkpoint) - Research traceability (Plan 01-10 RESEARCH §5 + §Pitfall-5 + W3C §6.1) - Threat model (T-01-14-04 mirrors Plan 01-13) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 41c1f7e82f |
feat(01-14): plan — monitorTypeSurfaces picker enhancement (canonical post-closure scope)
Plan 01-14 ships W3C Screen Capture monitorTypeSurfaces: 'include' (Chrome
119+) on the offscreen getDisplayMedia call, plus an A23 harness regression
assertion that verifies the constraint reaches the call site via the
existing offscreen-hooks bridge.
Scope: 1 source line + A23 wiring + Tier-1 grep gate inventory update
(lockstep extension of unit-gate + UAT A0 FORBIDDEN_HOOK_STRINGS).
Autonomous, single executor; no operator empirical checkpoint (UAT 16/16
harness coverage suffices per feedback-pre-checkpoint-bundle-gates.md).
Canonical sources:
- Plan 01-10 RESEARCH section 5 ('monitorTypeSurfaces: include' recommendation)
- Plan 01-10 RESEARCH section Pitfall-5 ('Misinterpreting displaySurface
as a hard constraint' — monitorTypeSurfaces is the picker-UI complement
to D-15's post-grant validation)
- W3C Screen Capture spec section 6.1 DisplayMediaStreamOptions
- developer.chrome.com/docs/web-platform/screen-sharing-controls
Decisions honored:
- D-01 (whole-desktop only via getDisplayMedia; reject window/tab) — the
new constraint is the picker-UI realization of D-01's intent.
- D-15 (post-grant displaySurface validation) — UNCHANGED; remains the
enforcement (this plan is belt-and-suspenders at the picker UI level).
Ceremony note: this plan replaces the prior AMENDMENT-A.md improvisation
path retired per 01-11-SUMMARY Architectural Notes. Canonical GSD ceremony
(plan -> checker -> executor -> SUMMARY).
Validations:
- gsd-sdk frontmatter.validate -> valid: true (8/8 required fields).
- gsd-sdk verify.plan-structure -> valid: true (1 task; hasFiles/hasAction
/hasVerify/hasDone all true).
- ROADMAP.md Phase 1 plans list extended with 01-14 entry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 4d828f1080 |
docs(01-10): install-flow + auto-select research — both asks INFEASIBLE
Researcher (gsd-phase-researcher) returned HIGH-confidence verdicts on the
12-area brief from .continue-here.md:
Ask 1 (install-time auto-start): INFEASIBLE in unmanaged Chrome.
W3C Screen Capture spec §5.1 mandates transient user activation;
chrome.runtime.onInstalled confers none. Floor: 2 clicks
(toolbar/welcome-page → Share button on picker). Enterprise policy
ScreenCaptureWithoutGestureAllowedForOrigins exists (Chrome+Edge ≥ 123)
but only applies to managed-Chrome contexts with extension URL
whitelisted — does NOT apply to Load-Unpacked deployment; deferred-idea.
Ask 2 (auto-select desktop / skip picker): INFEASIBLE in unmanaged Chrome.
W3C spec mandates user MUST choose every time. displaySurface:'monitor'
is a hint (already applied src/offscreen/recorder.ts:270).
chooseDesktopMedia doesn't auto-accept on single-monitor setups AND
streamId not usable in MV3 offscreen documents (Chrome DevRel position).
Primary recommendation: KEEP Plan 01-10's current informational CTA
charter (commit
|
|||
| 6a29ae4124 |
chore(01): resume work — consume HANDOFF.json + ignore dist-archives
- Delete .planning/HANDOFF.json (one-shot artifact per resume-project workflow) - Add dist-archives/ to .gitignore (from prior session's distribution-zip build) - Bump STATE.md Session Continuity to reflect resumed session + next action (install-flow + auto-select researcher spawn) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| cecefc61f9 |
wip: phase-01 paused — .continue-here.md handoff (pairs with c60b887 HANDOFF.json)
Human-readable handoff. Captures: - 3 BLOCKING CONSTRAINTS from saved memory (scope reduction, GSD ceremony, pre-checkpoint gates) - 3 anti-patterns from this session (improvised artifact types, claiming canonical without verifying, save-stops UX cycle) - Current state + working tree + test/build baseline - Next-session order of operations (10 steps) - Required reading order - Pending researcher brief (12 areas; was 529-blocked) - Infrastructure state + API capacity note - Followup backlog Resume: /clear → /gsd-resume-work Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| c60b8878df |
wip: phase-01 paused — Plan 01-13 closed; 01-10 + 01-12 plans ready; researcher pending
Session arc: - Plan 01-13 UAT harness: closed at |
|||
| e035fd279d |
docs(01-09): Amendment 3 + 01-13 SUMMARY reversal note + STATE.md sync + debug records
Plan 01-09 Amendment 3 (2026-05-19) — atomic documentation pass for the save-does-not-stop-recording charter reversal. Changes: - .planning/phases/01-stabilize-video-pipeline/01-09-PLAN.md: Amendment 3 block added above <success_criteria> (mirrors Amendment 2 placement). Describes the reversed charter, references the new debug record, points at the inverted test file + harness A14. - .planning/phases/01-stabilize-video-pipeline/01-13-SUMMARY.md: "Subsequent Reversal (2026-05-19)" footer added. Notes that npm run test:uat still 15/15 GREEN under the inverted A14 contract; vitest baseline preserved at 98 GREEN. - .planning/STATE.md: Plan 01-13 closure block extended with CHARTER REVERSAL bullet citing the 4 commit SHAs ( |
|||
| 1baaf45702 |
feat(01-13-A14-invert): A14 — invert to assert continuous-recording post-SAVE
Plan 01-09 Amendment 3 (2026-05-19) end-to-end lock. Inverted A14 to
match the reversed charter (SAVE creates zip, recording continues).
Page-side (tests/uat/extension-page-harness.ts):
- assertA14: assert badge==='REC' (was ''), popup endsWith
'src/popup/index.html' (was ''), no-new-recovery-notif (unchanged).
- A14 name + check labels updated to reflect continuous-recording semantic.
- New constant A14_POPUP_HTML_SUFFIX for the popup endsWith check
(ext-id-agnostic via suffix match).
- A13 docstring + diag strings refreshed: setupFreshRecording is now
defensive (orthogonal to A12 ordering) rather than a workaround for
the prior auto-stop. 11s settle preserved (same wall-clock cost).
Host-side (tests/uat/lib/harness-page-driver.ts):
- driveA14 docstring refreshed to mention Amendment 3 + the inverted
contract; mechanical wrapper unchanged.
Verification:
- npm run test:uat: 15/15 GREEN
- A14 actual output:
badge='REC'
popup='chrome-extension://<ext-id>/src/popup/index.html'
recoveryDelta=0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 7645765401 |
feat(01-09-no-stop): GREEN — remove SAVE_ARCHIVE finally block; recording continues
Plan 01-09 Amendment 3 (2026-05-19) — code surgery to drive tests/background/save-archive-does-not-stop-recording.test.ts from RED (commit |
|||
| 6ac23fdbd8 |
test(01-09-no-stop): RED — invert save-archive contract to lock always-on charter
Per operator UX iteration (2026-05-19), the Amendment 2 save-stops-recording
fix (commits cd83eb0+4f4c3e2+2b6c24b+89f3337) is REVERSED. SAVE_ARCHIVE
creates a new zip but does NOT stop the recorder — matches SPEC's
continuous-capture / always-on safety-net framing.
This commit renames the test file via `git mv` (history preserved) and
inverts tests A..C to assert the new contract:
- A: no NEW setBadgeText({text:''}) call (badge stays REC)
- B: no setPopup({popup:''}) call (popup stays pinned to popup.html)
- C: no STOP_RECORDING dispatch via chrome.runtime.sendMessage
Test D (no recovery notification) preserved unchanged as regression guard.
RED expected — src/background/index.ts still has the Amendment 2
`finally` block dispatching STOP_RECORDING + setIdleMode. Next commit
removes that block to drive GREEN.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 8d1c8fb0cc |
docs(01-12): create Plan 01-12 (Design Integration; R2 Lora unblocks; 7 waves)
Final designer reply received 2026-05-19 unblocks Plan 01-12: R2
substitution — replace Newsreader with Lora (OFL, Cyreal foundry, full
Cyrillic-Latin parity, variable wght 400-700). All 9 brand decisions
now resolved; R2 displaces Newsreader from `--mks-font-display`.
Plan structure: 7 waves, 10 tasks.
- Wave 0 (TDD scaffolds): 6 RED unit tests — tokens-adopted,
fonts-present, icons-present, no-remote-fonts, manifest-i18n,
locale-parity. Each RED until its corresponding artifact wave lands.
- Wave 1: Self-host OFL font bundle (Lora variable normal + italic,
Plex Sans ×4, Plex Mono ×2) at src/shared/fonts/ via pyftsubset
(Latin + Cyrillic basic subset); land src/shared/tokens.css canonical
(Google Fonts @import → 7 local @font-face rules; Newsreader → Lora
per R2; .mks-word class added per RESEARCH §8 + lockup SVG line 21).
- Wave 2: Rasterize Loom mark to icons/icon{16,48,128}.png via
rsvg-convert; overwrite Bug A placeholders; 8-bit RGBA at all sizes.
- Wave 3: Land _locales/{en,ru}/messages.json (12 keys: 8 Brief §02
operator strings + 4 supporting keys); manifest.json → __MSG_extName__
+ __MSG_extDesc__ + default_locale 'en' + action.default_title.
extName='Mokosh — Session Capture' per D-07 user override; extDesc per
D-08 brand-decisions-v1.md wording.
- Wave 4: src/popup/ + src/background/ adopt tokens.css (loom palette)
+ chrome.i18n.getMessage at every operator-facing copy site; replace
hex literals with var(--mks-*) references; BADGE_REC_COLOR madder
'#b2543d' (= --mks-madder-600 per D-04 + RESEARCH §10 Open Q A7).
- Wave 5: Welcome page conditional migration (if 01-10 landed, swap
welcome-tokens.css → @import canonical tokens.css; migrate copy.ts
shim to chrome.i18n.getMessage fallback); add __VITE_DEV__ define
per RESEARCH §12 D-09 spirit; scripts/README.md smoke-isolation note.
- Wave 6: UAT harness A18-A22 (font reachability via document.styleSheets
walk + fetch + byteLength; icon-not-placeholder via fingerprint diff;
manifest:name === 'Mokosh — Session Capture'; --mks-font-display
resolves to Lora via getComputedStyle; welcome tokens loaded
conditional on 01-10). Tier-1 forbidden-strings UNCHANGED at 10.
- Wave 7: Operator empirical brand-fit checkpoint (last Phase 1 gate);
SUMMARY + STATE.md + ROADMAP.md sync.
ROADMAP.md Phase 1 plan list extended from 7 → 13 entries (gap noted in
01-13 SUMMARY's known-limitations now closed).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 3a530c2334 |
docs(01-10): rewrite plan in place — D-02/D-08/D-17-onboarding charter + design-swap-in-ready arch + harness A15+A16+A17
Drops the 2026-05-17 draft (zero commits, zero SUMMARY — virgin). Carries: - onInstalled flag-gated welcome tab (3 RED→GREEN unit tests; storage-key contract pinned per prior I-02 fix) - 5 new src/welcome/* files: welcome.html + welcome.ts + welcome.css + welcome-tokens.css + copy.ts - Design-swap-in-ready: every color via var(--mks-*); every string via COPY map; every font via var(--mks-font-*) with system fallback - vite.config.ts + vite.test.config.ts both gain welcome rollup input - manifest.json gains web_accessible_resources for welcome.html - Harness extended A15+A16+A17 (onboarding flag observability + no-re-open settle + design-swap invariant); Tier-1 forbidden-strings inventory unchanged at 10 - 4 autonomous tasks + 1 operator empirical checkpoint Deletes: REQUEST_PERMISSIONS flow (gone in 01-09); duplicate Start button (D-16-toolbar owns start path); start-path divergence Cites: D-17-onboarding (CONTEXT.md L537+), D-02 (welcome layout), D-08 (tagline), D-03 (voice register), D-16-toolbar (start ownership), brand-decisions-v1-followup-display-font.md (Plan 01-12 blocker; this plan ships TODAY with placeholders) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 285e46f620 |
docs(01-13): close — operator UAT ack 2026-05-19 + save-stops debug resolved + SUMMARY landed
Plan 01-13 fully closed. Operator UAT acked "all good" on 2026-05-19; recovery flow (A7) + restart-after-click (A2) both harness-covered, no manual verification needed. What this commit lands: - 01-13-SUMMARY.md (full spike-pivot-then-implementation narrative; tracks all 16 commits across Plan 01-11 spike + Plan 01-13 4-wave execution + save-stops debug session; documents 15/15 npm run test:uat GREEN + 98/98 vitest GREEN + Bug A/B regression-rewind demos verified) - Save-stops debug record moved to .planning/debug/resolved/ (closure- canonical location; prior inline path tracked via git mv) - STATE.md sync: completed_plans 11→12, percent 95→96, Plan 01-13 fully-closed narrative + save-stops debug session captured Phase 1 functional contract: CLOSED via harness PASS. Remaining Phase 1 gates: Plan 01-10 (welcome tab) + Plan 01-12 (design integration; pending designer Newsreader-Cyrillic reply). Phase 2 inherits the harness as its closure-gate template. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 89f3337334 |
docs(01-09-save-stops): debug record — RED → GREEN → A14 evidence + closure notes
Updates .planning/debug/01-09-save-stops-recording.md with: - status awaiting_human_verify - SHA references (red_commit |
|||
| 2b6c24b2d9 |
feat(01-13): A14 — post-SAVE state check (badge='', popup='', no new recovery notif)
Plan 01-13 Task 9 closure for operator empirical UAT bug
.planning/debug/01-09-save-stops-recording.md. Adds the harness
assertion that empirically verifies the SAVE-auto-stops-recording fix
(committed at
|
|||
| 4f4c3e2241 |
feat(01-09-save-stops): GREEN — SAVE_ARCHIVE auto-stops recording per SPEC one-shot intent
Operator UAT closure for Plan 01-13 Task 9. Patches saveArchive() in
src/background/index.ts with a `finally` block that dispatches
STOP_RECORDING to offscreen (mirrors the existing START_RECORDING
control-plane channel via chrome.runtime.sendMessage), flips
isRecording=false, and calls setIdleMode() — applied to BOTH the
success and empty-buffer-error paths.
Operator UX contract: SAVE click ALWAYS stops the session, regardless of
internal success/empty-buffer outcome. The badge clears, the popup
empties (re-enabling chrome.action.onClicked for restart), and Chrome's
sharing banner closes via the offscreen recorder's stopRecording()
(which nulls mediaStream + stops all tracks + clears the rotation
timer — line 527 of src/offscreen/recorder.ts, already wired since
Plan 01-05).
Trade-off documented inline: empty-buffer path still surfaces a
recovery notification (the catch branch emits RECORDING_ERROR{
error:'empty-video-buffer'} → SW's own onMessage handler runs
setErrorMode + creates a mokosh-recovery-* notif). The finally block
then setIdleMode()'s, so the FINAL visible state is OFF/empty-popup —
clean restart path. The notification stays visible briefly so the
operator sees that something went wrong, then clicks it to start a
new session.
Test count: 94 GREEN (baseline) → 98 GREEN (+4 from
tests/background/save-archive-stops-recording.test.ts).
Files modified:
- src/background/index.ts (saveArchive + finally block; no
PortMessage/Message type changes — STOP_RECORDING already in
MessageType per src/shared/types.ts:14, offscreen handler at
recorder.ts:848 already wired)
Toolchain:
- npx tsc --noEmit: exit 0 (no type errors)
- npm run build: exit 0 (dist/ clean rebuild)
Debug record: .planning/debug/01-09-save-stops-recording.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|