DUNIN7 · Loomworks · Completion Record
Plain-language summary
The Companion now has a server-side memory of "the engagement you're in right now." Each person's small record (host_account) gained one line — current_engagement_id — beside the "Personal engagement" line that already worked the same way. The conversation path reads it: when a turn names no project, the scope defaults to the focused engagement; when nothing is focused and nothing is named, the Companion asks rather than guessing Personal. A focus-set operation writes it on "open X," a unified resolver turns any handle (your number for it, its name, its E-label, a UUID, or "Personal") into the engagement, and a "where am I" read says where you are by name + your number + E-label.
This CR was re-scoped once, mid-build, and the re-scope is recorded here rather than smoothed. v0.2 framed Step 4 as "only the number lookup is new — reuse an existing name resolver and an existing 'open X' handler." Execution found that false: the cited name resolver runs the wrong direction (id→name), and there is no "open X" intent to wire into. The build halted, the Operator chose pause-and-revise, and the work split: v0.3 (this record) builds the engine handle-resolution and the spine; the spoken "open X" classifier intent is sibling CR-2026-124.
At Step 4 the v0.2 plan met the codebase and two of its three "reuse" claims were false against engine 59c8249:
engagement/redirect.py:103 _resolve_engagement_name as reusable name resolution. That function is id→name — it reads the Seed's engagement name to label a cross-engagement redirect. "open Goosey" needs name→id, which existed nowhere.IntentLabel carried 23 intents, none open/navigate/switch. The only by-number "open" was a frontend list-expansion shorthand with no engine path.So two of three handles were new construction, not reuse — the "imagined state encoded as fact" Finding ② warns against, surfacing one step late. The build halted at the Step 4 boundary (Steps 1–3 already committed and green). A redraft handoff was filed (cr-drafting-handoffs/…step-4-redraft-handoff-v0_1.md), the match-policy questions were answered against live data, and CR v0.3 re-scoped Step 4 to engine resolution only, splitting the spoken intent to sibling CR-2026-124. All name-match and collision policies are settled, grounded decisions (exact / case-insensitive / person-scoped; duplicate title = ask-don't-guess; "open 0" = Personal).
Cleared against candidate seed v0.12 (silent on current-engagement focus / converse-path addressing) and architecture spec v0.6 (the §08 spine reconciliation: surface signals posture when a surface signals; the carried current engagement supplies scope when none does; ask-don't-guess at the null boundary). The authentication framework is untouched — current_engagement_id is a focus pointer, resolution never keys on email. Addressing vocabulary (name + per-Operator number + E####; UUID invisible) follows the engagement-identity record.
Branch cr-2026-123-current-engagement-spine off engine main 59c8249. Not pushed.
| Commit | Step | Change |
|---|---|---|
0b1279e | 0 | Archive CR v0.2 as branch base (baseline main 59c8249, suite 2940/46, alembic 0094). |
c88b30c | 1 | host_account.current_engagement_id — nullable FK clone of personal_engagement_id; migration 0095 (down_revision 0094); up adds column + FK, down drops both; up/down/up clean. |
419b180 | 2 | Converse scope defaults to focus when project_id omitted; threaded through verify/context/history/classifier/route. Null boundary asks; no Personal fallback. current_engagement_id on the HostAccount view. +3 tests. |
000396e | 3 | PUT /me/current-engagement set/clear + set_current_engagement seam; reuses verify_project_membership; close (null) clears. +4 tests. |
99e5d89 | — | Archive CR v0.3 (Step 4 re-scope) onto the branch. |
1f97230 | 4 | handle_resolver.py resolve_engagement_handle — delegate Personal/"0"/E####/UUID to resolve_engagement_address first, then bare-int → number lookup (person-scoped), else name lookup (exact / case-insensitive / person-scoped). Duplicate title → AmbiguousEngagementHandleError with candidates. Wired into the endpoint (handle precedence; 404 unknown; 409 ambiguous). +16 tests. |
05e3392 | 5 | GET /me/current-engagement — "where am I": name + per-Operator number + E####; null focus → not-in-an-engagement, no guessed Personal. +3 tests. |
Suite trajectory: baseline 2940 → Step 2 2943 → Step 3 2947 → Step 4 2963 → Step 5 2966 passed, 0 failed, 46 skipped. API docs regenerated at Steps 3/4/5; freshness CI guard green at each. Every commit lands green; per-step reversibility holds.
0094, unmoved — grounding held; the new migration's down_revision is cleanly 0094. Migration 0095 added; up/down/up clean on playground_dev; test DB migrated to 0095.engagement_id = body.project_id or host_account.current_engagement_id. project_id callers byte-for-byte unaffected (every existing test has null focus). Null boundary asks; personal_engagement_id is never a routing scope (no silent Personal fallback).PUT /me/current-engagement per the /me/* convention. Reused verify_project_membership; no new auth surface. CR-2026-124's "open X" reuses the same set_current_engagement seam.uq_memberships_person_operator_sequence_number (UNIQUE per person — at most one). Name exact/case-insensitive/person-scoped on free-text title. Duplicate title → ambiguous error with candidates (id + E#### + number); never silent first-match. Personal/"0"/E####/UUID delegated, not re-implemented; "open 0" → Personal. No 4-digit assumption; identifier collapse not pre-adopted.GET /me/current-engagement, symmetric with the PUT (the Step 5(a) default).host_account.current_engagement_id) + its FK.playground_dev, non-destructive (rolled back)All eight passed, exercised at the resolution + endpoint level (the spoken "open X" utterance walk belongs to CR-2026-124):
project_id default None (asks, no Personal fallback) — PASSproject_id → wins over focus — PASS(The converse-default halves of steps 1/7/8 are also pinned by the committed Step 2 integration tests, green against playground_test.)
Halt before push — Operator authorization required
The branch (7 commits, 0b1279e..05e3392) is not pushed. On the Operator's review and explicit authorization, push CR-2026-123 (merge --no-ff to engine main per convention). Until then nothing is on origin.
resolve_engagement_handle → set_current_engagement. It inherits the duplicate-title disambiguation surface (the spoken "did you mean A or B?", which v0.3 surfaces as a 409 / typed error). v0.3 + CR-2026-124 = the full voice "open X" acceptance walk.host_account row as a later, separate CR. Not built.api.loomworks.dunin7.com is not served (deferred per the spine manifest). 0095 runs when production is stood up, as part of standing it up.cr-2026-123-current-engagement-spine at 05e3392, 7 commits ahead of main 59c8249. Not pushed. Working tree clean. dev + test DBs at alembic 0095.change-requests/), the Step-4 redraft handoff (cr-drafting-handoffs/, filed at 24d6bb3), and this completion record. Architecture spec v0.6 already filed and pushed (3d43996).