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

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

Version 0.1  ·  Date 2026-06-29
Repo loomworks-engine (+ OL loomworks for the tray UI)  ·  Base main @ d32c99b (rebase onto post-CR-2026-120 head)
Room Memory — Pipeline-walk Finding 2 of 3
Author Claude.ai  ·  Executor Claude Code  ·  Operator Marvin Percival
Depends on CR-2026-120 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's no way to list 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.

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

show held items  → your held drafts, each with a stable number
commit 1 / commit all  → save to durable memory
change 1 to June 27  → correct a held draft in place; or, if the number names a saved fact, supersede it (original stays in the record)
discard 2  → turn a held draft away (recorded, not erased)

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 commit'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.
  2. Three net-new classifier intents + handlers, each thin, each using the resolver and an existing data-layer function:
    • show_held — list held assertions with display_numbers as structured data (not prose) so the conversation can render an addressable tray.
    • discard_held ("discard N"/"drop N") — resolve → existing discard_assertion (append-only, human-gated).
    • change_assertion ("change N to X") — resolve, then branch on state: held → revise_assertion in place (frictionless); committed → supersede with a confirm step ("this corrects a saved fact — original stays in the record — apply?"), proceed only on Operator yes (consequential, durable memory changing).
  3. Natural-language routing. Route "save 1"/"save that"/"ok save"/"commit 1" → commit; a bare affirmative in held-tray context → commit the addressed item. This removes the core burden: the grammar works when the Operator talks normally. The broken approve_draft→card-queue path is routed around, not repaired; mark deprecated-by-this-change (no deletion).
  4. Truthful confirmations. "Saved 1" only if commit succeeded; "Updated 1" only if revise wrote; "Discarded 2" only if discard wrote. No "Logged" without a write.

OL (frontend)

  1. In-conversation held tray. Render the structured held list as a numbered tray in the Companion stream (copy HeldCard/HeldNoteCard). Extend ConverseResponse (currently companion_message: string only) to carry a held-list array. Route the "What's held for me?" chip here instead of to a prose bubble.

Out of scope — residue, recorded

  • Deleting the deprecated approve_draft card queue — routed around here, removed in later cleanup.
  • The compositions.py:73 relabel (CR-2026-120 residue).
  • System-wide false-success audit beyond these four motions (Finding 3 proper).

Step-0 inspect-first (mandatory)

Rebase onto post-CR-2026-120 head first. Then confirm at current HEAD, halting on divergence:

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

Execution steps

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

  1. Shared resolver. Extract commit's by_number into one held-reference resolver; repoint commit to it (no behavior change, regression-locked). Commit. Suite green.
  2. show_held + structured response. Intent + handler returns held assertions with display_numbers as structured data; extend ConverseResponse. Commit. Suite green.
  3. discard_held. Intent + handler → resolver → discard_assertion. Human-gated. Truthful confirmation. Commit. Suite green.
  4. change_assertion, held branch. Handler → resolver → revise_assertion in place, frictionless. Commit. Suite green.
  5. change_assertion, committed branch. Committed target → confirm-step → supersede on yes (prior preserved, wasRevisionOf set). Reuse existing two-turn confirm. Commit. Suite green.
  6. Natural-language routing. "save N"/"save that"/"ok save" → commit; bare affirmative in tray context → commit addressed item. Mark approve_draft deprecated. Commit. Suite green.
  7. OL conversation tray. Render numbered tray in the stream (copy HeldCard); route "What's held for me?" chip here. Frontend tests. Commit. Suite green.
  8. Tests. Each intent resolves by display_number; held-change edits in place; committed-change supersedes-with-confirm + preserves prior; discard recorded; natural save phrasing routes to commit; confirmations gated on real writes. Commit. Suite green.

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

Verification — runtime, both probes

Restart dev after merge.

E0089 — clean loop, the headline test

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

E0060 — committed-revision

  1. With a committed assertion present, "change N to X" on its number → supersession confirm ("original stays in the record — apply?"); on "yes" → new version commits, prior preserved and still visible.
  2. Bare "yes" with a tray in view commits the addressed item; "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