PLANNING BUG — a request-count criterion named no operation, and the bug filed about it asserted an absence it never enumerated: the bulk read existed, on a component its own evidence list named
The record of the defect behind the 2026-08-08 re-plan of MOTIR-2422, filed under THE REPLAN ACTION step 5.
⚠️ RE-SCOPED 2026-08-08, mid-
motir run— this card's own central claim was FALSE, verified onorigin/main.It previously read "a card asserted a bulk read that no operation offers" and concluded "Every path was closed." Every path was not closed. The evidence below is the re-grep; the RULE this card proposes survives, sharper in its discharge. Nothing was archived; MOTIR-2422's outcome and MOTIR-2445 both stand.
The defect, in two layers
Layer 1 — the original (2026-08-07). MOTIR-2422 required:
The parent read happens ONCE per run at close-out, not once per card, asserted on the request count.
It named no operation. A per-request budget written as a constraint is in fact an assertion that some read answers for a SET — and this one was never cited.
Layer 2 — the compounding (2026-08-08), which is this card. The re-plan diagnosing layer 1 wrote, onto MOTIR-2422 and into this card:
No such read exists.
parentKeyis on three generated components and every operation returning one is addressed by a single{key}; the three collection rows a loop could read in bulk (ReadyItem,WorkItemSummary, the search page) all omit it. … Every path was closed.
What is actually true (grepped on origin/main, 2026-08-08)
The three carriers that sentence names are WorkItemDetail, WorkItemLinkGroups and WorkItemRef — and WorkItemRef IS a bulk row:
| fact | evidence |
|---|---|
WorkItemRef carries the field | lib/api/v1/workItems/schema.ts → workItemRefSchema … parentKey: workItemKeySchema.nullable() |
| it is returned in BULK, twice | GET /api/v1/sprints/{sprintId}/work-items and GET /api/v1/projects/{projectKey}/backlog, each RankedPageEnvelope & { items: WorkItemRef[] } (lib/api/v1/rankedCollections.ts → presentRankedPage / presentWorkItemRef) |
| it predates the claim by four days | 94a65035 (#1814, 2026-08-04) added the field; 1d7b7780 (#1819, 2026-08-04) the ranked collections — both ancestors of origin/main |
| the CLI can already call it | listSprintWorkItems is bound in packages/cli/src/api/operations.ts |
So MOTIR-2422's original criterion was satisfiable at ONE request per run. The door was open.
The specific miss — the enumeration was right, the JOIN across it was never made
The card ran the first grep (which components carry parentKey? → three, correct, from the emitted document) and answered the second (how is each addressed?) from recall, listing seven {key}-shaped routes. That question is equally mechanical: map every path to the component in its 200 response and read off which are collections. Two of the thirty-nine return a page of WorkItemRef.
Two things hid it:
- The two lists were in different vocabularies. Carriers were component names; bulk rows were two component names plus "the search page's rows" — a UI phrase. A term cannot be reconciled against a description, so
WorkItemRefnever landed beside "collection row" despite sitting three sentences away. - The genre camouflaged the claim. This is the asserted-ABSENCE limb (
notes.html#221) — "grep the target and QUOTE the result before asserting the gap" — which did not fire because a planning-bug record's entire content IS an asserted gap: there is no annotation to demote and nothing that looks like the edit-shaped card the limb describes.
And the outcome it justified was right, which is why it survived: the re-plan moved the parent onto DispatchPromptDto (MOTIR-2445), which the loop already fetches — zero extra requests, genuinely cheaper than the sprint listing it wrongly ruled out. A correct decision lends its correctness to the reasoning that reached it.
What to change
motir-meta/prompts/plan-rules.md — a request-COUNT limb on the precondition-verification gate, beside the data-seam rule it sharpens:
A criterion that constrains HOW MANY REQUESTS something costs is a claim about an OPERATION — name it, and discharge it by ENUMERATING the operation set, never by recalling endpoints. "Once per run", "one request", "not per item", "no additional request" each assert that some operation answers for a SET. The discharge is TWO greps over the emitted spec — (1) which components carry the field, (2) map EVERY path to the component in its
200response and read off which are collections — and only the second finds anything, because the first feels conclusive. Write both lists in ONE vocabulary. Ask for the fattest response that EMBEDS the component, not for the resource's own endpoint. Both directions, and the ABSENCE is the dangerous one.
And motir-meta/notes.html — a .mistake entry (#243), count bumped, per step 4.
Scope BOUNDARY
The rule in plan-rules.md and the notes.html entry — a motir-meta branch + PR, never a direct commit to main. It does NOT change MOTIR-2422's outcome or MOTIR-2445, both of which stand on their merits; 2422's false sentence is corrected on the card itself, not here. It does NOT add a field to any API. It does NOT carry the motir-ai mirror — a standing RULE has TWO homes and that one is MOTIR-2448, which this card blocks (ONE SUBTASK = ONE REPO = ONE PR).
Acceptance criteria
plan-rules.mdcarries the rule as a limb of the precondition-verification gate, with this instance as its worked example — including that the operation EXISTED and the enumeration was the step skipped.- The rule states the discharge as an enumeration of the operation set, not merely "name the operation" — the weaker form is what this card originally proposed and is what layer 2 would still have passed.
notes.htmlcarries a.mistakeentry matching the existing markup, gapless numbering, theAcross the Ncount bumped to match the last entry, naming what was assumed, theorigin/mainevidence, and the lesson.- Both land on ONE
motir-metabranch + PR, authored as Zhu Yue with no co-author trailer, with noMOTIR-<n>in the branch name or PR title (the motir-meta sync rule).
Context refs
- MOTIR-2422 — the re-scoped card; its comment carries this evidence table.
- MOTIR-2445 — the field the re-plan carved out; stands.
- MOTIR-2448 — the
motir-aiSHARED_PLANNING_RULESmirror of this rule. motir-meta/prompts/plan-rules.md— the precondition-verification gate, and its asserted-ABSENCE limb.motir-corelib/api/v1/workItems/schema.ts·lib/api/v1/rankedCollections.ts— the bulk read.- Story: MOTIR-1855.