Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
144
planned
1,362
shipped

Motir · Work items

MOTIR-3155Done

Planning bug: a surface with a LIFECYCLE was planned one state at a time and nobody owned the state AFTER the terminal transition — four cards each answered it locally and one of them answered with a DELETE

Type · planning mistake (the RULE half of MOTIR-3154; the product half ships in motir-core) · Discovered in · motir log-bug over the plan-review canvas, 2026-08-19.

What the plan missed

The plan-review surface is backed by an explicit state machine — Plan.status: generating → planned → approved | declined — and design/ai-planning draws the states BEFORE the decision. The state after it was never drawn, and no card owned it, so four cards each answered it locally and the composite is a surface that cannot show the decision it just took:

cardwhat it decided about the decided state
MOTIR-1336 (substrate)decline DELETES every plan_item row — the tree was never touched, so the rows read as litter
MOTIR-1377 (bug)a declined plan's empty canvas shadowed the declined outcome → fixed with a decided short-circuit, not by keeping the cards
MOTIR-1775 / MOTIR-1782the approved canvas pane goes to the repository-establish step — "the canvas of proposals has served its purpose"
MOTIR-3083redrew the canvas as a roadmap level and scoped approve/decline explicitly OUT

Each is locally defensible. Together they mean a declined plan reads 0 items — indistinguishable from a plan that proposed nothing — and the record of what the planner suggested and the user refused is gone from the database.

Why no gate caught it

Every gate reads ONE card, and the missing state is on no card. Gate 5 asks whether a UI card cites a design asset — all four did, and the asset exists. Gate 2 asks whether the card's claims match shipped reality — they did. Only a reader holding all four at once sees the hole in the union of their scopes, and no pass is ever handed that union.

The state also has no natural owner: a pre-terminal state belongs to the card that builds the surface, while the post-terminal state arrives after the feature is "done", so every card can honestly say not mine — and MOTIR-3083 did, in a scope boundary nobody could object to. Gate 9's durable shape names empty-loading-error as the state set to plan for; a lifecycle's TERMINAL states are not in that list.

The rule question this card exists to settle

Is the takeaway RULES-tier or LESSONS-tier (plan-rules.md § RULES vs LESSONS)?

  • Candidate rule (a sharpening of gate 5 / gate 9, not a new gate): when a card touches a surface backed by a status or state enum, the enum is the checklist — the card's criteria name what the surface shows for EVERY value, and a value no asset draws is the design gate FIRING, not a scope boundary.
  • Against: it is conditional (fires only on state-machine-backed surfaces), and the discriminator says a conditional pattern is a LESSON, already carried by the notes.html entry.
  • A second, separable limb worth weighing on its own: a card whose implementation DELETES rows that record a human judgement (a proposal, a review, a refusal) owes a stated reason why retention is wrong — "nothing downstream reads them" is a claim about today's readers, and a deleted row cannot be restyled by any later card.

GREP BEFORE ADDING (notes.html #221): both limbs must be grepped in prompts/plan-rules/ and in motir-ai's SHARED_PLANNING_RULES (grep -a — that file's long lines defeat plain grep) before anything is written, and a sharpening of an existing clause is preferred over a new one.

Acceptance criteria

  1. Both limbs are grepped in BOTH homes (motir-meta/prompts/plan-rules/*.md and motir-ai's SHARED_PLANNING_RULES, with grep -a), and the card records what each grep returned — a hit means the limb is already carried and the disposition is "no change". ⚠️ AMENDED 2026-08-20, on the record: this criterion named src/llm/treeGeneration.ts as the mirror's home. It is not, and has not been since 2026-08-17 — that file now carries only export { SHARED_PLANNING_RULES } from './planningRulePacks.js';, and the constant is defined in src/llm/planningRulePacks.ts, split into ~34 named rule constants tagged by pack in CORPUS_ORDER. The old path still RESOLVES, which is what makes the stale citation dangerous: probing there returns 0 for everything and manufactures a false absence (the MOTIR-2663 shape). The greps below were run at planningRulePacks.ts.
  2. Each limb gets an explicit RULES-vs-LESSONS verdict, in writing: RULES ⇒ sharpen the existing clause (generic, no incident recap, cite the mistake #) in plan-rules/ AND sync it into SHARED_PLANNING_RULES with a guard assertion; LESSONS ⇒ the notes.html entry is the whole deliverable and nothing is added to either rules home. (The criterion named treeGeneration.test.ts; the per-cell delivery guards live in tests/planningRulePacks.test.ts today, so the block landed there and treeGeneration.test.ts carries one composed-prompt case as well.)
  3. If nothing lands in the rules tier, the card is closed with that verdict recorded — a "no rule change" outcome is a result, not an unfinished card.

Context refs

  • MOTIR-3154 — the product half (motir-core: the design amendment, declinePlan retaining its rows, the decided node treatment). This card changes no product code. done as of 2026-08-20.
  • notes.html mistake #328 — the corpus entry, merged and on origin/main (verified by content, not by its number; the live corpus is at #353). Its lesson and prompt-hint already carry BOTH limbs.
  • motir-meta/prompts/plan-rules/core.md — gate 5 (Design linked), gate 9 (Durable shape, whose state list is empty-loading-error); phase-deepen.md § No shortcuts — the COMPLETENESS axis, which is gate 9's full statement and where the new limb lands; type-design.md — the design-authoring bars.
  • motir-meta/prompts/plan-rules/CORPUS-MAINTENANCE.md § RULES vs LESSONS (and § THE THIRD TIER) — the taxonomy that decides this card's outcome. (plan-rules.md is an index now and points here.)
  • MOTIR-1336 · MOTIR-1377 · MOTIR-1775 / MOTIR-1782 · MOTIR-3083 — the four cards whose local answers compose the defect.

Resolution

Settled 2026-08-20. Limb A → RULES, as a WIDENING of an enumeration that already ships. Limb B → LESSONS, and nothing was added to either rules home for it. Both limbs were grepped in both homes first and neither had a hit in either — the full tables, with the populated baselines the zeros stand against, are in the close-out comment and in both PR bodies.

Limb A did not turn on the occurrence count — one incident is a LESSON by default, and that default usually wins. It turned on the ORDER test: the totality rule ALREADY EXISTS at rung 2 of the decision ladder ("a lookup keyed off an enum must be TOTAL over every value it can hold"), is always-injected, was available, and the miss happened anyway — because its subject is a lookup and not a rendered surface. That is the notes.html #47 → #177 shape, where the LESSON tier has been empirically tried on a check and the gap is a missing line in the executable index. Family by content: #29 (a hand-authored map ranked above the shipped enum — the incident that produced the rung-2 clause) and #334 (the totality rule read, applied correctly, a partial map shipped anyway). And it is FIRABLE (notes.html #166): the enum is in the schema the card is already reading — the same observability argument NO_SHORTCUTS makes for itself when it gives the SHAPE axis a countable trigger.

Limb B stays a LESSON on OBSERVABILITY rather than merit. Its input is a deleteMany in an implementation and a planner reads a card; keyed on a card that already SAYS it deletes, it would fire on the honest author and miss the silent one — the inversion MOTIR-2432's measurement rejected candidate B for. In this fixture nothing was said at all, which is why every gate passed. The data loss is reached anyway as a CONSEQUENCE of limb A (a drawn terminal state has proposals to render, so the delete stops looking free), and the mirrored limb says so in one clause without keying a rule on an unobservable trigger. src/seed/lessons.base.ts is a frozen seed corpus — highest notes.html reference #158 against a live corpus at #353 — so no distillation row is owed either.

What shipped — two PRs, ONE branch name, targetRepos: [motir-meta, motir-ai], the card held at In Review until both merge:

  • motir-meta #302 — the STATE-SET limb in full in phase-deepen.md's COMPLETENESS axis; a one-sentence index entry at core.md gate 9; and the state-axis twin of gate 5's door inverse (a drawn pre-terminal state is not a drawn post-terminal one, so an undrawn value is the design gate FIRING, not a boundary a consuming card may inherit). Two MOTIR-3154 · ×1 warrants. conserve.py: both edited packs PASS. measure.py: core.md 807/825 → 820/837, still PASS; worst cell +3 overage for +40 lines on a pre-existing FAIL.
  • motir-ai #265 — the same words appended inside NO_SHORTCUTS (phase-deepen) and THE_DESIGN_GATE (kind-leaf-deepen), placement kept, no pack created; +14 guard cases pinning per-cell delivery, each discharge separately, placement, pack choice, the host's ARITY ("Three axes" stays true — this is not a fourth axis) and non-duplication between the two limbs; conservation baseline refreshed in the same commit with 0 removed words. Four guard suites green, 1 162 tests.