DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path change-requests/cr-2026-127-addressable-held-tray-grammar-v0_1.md

CR-2026-127 — Addressable Held-Tray Grammar + Committed-Revision — v0_1

Version. 0.1 Date. 2026-06-29 Repo. loomworks-engine (+ OL loomworks for the conversation tray UI) Branch base. main @ d32c99b (rebase onto post-CR-2026-120 head before starting) Author. Claude.ai. Executor. Claude Code (DUNIN7-M4). Operator. Marvin Percival. Room. Memory — Pipeline-walk Finding 2 of 3. Depends on. CR-2026-120 (actor-kind read contract) merged + verified — held items must render before a tray grammar over them is usable.


Plain-language summary

Today you can speak a note and it becomes a held draft, but you cannot reliably save, change, or remove it by talking normally. The only working save phrase is "commit held N" — which no one would think to say — and there is no way at all to list your held items in conversation, change one, or discard one by reference. "Save" and "yes" route to a queue that never sees your draft, so nothing commits and the Companion sometimes says "Logged" when nothing was logged.

This CR builds one consistent grammar over an addressable held tray:

One grammar: verb + number. You name the target; the system acts on exactly that. No magic phrases, no guessing which draft you mean, no false success.


What already exists (reused, not rebuilt)

The grounding walk found three-quarters of this already in the substrate:


Seed alignment


In scope

Engine

  1. Shared held-reference resolver. Generalize _route_commit_assertion's by_number map (router.py:456-463) into one helper: held-reference (number | "all") → assertion(s), keyed on display_number. All four motions call it. Single source of truth for number→assertion resolution.
  1. Three net-new classifier intents + router handlers, each thin, each using the shared resolver and an existing data-layer function:
  1. Natural-language routing. The classifier must route natural save/commit phrasing — "save 1," "save that," "ok save it," "commit 1" — to the commit path, and a bare affirmative ("yes" / "ok") when a held tray is the active context to commit the addressed item(s). This closes the core burden: the grammar must work when the Operator talks normally, not only on exact phrases. The broken approve_draft→card-queue path is routed around, not repaired; mark it deprecated-by-this-change (do not delete in this CR).
  1. Truthful confirmations. Each motion's reply is gated on the engine's confirmed result: "Saved 1" only if commit succeeded; "Updated 1" only if revise wrote; "Discarded 2" only if discard wrote; the committed-revision confirm only claims supersession after it lands. No "Logged" without a write.

OL (frontend)

  1. In-conversation held tray. When show_held (or a held-context reply) returns structured held data, render the numbered tray inside the Companion conversation stream, copying the HeldCard/HeldNoteCard pattern. ConverseResponse must carry a structured held-list array (currently companion_message: string only — extend it). The "What's held for me?" chip routes here instead of to a prose bubble.

Out of scope (residue, recorded)


Step-0 inspect-first (mandatory)

Rebase onto the post-CR-2026-120 head first. Then CC reads and confirms at current HEAD, halting on any divergence:

Report the chosen home for the shared resolver and the confirm-step mechanism before editing.


Execution steps (halt-before-push; suite green at each)

Stage by explicit path. Per-step commits. Push is separately authorized — do not push until the Operator says so.

Step 1 — Shared resolver. Extract commit's by_number logic into one held-reference resolver. Repoint commit_assertion to use it (no behavior change — regression-locked by existing commit tests). Commit. Suite green.

Step 2 — show_held intent + structured response. Add show_held to IntentLabel + classifier; handler returns held assertions with display_number as structured data; extend ConverseResponse to carry the held-list array. Commit. Suite green.

Step 3 — discard_held intent. Add intent + handler → resolver → discard_assertion. Human-gated. Truthful confirmation. Commit. Suite green.

Step 4 — change_assertion intent, held branch. Add intent + handler → resolver → revise_assertion for held items, in place, frictionless. Commit. Suite green.

Step 5 — change_assertion, committed branch (supersession + confirm). Extend the handler: committed target → confirm-step → supersede on yes (prior preserved, wasRevisionOf set). Reuse the existing two-turn confirm mechanism. Commit. Suite green.

Step 6 — Natural-language routing. Classifier routes "save N"/"save that"/"ok save" → commit; bare affirmative in held-tray context → commit addressed item. Truthful confirmations across all motions. Mark approve_draft deprecated-by-this-change (comment, no deletion). Commit. Suite green.

Step 7 — OL conversation tray. Render the structured held list as a numbered tray in the Companion stream (copy HeldCard pattern); route the "What's held for me?" chip here. Frontend tests. Commit. Suite green.

Step 8 — Tests. Engine: each intent resolves by display_number; held-change edits in place; committed-change supersedes-with-confirm and preserves prior; discard is recorded; natural save phrasing routes to commit; confirmations gated on real writes (no false success). Commit. Suite green.

HALT. Report terminal SHA + suite count. Await push authorization.


Verification (runtime — both probes)

Restart dev after merge. Then:

E0089 (clean loop, the headline test):

  1. Speak two notes ("Fenwick watered June 28", "Fenwick moved to balcony"). Say "show held items" → tray shows 1 and 2 with content.
  2. "change 1 to June 27" → item 1 updates in place; item 2 untouched. (This is the Operator's exact three-line example — it must work.)
  3. "commit 1" → item 1 saves; confirm Memory room shows it committed; confirm "Saved 1" only appeared because it actually committed.
  4. "discard 2" → item 2 turned away; confirm it's recorded (Memory shows discarded, not vanished), not in the held tray.
  5. "commit all" when multiple held → all save.

E0060 (committed-revision):

  1. With a committed assertion present, "change N to X" on its number → Companion surfaces the supersession confirm ("original stays in the record — apply?"); on "yes" → new version commits, prior preserved and still visible as trajectory.
  2. Confirm a bare "yes" with a tray in view commits the addressed item; confirm "save 1" (natural phrasing) reaches commit.

Pass condition: the three-line example works end to end on E0089; commit/discard/change-held are addressable and truthful; committed-revision supersedes-with-confirm and preserves the prior; natural save phrasing reaches commit. Only then does Room 1 advance to Finding 3.


Risk notes