Type · planning bug (a corpus FACT that is false, stated with a code citation and the word verified)
Parent · MOTIR-1465, the planner-bug home. Holds up nothing, joins no sprint.
Discovered in · the approval of the design-result plan (MOTIR-3780), by observing what the eleven materialized subtasks were actually born at.
prompts/plan-procedure.md:453 — CORRECT:
"a proposal has no status, and approve seeds it from the
blockedByRefstheaddcarried — so getting the EDGES right IS setting the status"
prompts/_shared.md:171-172, _shared.md:516-517 and prompts/run.md:1500-1501 — FALSE, and each states it with a code citation:
"⚠️ AND KNOW WHAT APPROVE DOES NOT DO. A materialized
addtakes the workflow's INITIAL status; nothing reads its edges to seed Blocked (plansService.materializesetsstatus: statusKey— verified onorigin/main)."
Three sites say one thing, one says the opposite, and the majority is wrong.
lib/services/plansService.ts on origin/main has two passes, ~180 lines apart:
:1084 status: statusKey ← the initial status, at create. This is the line
the false claim was read off.
:1264 const unready = createdIds.filter(
(id) => !classifyBlockerReadiness(byItem.get(id) ?? [], terminalByProject).ready)
:1274 const blockedStatus = await workflowsRepository.findStatusByKey(… 'blocked' …)
:1299 await workItemRepository.update(id, { status: blockedStatus.key }, tx)
The second pass is guarded — it resolves the project's OWN blocked status rather than assuming one, and under a restricted workflow policy it requires the initial → blocked hop to be a declared transition. Either guard failing leaves the initial status in place, which is almost certainly the behaviour the false claim was generalised from.
Observed, on this project's own tree. MOTIR-3780 materialized eleven subtasks at 17:52:37:
| card | blockers | status at birth |
|---|---|---|
| MOTIR-3781 | none | todo |
| MOTIR-3782 … MOTIR-3791 | one or more not-done | blocked |
A blanket initial status would have made all eleven todo. The split is exactly the edge graph, so the derivation ran.
A correct reading of ONE site, generalised into a claim about the mechanism — and then hardened by the words "verified on origin/main", which is what makes it expensive: a reader who does the right thing and trusts a cited, verified claim inherits the error, and the citation points at a real line that really does say status: statusKey.
This is the second instance of this exact shape recorded today — see MOTIR-3775, where a card's measurement block was impeccable and the causal sentence beside it was never checked. There the unit was a sentence next to a measurement; here it is a second pass 180 lines below the line that was read. The family is a verified reading of a part, asserted of the whole, and at two occurrences it is worth naming rather than re-diagnosing.
A likely history worth checking: MOTIR-3050 — "An approved plan's blocked cards land as todo — materialize never derives a status" — is done. So the claim was TRUE when written, was FIXED, and the corpus was never swept. If so this is also an instance of a fix that closes a card and leaves the prose that described the defect standing, which is worth a sentence in the correction.
The guidance the false claim supports — "get the EDGES right; do not expect the status to follow" — is still good practice, so nothing built on it is wrong today. That is precisely why it has lasted. But the FACT is load-bearing in two ways:
todo may plan a post-approval status sweep, or tell a reviewer the board will lie to them. Both are now false, and both cost._shared.md is read by every command; a warning labelled ⚠️ and verified is the last thing anyone re-checks.prompts/_shared.md:171-172, prompts/_shared.md:516-517, prompts/run.md:1500-1501 — to say that approve DOES derive blocked from blockedByRefs, naming the two-pass shape and both guards (the project must HAVE a blocked status, and under restricted the initial → blocked transition must be declared).plan-procedure.md:453 is left alone — it was correct — and the correction CITES it, so a reader who met the old text can see which half won.motir-meta's standing convention): strike or ⚠️ CORRECTED with the date, so an existing citation still lands somewhere that explains what changed.plansService.ts, both guards, and the MOTIR-3780 materialization table above.done fix leaves the prose describing the defect standing" is the reusable half.SHARED_PLANNING_RULES needs NO sibling card — verified: git grep -in 'INITIAL status|seed Blocked|materialize.*status' over motir-ai origin/main -- src/ returns only unrelated blockedByRefs plumbing in coreClient.ts and the job handlers. Gate 19 is discharged by that grep, quoted in the pull request.motir-meta/prompts/_shared.md — the two false sites.motir-meta/prompts/run.md — the third.motir-meta/prompts/plan-procedure.md — the correct site, to cite rather than change.motir-core/lib/services/plansService.ts — :1084 (the line that was read) and :1264–:1302 (the pass that was not).(open)