Record version. v0.1
Date. 2026-06-26
Change Request. CR-2026-124 — Spoken "open X" and "close" intents — v0.2 (2026-06-26).
Status. Executed on branch cr-2026-124-open-engagement-intent through Step 3; suite green at every step; the 8-step spoken-loop database-truth walk passed; the anti-theft loop was exercised with a live key. NOT pushed — halt before push, awaiting Operator authorization.
Executing agent. Claude Code on DUNIN7-M4.
Operator. Marvin Percival.
Grounded by. CR-2026-124 v0.2 (Step 0 §2, confirmed-live); the merged CR-2026-123 spine (engine 6f785f9, alembic 0095) supplying resolve_engagement_handle + set_current_engagement.
This is the spoken half of the current-engagement spine. CR-2026-123 made the engine able to resolve an engagement handle and set it as your current focus, but only through a direct endpoint call. This CR teaches the Companion the spoken triggers: say "open Goosey" (or "open 7", or "open E0007") and it takes you into that project; say "close" and it clears where you are. With both CRs merged, the full loop works — say "open Goosey," then "commit held 14" lands in Goosey without re-naming it, then "close" and the next unqualified instruction makes the Companion ask which project.
v0.2 folded a spoken "close" in alongside "open X" (your decision), so this CR adds two intents, not one. The risk in touching the live classifier is intent theft — a new intent capturing utterances that should mean something else, especially the common words in the "close" family ("close", "done", "leave"). That was checked empirically with a live Anthropic key: the two new intents classify correctly and a sample of eight existing intents still classify as themselves. No tuning was needed.
Branch cr-2026-124-open-engagement-intent off the CR-2026-123 merge 6f785f9. Not pushed. No schema change (classifier-only).
| Commit | Step | Change |
|---|---|---|
| 4a1a129 | 0 | Archive CR v0.2 as branch base (baseline 6f785f9, suite 2966/46, alembic 0095). |
| 5e5d89a | 1 | open_engagement + close_engagement added to IntentLabel (23 → 25); two classifier blocks + the open_engagement PARAMETER EXTRACTION line in intent_classifier.md (the close block carries a disambiguation note vs orient and vs a confirmation turn); two instruction .md files; both enumeration guards bumped 23 → 25. |
| d104c88 | 2 | _route_open_engagement (resolve handle → set focus → confirm; ambiguous-before-miss catch order) + _route_close_engagement (clear focus) + dispatch branches in route_intent; helpers _engagement_display_name / _ambiguous_open_message. All replies server-composed. |
Suite: baseline 2966 → Step 1 2966/63 → Step 2 2973 passed, 0 failed, 63 skipped. The skipped count is 46 baseline + 17 new LLM-classification tests (@skip_no_llm_key) that skip in the keyless gate. No schema change → no migration.
The classification + anti-theft tests (tests/test_cr_2026_124_open_close_intents.py) are LLM-dependent (the established test_classifier.py pattern: @skip_no_llm_key). They skip in the keyless suite, so they were run directly against a live Anthropic key (from the repo .env) to exercise the anti-theft loop the CR anticipated:
open_engagement, with engagement_reference extracted ("Goosey" / "7" / "E0007").close_engagement.commit_assertion; "The deadline is March 15th." → add_knowledge; "Save this filter as 'urgent across all'." → save_filter; "Less blur." → tune_setting; "I want to start a new project." → create_project; "I need to download the report." → request_download; "What can I do here?" → orient; "What did I decide about the budget?" → ask_about_past_input. All held — neither new intent (incl. the common-word "close" family) stole them.17/17 passed with the key. No tuning halt was needed (the expected "close"-collision halt did not fire).
IntentLabel members: open_engagement, close_engagement (23 → 25). Enumeration guards bumped at tests/test_phase_54_seed_commit_from_brief.py (~587) and tests/test_phase_55_engagement_creation_assistance.py:144.orient ("what's next") and from a confirmation turn.test_open_duplicate_title_ambiguous_before_miss) — the ambiguous error subclasses the plain miss, so reversing the order would swallow every duplicate-title as "not found"; it is caught first.set_current_engagement(None); the next unqualified turn then asks (the Decision-2 boundary, inherited from CR-2026-123, not re-implemented).playground_dev, rolled backAll passed:
project_id, converse-default → focus) lands in Goosey — PASS (the spine + voice end to end).no_project (asks), nothing lands in Personal — PASS4a1a129..d104c88) is not pushed. On your review and explicit authorization, push CR-2026-124 (merge --no-ff to engine main). With both CRs pushed, the full spoken open/close loop is live.cr-2026-124-open-engagement-intent at d104c88, 3 commits ahead of main 6f785f9. Not pushed. Working tree clean. No schema change (alembic stays 0095).change-requests/) and this completion record. The CR-2026-123 spine it depends on is already merged and pushed.DUNIN7 — Done In Seven LLC — Miami, Florida Completion Record — Spoken open/close intents — v0.1 — 2026-06-26 CR-2026-124 v0.2