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

Planning bug: the design gate makes the design card a CHILD of the card it gates — a prerequisite must be a SIBLING wired `blocked_by`, never a child

Done
Description

Repos · motir-meta + motir-ai, in that order. A rule change, not product code — and a standing planning RULE has TWO homes, so this card carries both: the runbook the manual planner reads (prompts/) and SHARED_PLANNING_RULES, which the shipped planner reads. It completes only when BOTH have merged.

⚠️ RE-FILED 2026-08-25 23:0x — the original premise was rejected (Yue)

This card was first filed as a motir-core defect in parentStatusRollupService: "a card that carries its own acceptance criteria is completed by its PREREQUISITE child's merge." That is not a bug. The rollup's rule — every child done ⇒ the parent is done — is correct, because a child IS a parent's content. What was wrong is the SHAPE it was fed: the design card was made a child of the card it gated. A design prerequisite is a SIBLING wired blocked_by, never a child. The producer of the wrong shape is run.md guard #3, and that is what this card changes. Nothing in motir-core is touched.

What happened

During motir run MOTIR-3490 on 2026-08-25:

  1. Guard #3 (the design gate) fired: MOTIR-3490 is UI-touching and design/roadmap/ depicted neither the grouped root node nor a truncated level. Correct so far.
  2. The remedy was applied as written — "PROPOSE a type: design Subtask for the surface" — and MOTIR-3493 was created under MOTIR-3490, plus the blocked_by edge.
  3. #2280 merged at 22:25:42, MOTIR-3493done, and MOTIR-3490done in the same beat with all six of its acceptance criteria unbuilt — because from the rollup's side it had one child and that child was complete. (An earlier in_progress → in_review at ~22:07 is the same thing one rung down.)

The tree has since been corrected by hand: MOTIR-3493 is now a root-level task (type: design) that blocks MOTIR-3490, with the blocked_by edge unchanged and MOTIR-3490 holding no children at all. That instance needs nothing further; this card is about the rule that produced it.

Why the rule produced it

prompts/run.md guard #3 said:

If NONE existsPAUSE. Do NOT improvise and do NOT build the UI. PROPOSE a type: design Subtask for the surface (create_planadd_plan_items { op: 'add' } …), and carry the edge on the SAME plan as { op: 'modify', workItemId: <the UI code Subtask>, patch: { blockedByAdd: [...] } }

The word Subtask is the defect. A subtask cannot be a root (prisma/sql/work_item_triggers.sql — it is the one kind that must have a parent), and ALLOWED_CHILD_TYPES admits bug → [subtask], so when the gated card is a parentless leaf the only kind-legal placement the remedy leaves is under the gated card itself. The rule never says "make it a child"; it makes a child unavoidable. And the guard's own framing — "the UI code Subtask" — assumes the gated card is a subtask hanging under a story, which is the one case where a sibling placement happens to be legal by accident.

The correct rule has nothing to do with kind: a prerequisite goes beside the card it gates, at the same level, and the ONLY thing joining them is the blocked_by edge. Its kind then follows from where it sits — subtask when the gated card has a container parent (a sibling under that same parent), task at the root or under an epic when it does not.

Acceptance criteria

  1. prompts/run.md guard #3's remedy states the placement explicitly: the proposed design card is a SIBLING of the gated card — the same parentKey the gated card has — and is NEVER parented under it, with the kind following the placement (subtask under a container parent; a root-level task when the gated card is itself parentless). The blocked_by edge stays exactly as it is today; it is what makes the sibling a prerequisite.
  2. The remedy also states, in one line, WHY: a child is read as the parent's CONTENT by parentStatusRollupService, so a prerequisite parented under the card it gates completes that card on its own merge. Cite the MOTIR-3490 / MOTIR-3493 fixture by key and date, one sentence, no recap.
  3. Guards #4 (the missing-precondition card) and #5 (the repo split) are audited for the same wording and carry the same placement rule — or an explicit one-line cross-reference to it. Any run-time remedy that PROPOSES a prerequisite is in scope; state which ones were checked, in the PR body.
  4. The plan-time mirror is closed: the plan-rules/* files instructing the planner to add a type: design card for an unspecified surface, or a missing precondition, state the same rule — so a plan authored from scratch cannot produce the shape either.
  5. A grep proves it: after the change, no file under prompts/ instructs anyone to add a type: design card under the card it gates. Run it and paste the output in the PR body — the sweep is by FILE, not by remembering where the rule lives.
  6. The motir-ai mirror ships in the same pass (Yue, 2026-08-25: "if motir-meta needs update, motir-ai needs too"): the rule lands in SHARED_PLANNING_RULES (src/llm/planningRulePacks.ts) — generic, unbranded, no source-file citations — with a tests/planningRulePacks.test.ts block pinning the limb AND its delivery into every cell its host reaches, and the conservation baseline refreshed through scripts/refresh-planning-rules-baseline.ts rather than by hand.
  7. parentStatusRollupService is NOT changed by this card — and the PR body says so, so a later reader does not re-open the rejected premise.

Delivery

  • motir-meta #322run.md (the named rule + guards #3/#4), plan-rules/core.md gate 4, plan-rules/kind-leaf.md, plan-rules/kind-bug.md, _shared.md, plan-procedure.md. Guard #5 audited and already correct (it proposes a sibling per repo).
  • motir-ai #297THE_DESIGN_GATE + VERIFY_EVERY_PRECONDITION, the test block, the refreshed baseline.
  • Both on the branch subtask/MOTIR-3496-prerequisite-is-a-sibling; both PR titles carry the key, so the first merge defers and the last one closes the card.

Context refs

  • prompts/run.md — guard #3 (the design gate, the producer), guards #4/#5 (the same shape), and the motir run CREATES NO WORK ITEM EXCEPT A bug block that already carries the edge-on-the-same-plan mechanics.
  • prompts/plan-rules/core.md gate 4 · prompts/plan-rules/kind-leaf.md (the planning-time design gate) · prompts/plan-rules/kind-bug.md — the plan-time half.
  • motir-ai/src/llm/planningRulePacks.tsSHARED_PLANNING_RULES; tests/planningRuleConservation.test.ts is the guard that goes red on a re-word.
  • motir-core/lib/issues/parentRules.tsALLOWED_CHILD_TYPES / TYPES_REQUIRING_PARENT: why "Subtask" forces the child placement; read, not changed.
  • motir-core/lib/services/parentStatusRollupService.tsagg.done === agg.total, the rung ladder, and the agg.total === 0 guard that makes an EMPTY parent safe; read, not changed.
  • MOTIR-3490 / MOTIR-3493 — the fixture and the corrected shape.