11.5.22 The DETAIL reads over `/api/v1` — `getWorkItem`, the activity stream, and what `--json` emits
The third and last read slice 11.5.4 was split into — the sharpest reshape, plus the one user-visible decision the port forces.
| method | v1 operation |
|---|---|
getWorkItem | GET /api/v1/work-items/{key} |
getWorkItemActivity | GET /api/v1/work-items/{key}/activity — ONE operation serves all three views, so a cursor stays scoped to the view that issued it |
⚠️ THE DECISION: --json emits the v1 RESOURCE
The port makes "--json output is unchanged" unsatisfiable, and the reason is worth stating plainly: under MCP the view model WAS the tool payload — the mirror was structural — so "unchanged" cost nothing. Under v1 the payload is a different shape, so --json changes whichever way it goes.
Decided: --json emits the v1 body VERBATIM.
The view model is deliberately LOSSY — mcpClient.ts says heavier fields are omitted "because --json prints the tool payload itself, so nothing is lost by omitting them here." --json is the escape hatch that makes that narrowing safe. Emitting the view model instead would point the escape hatch at the narrowed thing and remove labels, components, commentCount, createdAt, reporterId, dueDate and the provenance fields from every script that reads it — a capability removal wearing a shape change.
Emitting the v1 body also makes the flag DOCUMENTABLE: "motir show --json returns the /api/v1 work-item resource" is a promise with an OpenAPI document behind it, versioned and additive-only under §8. And motir show KEY --json then agrees byte for byte with a direct curl of the same resource, which is the story's whole thesis made observable.
The adapter boundary is NOT violated. ADR Q4 exists so RENDERERS do not become a second consumer of the wire contract; --json is a pass-through, not a renderer, and it needs BYTES rather than a type. So the client exposes the raw body typed as unknown, the command JSON.stringifys it, and no generated type is IMPORTED outside src/adapters/ and src/transport.ts — the freshness test's rule is untouched.
One consequence, named: --json loses item.id. §7 keeps the internal cuid off the wire, and this is the same deliberate loss Amendment 10 Q3 already recorded when the exclusion list moved to keys.
This decision is recorded as an ADR amendment by this card, because it is a public-behaviour promise rather than an implementation detail.
The DETAIL reshape
ancestorKeys→ancestors, narrowed to{ identifier }:render.ts:747reads.identifieralone, so the alternative is inventing a kind/title/status the wire never sent.links(five groups) → the three arrays the CLI declares.linkIdleaves the view model — nothing read it, and v1 does not publish it.readiness.blockedByAncestorKey+…Title→ one{ identifier, title }. The two are null together on the wire, so the object is null or complete.WorkItemDetail.item.idand.parentare DROPPED — 11.5.19 removed the last reader of the first, and the second never had one.
The ACTIVITY reshape
- The CLI's
ActivityValue/ActivityPartstay LOOSER than the wire's closed unions, deliberately: the CLI ships on its own release train and meets newer servers, soactivityValueText's default branch must stay REACHABLE. A faithful re-narrowing turns an unfamiliar value into a crash. - The wire's
workItemKeybecomesidentifier— whatrender.ts:850reads. totalComments/totalChangescome from 11.5.18; the footer derives "44 more comments" astotal − shown, which page counts cannot supply.orderis not on the wire and does not need to be — it is what the CLI itself requested (defaultasc), so echoing it states no fact the server did not.
Scope BOUNDARY
These two methods, the --json decision and its ADR amendment, and the suites they break. It does NOT re-open the identity or collection reads. It does NOT change render.ts, any flag, or any HUMAN-readable output string — --json is the one deliberate exception, decided above. It does NOT delete the MCP transport (11.5.6).
Acceptance criteria
- Both methods call
/api/v1through the transport core; no MCP tool call remains on either path. motir showproduces HUMAN output identical to the MCP-era implementation for the same data — header, readiness, lineage, the WAVE view, every edge group and the body.motir show --jsonemits the v1 work-item resource byte for byte, and--activity --jsonthe v1 activity page — asserted by comparing against the exact body the server returned, not against a hand-written expectation.- The ADR carries the amendment recording this, including why the view model was rejected and why the adapter boundary is not violated.
- No generated wire type is IMPORTED outside
src/adapters/andsrc/transport.ts; the raw body reaches the command asunknown. - An activity value or part kind this build has never seen renders generically and does not throw — asserted with an invented
type. - A cursor issued for one VIEW is never sent to another.
- The whole
packages/clisuite is GREEN at this commit. - The per-file coverage floor (≥90%) holds on every new and modified file.
Context refs
scratchpad/11.5.4-wip.patch— this slice's mappers and converted fixtures are already written there.- 11.5.4 — the adapter module and the harness's
/api/v1capability. - 11.5.18 — the activity totals, and the
extendmechanism whose Ajv defect the patch also fixes. lib/api/v1/workItems/schema.ts·workLoop/schema.ts— the wire shapes.packages/cli/src/render.ts:565,747,850·assignChildWaves(570) — the readers the narrowings are measured against.packages/cli/test/read.test.ts(theshow/ activity blocks) ·dispatchCommand.test.ts— the suites this slice converts.- Story: 11.5.