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-3792

Planning bug: the corpus CONTRADICTS ITSELF on what approve does to a proposal's status — `_shared.md` and `run.md` say it derives nothing and cite the code, `plan-procedure.md` says it derives from the edges, and the shipped `materialize` agrees with `plan-procedure.md`

Done
Description

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.

The contradiction, in the corpus's own words

prompts/plan-procedure.md:453 — CORRECT:

"a proposal has no status, and approve seeds it from the blockedByRefs the add carried — 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 add takes the workflow's INITIAL status; nothing reads its edges to seed Blocked (plansService.materialize sets status: statusKeyverified on origin/main)."

Three sites say one thing, one says the opposite, and the majority is wrong.

What shipped reality does

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:

cardblockersstatus at birth
MOTIR-3781nonetodo
MOTIR-3782MOTIR-3791one or more not-doneblocked

A blanket initial status would have made all eleven todo. The split is exactly the edge graph, so the derivation ran.

Root cause

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.

Why it matters, given the ADVICE around it happens to survive

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:

  1. It invites redundant work. A planner who believes approve leaves every card 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.
  2. It teaches a reader to distrust a mechanism that works. _shared.md is read by every command; a warning labelled ⚠️ and verified is the last thing anyone re-checks.

Acceptance criteria

  1. All three false sites are correctedprompts/_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).
  2. The correction keeps the surviving advice, which is unchanged and still the point: get the EDGES right, because the edges are what the derivation reads.
  3. 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.
  4. The corrections are ADDED with the old text marked, never silently re-worded (motir-meta's standing convention): strike or ⚠️ CORRECTED with the date, so an existing citation still lands somewhere that explains what changed.
  5. The evidence is on the record: the two line numbers in plansService.ts, both guards, and the MOTIR-3780 materialization table above.
  6. MOTIR-3050 is read and its relationship stated — if it is the fix that made the claim stale, say so, because "a done fix leaves the prose describing the defect standing" is the reusable half.
  7. 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.

Context refs

  • 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).
  • MOTIR-3780 — the materialization this was observed on.
  • MOTIR-3050 — the probable origin of the staleness.
  • MOTIR-3775 — same family, filed earlier today.

Resolution

(open)