11.5.19 The exclusion list is keyed by KEY, not the internal row id
Move the CLI's PERSISTED exclusion list off the internal row id and onto the key, so no getWorkItem read has to supply an identifier /api/v1 will never publish. Transport-agnostic — it ships against the MCP tools that exist today.
⚠️ RE-SCOPED 2026-08-07, mid-run, on a verified finding. Retiring
nextReadymoved to 11.5.5.This card originally also retired
next_readyin favour oflistReady+items[0]. That is not safely doable here, and the reason was not visible when the card was written:
list_ready's row already carriesstatus/type/executor/id(presentMcpReadyRow→readyRowFields,lib/mcp/payloads/workItems.ts:355), so classification is fine.- It does NOT carry
targetRepo— that is only on the DISPATCH row (presentMcpReadyDispatch, same file) — andcommands/auto.ts:325needstargetRepotoresolveDispatchTargetbefore it canrepos.ensure(target).- The prompt does carry it, but
dispatchOnefetches the prompt withsessionBranch: repo?.branch ?? null(auto.ts:419) — which needs the repo, which needs the target. Fetching it earlier means seeding a session branch for the no-checkout case that today seedsnull, andsessionBranchis a fallback the server may act on.That is a real behaviour change under a byte-identical acceptance bar. It belongs in 11.5.5, which owns
dispatchPromptand the loop and can reorder both together.
What this card does
packages/cli/src/sessionExcludes.ts — ExcludeEntry becomes { key }. addExclude / removeExclude take a key; removeExcludeByKey collapses into removeExclude. Reading a file written by the previous CLI is naturally compatible (the old { id, key } entries already carry key); the read NORMALIZES to { key } so the id is not written forward.
packages/cli/src/commands/dispatch.ts — DeliverInput.id goes away; deliver excludes by the key it already has. detail.item.id stops being read — this is the read that blocks 11.5.4.
packages/cli/src/commands/auto.ts · batch.ts — the PERSISTED set is keys. The in-run excludeIds set that next_ready still takes is seeded EMPTY and grows as the loop sees items: when next_ready hands back an item whose key is persisted-excluded, the loop adds that item's id (which the dispatch row carries) and asks again. One extra round trip per persisted exclusion, once, and no behaviour change in what gets dispatched.
Scope BOUNDARY
The exclusion store and its callers. It does NOT retire nextReady or change what next_ready is asked for beyond the seeding above (11.5.5). It does NOT port any method to /api/v1. It does NOT change render.ts, any flag, or any output string.
Acceptance criteria
packages/cli/src/commands/dispatch.tsno longer readsitem.idfrom agetWorkItemdetail — the read that blocks 11.5.4. Asserted by a grep-shaped test over the source.- The persisted exclusion file contains only keys after any write — asserted by reading the file back.
- A file written by the PREVIOUS CLI (entries carrying both
idandkey) is read without error and still holds its items out; after the next write it carries keys only. - An exclusion recorded on a failed run still holds the item out of the next selection, and is cleared on success — driven through the real loop, not by calling the store directly.
motir next,motir run <key>,motir autoandmotir batchproduce byte-identical output to their pre-card behaviour for the same underlying data.motir donestill clears an exclusion by key.- The per-file coverage floor (≥90%) holds on every modified file.
Context refs
docs/decisions/public-api-conventions.mdAmendment 10 Q3 — exclusion by key, and the migration it names.packages/cli/src/sessionExcludes.ts:31–45,125–160—ExcludeEntry,addExclude,removeExclude,removeExcludeByKey.packages/cli/src/commands/dispatch.ts:112–120,173,195,248,312—DeliverInput.idand bothdelivercall sites.packages/cli/src/commands/auto.ts:260,307,314,352,353,359·commands/batch.ts:111,116,117,131,249,256— the exclude reads and the in-run id set.lib/mcp/payloads/workItems.ts:355,374,383— whylist_readycannot replacenext_readyhere.- Story: 11.5.