MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3849

Planning bug: a `blocked_by` wired to an ARCHIVED predecessor gates nothing — the card sealed `ready: true` against a retirement that has not shipped, and the advisory that named it was never read

To Do
Description

Found by: motir run MOTIR-3819, 2026-08-28, at the dispatch guard — before any worktree existed. The card was NOT claimed.

The defect

MOTIR-3819 was created at 19:09:37Z with its blocked_by pointing at MOTIR-3785, and relates_to edges to MOTIR-3785 and MOTIR-3791. Both of those cards had been archived at 18:36:26Z — 31 minutes earlier — by the restructure that hoisted the CI-publisher retirement out of MOTIR-3780 into the new story MOTIR-3795. Their live successors, byte-identical in title and scope, are MOTIR-3797 and MOTIR-3800.

An edge to an archived item does not hold a card out of the ready set. So MOTIR-3819 sealed:

readiness: { ready: true, openBlockers: [], blockedByAncestor: null }
validate_work_item: { valid: true, blockers: [] }

…on a card whose own body says, in its own words, "this should not merge before it" and "fixing it early would make the panel lie in the other direction for as long as the lane still runs." Every automatic signal said start; the card's prose said stop; and the prose was right.

Evidence that the precondition has NOT shipped

Read on origin/main @ c4d11059f at dispatch:

$ git ls-tree -r origin/main --name-only | grep upload-design-assets
scripts/upload-design-assets.mjs

$ git show origin/main:.github/workflows/ci.yml | sed -n '490,521p'
      - name: Publish the design result
        ...
          node scripts/upload-design-assets.mjs

The CI publisher and its design-guards publish step are both live. MOTIR-3797 — which deletes them — is blocked, behind MOTIR-3796 (the deployed-tenant gate) at todo. So MOTIR-3819 is two hops from ready, and ready: true was off by two hops.

⚠️ The advisory channel DID fire. Nothing read it.

validate_work_item on MOTIR-3819 returns, and returned at file time:

{ item: MOTIR-3819, referenced: MOTIR-3785, referencedStatus: "blocked", severity: "advisory" }
{ item: MOTIR-3819, referenced: MOTIR-3791, referencedStatus: "blocked", severity: "advisory" }

Both entries name the right cards at the right tier. This is the same shape as run.md's canonical guard-#4 fixture — "the advisory fired at the right tier, naming the right blocker … it changed nothing because no step read it" — with one sharpening worth recording: the two entries are plain advisory, not likely-missing-edge, because from the detector's side the edge is not missing — it is present and it points at an archived card. A runner filtering on likely-missing-edge sees nothing here, which is exactly the narrow reading run.md already warns against.

Root cause — a snapshot, not a lookup

The filing run had just performed the restructure. It wrote MOTIR-3819's references from the tree as it stood when it started reasoning, not as it stood when it wrote. The archived keys were, half an hour earlier, the correct ones.

The general shape: a card authored after a re-plan cites the pre-re-plan keys, and the graph accepts it silently — because an edge to an archived item is well-formed, resolvable, and inert. An absent edge and an inert edge are indistinguishable from every readiness signal, and only the inert one looks like diligence.

Disposition applied in this run

All DIRECT, between cards that already exist — no plan proposal was needed, because nothing had to be created or re-scoped:

  • link_work_items MOTIR-3819 blocked_by MOTIR-3797 (a sibling under the same parent MOTIR-3795, so gate 7 is satisfied and the edge belongs at the leaf).
  • unlink_work_items the dead blocked_by and relates_to edges to the archived MOTIR-3785, and the relates_to to the archived MOTIR-3791.
  • link_work_items MOTIR-3819 relates_to MOTIR-3800.
  • update_work_item MOTIR-3819 — every reference rewritten to the live successor, with an amendment block on the record.
  • transition_status MOTIR-3819Blocked.

Acceptance criteria

  1. MOTIR-3819 is blocked_by MOTIR-3797 and carries no edge to an archived item. (Landed in this run — closing this is a read.)
  2. MOTIR-3819's body cites MOTIR-3797 / MOTIR-3800 and records the amendment. (Landed in this run — closing this is a read.)
  3. A LESSON is written to the store carrying the takeaway: after a re-plan that archives and re-creates a card, a later referrer must cite the LIVE successor — and at dispatch, a blocked_by naming an archived item is an UNMET precondition, never a satisfied one, because readiness cannot tell an inert edge from an absent one. Scope is GLOBAL by the strip-the-identifiers test: the takeaway survives removal of every repo, key, path and gate number.
  4. run.md's guard #4 advisory block gains the archived-referent limb: a plain advisory naming a not-done reference must be checked for whether the card's EDGE points at that same item in an archived state — the one case where an edge exists and gates nothing.
  5. No product code. The advisory channel behaved correctly and the readiness rule is defensible on its own terms (archiving says do not work this, so keeping dependents blocked on it for ever would be worse). What failed is that nobody read the output.