Planning bug: a TERMINAL card's blocked_by set is written once and never re-derived, so every sibling filed after it leaves the card reading `ready: true` while definitionally un-runnable
Repo: motir-meta. Filed by the motir run MOTIR-2734 of 2026-08-17 — the third consecutive run of that card to stop before opening a PR, each time for the same structural reason and each time discovering it by hand.
The defect
MOTIR-2734 is a TERMINAL card: its own body says it "runs LAST", and its explanation says "flipping the default while any one of them is outstanding turns a green suite red." Its blocked_by set was written when it was authored — the twenty batch cards plus two application-layer cards — and has been amended only by the runs that tripped over it.
Its parent story MOTIR-2832 has since gained siblings continuously, because the story's own method is measure the residual, carve a card per class. On 2026-08-17 alone, three arrived — MOTIR-2887, MOTIR-2910, MOTIR-2911 — and not one carried a blocked_by edge to the terminal card whose acceptance criterion its failures falsify. So at the moment this run claimed it:
get_work_item→readiness: { ready: true, openBlockers: [] }validate_work_item→valid: true,blockers: [], and twoadvisoryentries that both point DOWNSTREAM (MOTIR-2515) or at an ARCHIVED predecessor (MOTIR-2528) — nothing naming 2887 / 2910 / 2911
Every readiness signal read green. Measured on the branch in post-cutover mode: 21 failures across four files, none of them this card's.
Why the existing machinery could not catch it
This is NOT the likely-missing-edge advisory's family, and that is the point. That channel scans a card's OWN description for references to not-done work items. 2734's description cannot name 2910 or 2911 — they did not exist when it was written, and the reference runs the other way: the NEW card is the one that should have named the terminal card. A scan of the referrer's text can only ever find edges the author already half-wrote.
Nor is it MOTIR-2903's shape (a card left todo after its substrate shipped) — the mirror case. Here the substrate never shipped and the card read ready anyway.
The rule this wants
A card whose scope is defined as after all of X has a blocked_by set that is a FUNCTION of its parent's child set, not a snapshot of it. Two places it can be enforced, and they are complementary:
- At FILE time (the cheap half). A new leaf created under a container that already holds a terminal card wires
blocked_byfrom that terminal card to itself as part of creation. This is the sweep-the-referrers corollary applied DOWNWARD — to the card that must wait — rather than upward to the cards that referenced it. - At RUN time (the backstop).
motir runon a terminal card re-derives itsblocked_byset from the parent's CURRENT child set before trustingready: true. Any not-done sibling absent from the set is either a missing edge or a deliberate exclusion the run must state.
The discriminator is cheap and needs no heuristic about "terminal": a card blocked_by more than half its siblings is one.
Acceptance criteria
- A rule in
plan-rules/states that a terminal card'sblocked_byis re-derived from its parent's child set, naming both the file-time and run-time halves, with 2734 / 2887 / 2910 / 2911 as the fixture. run.md's guard #4 gains the re-derivation check, stated so it fires onready: truerather than only on an advisory — the advisory channel is structurally unable to see this family, and the guard currently opens by reading it.- The rule says what a DELIBERATE exclusion looks like on the record, so "this sibling genuinely does not gate me" stays expressible.
Context refs
- MOTIR-2734 — the card, its three run comments, and the five → nine → twelve reader-set amendment trail.
- MOTIR-2832 — the parent story whose child set keeps growing.
- MOTIR-2903 — the mirror case, for the discriminator.
motir-meta/prompts/run.md— guard #4 and its advisory sub-rule;plan-rules/op-replan.md— the referrer sweeps.