Planning bug: guard #4 says an advisory's reference is "a `blocked_by` to WIRE" — followed literally on a CROSS-STORY reference it produces a cross-parent edge, which gate 7 forbids
Discovered 2026-08-21 by Yue, reading MOTIR-3295 immediately after it was planned: "story 3295 not valid".
What happened
Planning MOTIR-3293, the pass ran validate_work_item on the epic and got seven reference advisories. It disposed of them as guard #4 instructs:
a
referenceentry naming an item that is notdoneis ablocked_byto WIRE (link_work_items), never a note to leave
Two were wired: MOTIR-3302 blocked_by MOTIR-3300 and MOTIR-3304 blocked_by MOTIR-3299. Both cross parents — 3302 is a child of 3295 while 3300 is a child of 3294; 3304 is a child of 3296 while 3299 is a child of 3294.
core.md gate 7 says the opposite:
Edges a same-level DAG — every
blocked_bya same-kind sibling under the same parent
So the pass satisfied one rule by breaking another, and produced a subtree that validate_work_item then reported as not finishable for a reason that was partly its own doing.
Why it is the instruction and not carelessness
The five advisories the pass did NOT wire were disposed of correctly, with the right reason written down — "covered by the story edge". The same reasoning applies verbatim to the two it wired. What separated them was not judgement: the two wired ones were the ones where the reference looked most like a genuine consumption (a parameter passed, a column read), and guard #4's sentence is unconditional, so the more real the dependency felt the more the literal instruction applied.
The correct disposition for a CROSS-CONTAINER reference is an edge at the CONTAINER level, or nothing if one already exists. Both were already ordered: 3295 blocked_by 3294 and 3296 blocked_by 3294. Readiness confirms the container edge does the work — 3302 reads ready: false, openBlockers: [], blockedByAncestor: MOTIR-3295, so the subtask edge added no gating and only violated the DAG rule.
The fix
Guard #4's disposition sentence needs the qualification gate 7 already implies: wire the blocked_by when the referenced item is a SIBLING under the same parent; when it is in another container, the edge belongs between the containers — and if that edge already exists, nothing is owed. Same for the identical sentence in the create-time half (log-bug.md step 3 / the guard #5 validate block), which carries the same wording.
Acceptance criteria
run.mdguard #4'sreference-disposition sentence states the same-parent condition and names the container-level edge as the cross-container disposition.- Every other copy of that sentence in the corpus is found by grep and corrected or explicitly left, with the grep and its hit count quoted.
- The correction names
readiness.blockedByAncestoras the mechanism that makes a container edge sufficient, so a reader does not re-add the subtask edge for safety. - Lands via a branch + PR; no direct commit to
main.
Context refs
motir-metaprompts/run.md— guard #4's advisory-disposition block, and the identical wording in the guard #5 / create-time validate note.motir-metaprompts/plan-rules/core.md— gate 7, edges a same-level DAG… under the same parent.motir-metaprompts/log-bug.md— step 3's validate post-condition, same sentence.- MOTIR-3295 — the story that read invalid; the two offending edges have been removed.