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:
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.
The grounding walk found three-quarters of this already in the substrate:
display_number — the stable, short, per-engagement handle. Assigned at held-creation (not commit), preserved across revise/discard/commit/retract. E0060's held items are 1–5 now. This is the "stable IDs that don't renumber when one leaves" the Operator asked for — already built.commit N / commit all — fully wired, resolving by display_number, committing as the human. One of four motions, working today. The reference implementation the others mirror.revise_assertion and discard_assertion — both exist, both append-only/recorded (discard writes a discarded version + event, never hard-deletes; revise supersedes with wasRevisionOf, prior preserved). They lack only a by-number conversational route.HeldCard (Memory room) and HeldNoteCard (Dashboard) already render "Held N" lists. The conversation tray copies this; it is not designed from nothing.change N on a committed fact supersedes (new version, prior stays, wasRevisionOf link). Discard is recorded, not erased. Aligned — enforced, not bypassed.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.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).approve_draft→card-queue path is routed around, not repaired; mark deprecated-by-this-change (no deletion).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.approve_draft card queue — routed around here, removed in later cleanup.compositions.py:73 relabel (CR-2026-120 residue).Rebase onto post-CR-2026-120 head first. Then confirm at current HEAD, halting on divergence:
_route_commit_assertion / _parse_held_reference (router.py:298-340, 380, 456-468) — the resolver to generalize.revise_assertion (assertions.py:312) + discard_assertion (assertions.py:458) — signatures, append-only, human-gated.display_number assign-at-add (assertions.py:124-163) + preservation — confirm E0060 held items still 1–5.IntentLabel + intent_classifier.md — confirm show_held/change/discard absent, commit present.ConverseResponse shape (OL lib/types.ts:142-160) + HeldCard/HeldNoteCard to copy.Report the chosen home for the shared resolver and the confirm-step mechanism before editing.
Stage by explicit path. Per-step commits, suite green at each. Push is separately authorized — do not push until the Operator says so.
by_number into one held-reference resolver; repoint commit to it (no behavior change, regression-locked). Commit. Suite green.show_held + structured response. Intent + handler returns held assertions with display_numbers as structured data; extend ConverseResponse. Commit. Suite green.discard_held. Intent + handler → resolver → discard_assertion. Human-gated. Truthful confirmation. Commit. Suite green.change_assertion, held branch. Handler → resolver → revise_assertion in place, frictionless. Commit. Suite green.change_assertion, committed branch. Committed target → confirm-step → supersede on yes (prior preserved, wasRevisionOf set). Reuse existing two-turn confirm. Commit. Suite green.approve_draft deprecated. Commit. Suite green.HALT. Report terminal SHA + suite count. Await push authorization.
Restart dev after merge.
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.
revise_assertion, never an overwrite.