11.5.13 Decision, then implement — does a `/api/v1` collection row embed a minimal ACTOR object? (the ready row's assignee, `targetRepo`, and `excludeIds` without an internal id)
⚠️ Filed and corrected mid-run on 2026-08-06, twice, before any code was written. Both corrections are recorded on MOTIR-2280 and in
notes.html#229 / #230.(1) Its premise was wrong. The card originally claimed
readyItemSchemahad "narrowed away" three fields the way 11.7's three were — an unreasoned omission by a mapper. False for two of the three:lib/api/v1/ready/schema.ts:31–45carries a "What is deliberately NOT on the row" block namingassignee.name,assignee.avatarUrlandtargetRepoexplicitly, with reasons. The card cited the mapper and never read the comment fifteen lines above it.(2) It was filed in the wrong place — an epic-direct task wired
blocked_byinto two subtasks in two different stories, which made bothvalid: false. It now lives here, inside 11.5, as an ordinary subtask: the gap was found by 11.5's own run, so 11.5 owns closing it. (A brief detour created a story for it; that was over-structure for one decision card and has been archived.)
The CLI regression is real and unchanged. What changed is that fixing it is a DECISION against a recorded one, not a widening of an oversight.
On the number. Numbered 11.5.13 because it was added last, but its place in the DAG is before 11.5.4 — the
blocked_byedge is what orders it, not the digit.
The recorded decision, verbatim
// • `assignee.avatarUrl` / `name` — a public API must not acquire a second,
// accidental user resource. The id is what a client can act on (it is what
// 11.2's PATCH takes back); the display fields are the web app's.
// • `runCommand` / `contextRefs` / `sessionBranch` / `targetRepo` — those live
// on `ReadyItemDispatchDto`, the payload for Motir's OWN CLI dispatch path.
// They encode assumptions about a local checkout that a third-party
// integration does not share…
Both are defensible. Neither was written with a consumer in front of it. Now there is one.
The observed consequence (verified origin/main @ 6d472611)
renderReadyTable prints it.assignee?.name ?? 'unassigned' (packages/cli/src/render.ts:255). MCP's ReadyItemDto carries assignee: { id, name, avatarUrl } | null (lib/dto/ready.ts:56); the v1 row carries assigneeId only. Every assigned row in motir ready would read unassigned. 11.5.4 is blocked on this.
MOTIR-2229 (11.6.3) hits the same wall from the MCP side — re-basing list_ready onto readyItemSchema as it stands would REMOVE assignee from the MCP payload. There is deliberately no dependency edge to it: list_ready / next_ready / claim_next_ready are already on that card's exemption list, so nothing is broken while this is outstanding, and a cross-story edge would invalidate 11.6. It is a comment on that card instead.
Q1 — does a v1 collection row embed a minimal ACTOR object? (decision + implementation)
The real question, and bigger than this row: assigneeId alone means no client can render a work-item list without a user-directory endpoint v1 does not have. That is not a CLI quirk; it is true of any integration that draws a table.
Check rung 1 before deciding — do NOT take the prior below as verified. The expectation is that every mirror product embeds a minimal actor on issue rows: GitHub (assignee: { login, id, avatar_url }), GitLab (assignees: [{ name, username }]), Jira (fields.assignee.displayName). If that holds, the recorded rationale is out of step with every product this API is measured against, and rung 1 outranks a local preference on a product question.
Note the recorded reason is "a second, accidental user resource" — and an embedded, minimal, read-only actor is not a user resource: it has no endpoint, no collection, no expansion, and cannot be queried. Say so explicitly if that is the conclusion, because it is the sentence the original decision was missing.
If Q1 decides YES: add assignee: { id, name } | null to readyItemSchema (from the DTO the route already reads — no new query), amend the schema's own comment so it no longer says the opposite, record it in the ADR as the rule for EVERY v1 collection row, and bump V1_CONTRACT_VERSION's MINOR. avatarUrl stays off unless a terminal or a mirror justifies it.
If Q1 decides NO: then motir ready's ASSIGNEE column cannot survive the migration, and this card says so — which makes it a change to 11.5's byte-identical promise, decided here rather than degraded silently in 11.5.4.
Q2 — targetRepo stays OFF the public row (decision only, no code)
The recorded reason holds and this card should CONFIRM it rather than overturn it: targetRepo is dispatch metadata for Motir's own checkout-bound path, and 11.7 already ships GET /api/v1/work-items/{key}/dispatch-prompt carrying it. motir batch only needs it for its printed plan snapshot (batch.ts:135), and it already reads dispatch.targetRepo for the actual work (batch.ts:343).
So the CLI reads it from the dispatch prompt, and the public ready row keeps its shape. Record that in the ADR beside Q1 and note it on 11.5.4 — no schema change here.
Q3 — how a key-addressed collection answers "not these" (decision, no code here)
id is absent by ADR §7 and must stay absent. next_ready has no v1 endpoint and 11.5.4 makes it a client-side pick over the ready page, so excludeIds stops being a server argument and the CLI can filter by KEY — its exclude file already stores { id, key } (packages/cli/src/sessionExcludes.ts). Verify that, including an exclude file written by an older CLI, and record the corollary in the ADR so the next client does not re-derive it. The CLI-side change itself belongs to 11.5.4.
Scope BOUNDARY
Ends at the ADR decisions plus whatever schema change Q1 licenses. ONE repo, motir-core. It does NOT change the CLI — every consequence lands in 11.5.4. It does NOT touch any other v1 resource's schema, and it does NOT add a user endpoint (that is the thing the original rationale was right to fear).
⚠️ This is the ONE card in 11.5 that touches the server, against that story's "does NOT change the server" boundary — which was written when 11.7 was open and could own such gaps. 11.7 is done, so gaps 11.5's own run finds are 11.5's to close. The story's boundary carries an amendment saying so.
Acceptance criteria
- The ADR records Q1 as a decision with its rung-1 evidence CITED (the actual shape each mirror returns, not a claim that they do), its rejected alternatives, and — if YES — the rule stated for every v1 collection row, not just this one.
lib/api/v1/ready/schema.ts's "deliberately NOT on the row" comment agrees with the ADR afterwards. A schema whose comment contradicts the decision is the defect this card exists to stop repeating.- If Q1 is YES:
readyItemSchemacarries the assignee's display name, from the page already read with no per-row query — asserted by counting queries;renderReadyTableproduces byte-identical output from a v1 page and the equivalent MCP page, asserted by driving the REAL renderer with both; the field appears in the emitted document and the reference view model;V1_CONTRACT_VERSION's MINOR is bumped in the same PR. - If Q1 is NO: the card ships the ADR entry alone, amends 11.5's acceptance criteria for the column it gives up, and changes no schema.
- Q2 is recorded as a CONFIRMATION of the existing rationale, naming the dispatch-prompt endpoint as where a CLI reads
targetRepo. - Q3 is recorded as a §7 corollary, and an exclude file written by an older CLI is shown to still work — asserted with a fixture of the old shape.
- Cross-tenant and scope behaviour on the ready route is unchanged, asserted.
- The per-file coverage floor (≥90%) holds on every changed file.
Context refs
lib/api/v1/ready/schema.ts:31–45— read this block first; it is what the first draft missed.lib/dto/ready.ts:56—ReadyItemDto.assignee, the data the route already holds.app/api/v1/projects/[projectKey]/ready/route.ts— the route;getDependencyEdgesForItemsis the bounded-projection precedent.lib/api/v1/workLoop/schema.ts— the dispatch-prompt payload, wheretargetRepolives (Q2).packages/cli/src/render.ts:255— the ASSIGNEE column.packages/cli/src/commands/batch.ts:135·:343— the snapshot's repo vs the dispatch's.packages/cli/src/sessionExcludes.ts— the{ id, key }exclude file (Q3).docs/decisions/public-api-conventions.md§7 (keys, not ids), §8 (additive-only).- ⚠️
docs/decisions/public-api-conventions.mdis also being edited by 11.6 onparent/MOTIR-1856-mcp-schema-alignment(Amendment 7), unmerged. Expect a conflict at the end of the file; rebase rather than append blindly. - Blocks: 11.5.4. Informs (no edge): MOTIR-2229.