Record version. v0.1
Date. 2026-06-26
Change Request. CR-2026-125 — Companion surface Phase 1 (wire the focus capability into the frontend, template-clean, TopBar tokenized) — v0.1.
Status. Executed on branch cr-2026-125-companion-surface-phase1 (loomworks frontend, off main bafdb07) through Step 6; Vitest green at every step; next build compiles clean; both guards green. NOT pushed — halt before push, awaiting Operator authorization.
Executing agent. Claude Code on DUNIN7-M4.
Operator. Marvin Percival.
Grounded by. CR-2026-125 v0.1 + the Step-0 grounding/findings; the merged Engine focus capability (loomworks-engine cfeebd0 — CR-2026-123 spine + CR-2026-124 spoken intents).
The Engine already knew "where you are" — say "open Goosey" and your unqualified commands land in Goosey — but the screen showed none of it. This wires the two together: a top-bar chip reads here: 7 · Goosey · E0007 (your number · name · E-label, never the UUID); clicking a project opens it and sets focus as one act; saying "open X" / "close" moves focus and the chip follows. It is built template-clean — every colour is a design token, every brand word lives in strings.ts — with a guard test that fails the build if a guarded file introduces a raw colour or a hardcoded brand word. The shared top bar (TopBar) was itself tokenized, because the chip lives in it and a template's top bar must be swappable.
Branch cr-2026-125-companion-surface-phase1 off main bafdb07. Not pushed. Frontend-only; no Engine change.
| Commit | Step | Change |
|---|---|---|
| 9b795ee | 0 | Archive CR as branch base (baseline bafdb07, Vitest 470/62). |
| 2b52d2d | 1 | lib/api/me-current-engagement.ts — getCurrentEngagement / setCurrentEngagement({engagementId}\|{handle}) / clearCurrentEngagement; typed SetFocusResult mapping 404→not_found, 409→ambiguous (candidates), 401→unauthorized; added to the vocabulary-wall allowlist. |
| fdeb5a0 | 2 | CurrentEngagementProvider — GET-on-mount focus context; setFocus/clearFocus refetch GET on ok (GET-on-change); mounted in layout.tsx inside AuthProvider. |
| 346ba75 | 3 | Tokenize TopBar — 4 alpha-modifier conversions on existing tokens + 2 new tokens (--color-vellum-soft, --color-gold) wired into @theme; verified both render sites; Tailwind v4 compiles every class. |
| 263ee4f | 4 | FocusChip (the here: N · Name · E#### chip, hidden off-focus, never UUID) in TopBar + unified open (focus-set beside router.push, additive/non-blocking) + FOCUS string group. |
| a7d659a | 5 | Spoken open/close wired (useRefreshFocusOnIntent on the converse surfaces) + the template-cleanliness guard (tests/components/template-clean.test.ts). |
Vitest trajectory: baseline 470 → S1 479 → S2 484 → S3 489 → S4 494 → S5 498 passed / 69 files, 0 failed. next build compiles clean (TypeScript + Tailwind, all 18 routes prerender). No schema/Engine change.
Loomworks/Companion in the guarded files; no general JSX-text scanner (over-broad). String-registration stays a scope+review discipline.--color-vellum-soft #f0eadb and --color-gold #b58c28 — preserving the exact look while making them swappable.CurrentEngagementDetail (with current_engagement_id) would itself trip the vocabulary-wall in types.ts (engagement_id substring). The adapter therefore projects the snake_case wire shape into camelCase Operator-vocab types in types.ts (the projects.ts pattern), confining the wire fields to the allowlisted adapter. Same for the 404/409 detail: FastAPI's {detail: …} envelope is unwrapped in the adapter.useCurrentEngagementOptional (mirrors useAuthOptional) — the chip, the engagement list, and the converse surfaces use it so they degrade to a no-op outside the provider (139 existing isolation tests would otherwise throw). The chip is correctly a no-op without focus context.strings.ts to pass guard (b): Loomworks → BRAND.productName; Speak to Companion → NAV.voiceSpeakLabel (sourced from BRAND.defaultCompanionName, so a rename follows; static for Phase 1, per-Operator dynamic name is ready-not-active). Guard scope is an includelist (this CR's files + TopBar); EngagementList's pre-existing hex and other files' brand strings are deliberately out of scope (the repo-wide colour sweep is CR-2026-126).useRefreshFocusOnIntent only refetches focus on an open/close intent so the chip follows. The FOCUS.ambiguous/notFound strings are available for any frontend-initiated focus set (the endpoint path); the converse path uses the Engine's message.
| Walk step | Covered by |
|---|---|
| 1. click an engagement → navigates AND chip shows here: N·Name·E#### | UnifiedOpen.test (push + focus PUT {engagement_id}); FocusChip.test (chip labels) |
| 2. "open Goosey" → chip updates | RefreshFocusOnIntent.test (open_engagement refetches focus) |
| 3. unqualified command → lands in focus | Engine-side (CR-2026-123/124, merged + DB-walked); frontend reflects via the provider |
| 4. "open <duplicate>" → spoken candidates; focus unchanged | me-current-engagement.test (409→ambiguous w/ candidates); CurrentEngagementProvider.test (ambiguous leaves focus) |
| 5. "open <nonexistent>" → not-found; focus unchanged | me-current-engagement.test (404→not_found) |
| 6. "close" → chip clears; next unqualified → asks | FocusChip.test (close→clearFocus); CurrentEngagementProvider.test (clearFocus); Engine asks (CR-2026-124) |
| 7. guards green; TopBar unchanged both surfaces | template-clean + vocabulary-wall green; next build compiles; TopBar 0 hex/rgba; both surfaces render (InEngagementSkeleton + EngagementNavigationPage suites green) |
TopBar render-identical: the alpha conversions are mathematically exact (press-ink = the same base token at 14/28/58%; the two new tokens resolve to the exact original hex), and Tailwind v4.3.1 compiles every class — verified by direct compile, not just trusted. Manual pixel-diff on a running app is the one check not automatable in jsdom; the math + compile make it identical by construction.
9b795ee..a7d659a) is not pushed. On review and explicit authorization, merge to main and push.NAV.voiceSpeakLabel is static (brand default) for Phase 1; sourcing the per-Operator companion name is the ready-not-active extension.cr-2026-125-companion-surface-phase1 at a7d659a, 6 commits ahead of main bafdb07. Not pushed. Working tree clean (.dev.vars untracked + .next/ build output, both ignored).cfeebd0).4aa11f2); the no-hardcoded-CSS standing note + CR-2026-126 (3e25fa5); this completion record. (Record commits unpushed pending Operator authorization.)DUNIN7 — Done In Seven LLC — Miami, Florida Completion Record — Companion surface Phase 1 — v0.1 — 2026-06-26 CR-2026-125 v0.1