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

PLANNING RULES (motir-meta) — two standing per-card gates: an AC must be satisfiable inside the card's own scope boundary, and a bug found mid-story is parented BY that story

Done
Description

Repo: motir-meta. One PR. The RULES-tier half of two defects hit in one session on 2026-08-04. Its motir-ai mirror is a sibling card — see "Two homes" below.

Rule 1 — an acceptance criterion must be satisfiable inside the card's OWN scope boundary

MOTIR-2082 was authored with two criteria that cannot both hold:

  • The re-drive advances indeximport only. It does NOT skip the import step
  • The live MOTIR run is unwedged and reaches audit_convention, producing the convention + audit.

IMPORT.checkExit returns ready: true only when importSkipped || importCompleted, or a completed Import row exists. So the hop between those bullets is a human decision, and a re-drive honouring the first can never deliver the second. Doubly unreachable: AUDIT_CONVENTION.ensureKicked fires on the advance out of that step and carries the run into discovery, kicking a discovery job — restarting pre-planning on an established project, which is exactly why MOTIR-1761 was archived.

validate_work_item returned valid: true, no blockers, no advisories — correctly. It reasons over the dependency GRAPH; a contradiction between two prose bullets on ONE card is invisible to it, and the prose-vs-graph detector (MOTIR-2078 / MOTIR-2079) finds references to not-done items, not self-contradiction. The card was not stale either — it was authored and hit in the same session.

The rule: when a card declares a scope boundary ("this does not do X"), re-read every criterion on the far side of X and name who performs the step that crosses it. If the answer is a human or another card, the criterion belongs to that card. The tell is a criterion phrased as a WORLD-STATE ("the live project now shows Y") rather than an OUTPUT ("this code does Z") — world-states silently annex whoever else must act. Same family as #30, reached from the opposite direction: the manual step was not omitted from the plan, it was absorbed into a coding card's ACs.

Related but distinct from the existing Story SCOPE BOUNDARY rule (plan-rules.md ~line 1012), which asks which side a subtask belongs on. This asks whether a criterion is reachable from inside the boundary its own card drew. Land it adjacent to that section.

Rule 2 — a bug found mid-story is parented BY that story, and so is its design prerequisite

Yue, 2026-08-04, after catching it twice in one session:

"If the bug is found in the story implementation, you can just add the bug in that story — the original scope doesn't matter. So they should be just in story 1755. The bug blocks the card to do." "If the bug needs a design before fixing, add the same design card in the story too."

The rule: found while working Story S → parented under S, blocked_by-wired to S's card that it holds up, in S's sprint. No weighing of where the defect "conceptually lives" or which story owns the surface. And if the fix needs a design first, that design card is created in S too — never bolted onto another story's design card.

The trap this closes: letting the DESIGN's location choose the bug's parent. It never does — the bug's parent chooses where the design goes. Two designs may legitimately touch the same page provided each owns different ELEMENTS; say so on both cards so neither redraws the other's.

Evidence — the same wrong hop, twice, three days apart, between the same two stories. 2026-08-01, MOTIR-1961: filed under MOTIR-810 (done → rejected), then MOTIR-1754 ("owns the code-index loop" → still wrong), finally MOTIR-1755. 2026-08-04, MOTIR-2080 / 2081: correctly filed under MOTIR-1755, then moved to MOTIR-1754 because 1754 held the nearest design card — re-creating a cross-story dependency on a blocked story. Reverted; MOTIR-2087 created inside 1755 instead.

Two homes — this card is HALF the change

A standing planning RULE lands in both motir-meta/prompts/plan-rules.md (the manual planner) and SHARED_PLANNING_RULES in motir-ai/src/llm/treeGeneration.ts (the shipped planner, composed by both fresh generation and src/jobs/handlers/replan.ts). A rule in motir-meta alone teaches only the manual planner while the product keeps planning the old way — the opposite of dogfooding. This card is the motir-meta half; the motir-ai mirror is its relates_to sibling. Write the wording here so the mirror can be lifted without re-deriving intent.

Acceptance criteria

  • plan-rules.md gains both rules as standing per-card gates, each with its fixture named (MOTIR-2082; MOTIR-1961 + MOTIR-2080/2081).
  • Rule 1 states the world-state-vs-output tell explicitly, and is placed adjacent to the existing Story SCOPE BOUNDARY section rather than duplicating it.
  • Rule 2 states both halves — the bug AND its design prerequisite go in the finding story — and names the design's-location-chooses-the-parent trap.
  • The per-card gate checklist is updated so both are checkable at authoring time, not only in review.
  • Wording is liftable verbatim into SHARED_PLANNING_RULES (no motir-meta-only references the mirror would have to rewrite).
  • Branch docs/<slug>, no MOTIR-<n> in the PR title or branch (the motir-meta status-sync rule); the card reference goes in the PR body.

Context refs

  • motir-meta/prompts/plan-rules.md — the per-card gate checklist and the Story SCOPE BOUNDARY section (~line 1012).
  • motir-meta/prompts/_shared.md — the RULES-vs-LESSONS split, and the plan-rules.mdSHARED_PLANNING_RULES pairing.
  • motir-meta/notes.html #211 — the LESSON-tier record of Rule 1, already landed in motir-meta PR #97.
  • lib/services/migrateOnboardingService.tsIMPORT.checkExit, AUDIT_CONVENTION.ensureKicked: the reads that disprove MOTIR-2082's criterion.
  • Precedent for the two-repo split: MOTIR-1773 (motir-meta) + MOTIR-1774 (the motir-ai mirror).