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
145
planned
1,361
shipped

Motir · Work items

MOTIR-2967Done

`SHARED_PLANNING_RULES` has NO historical baseline — invariant B compares the rebuild to itself, and the 58 phrase assertions pin 1.20% of it, so a re-wording of the shipped planner corpus passes every test

Repo: motir-ai. One PR. Surfaced 2026-08-17 by the motir run MOTIR-2934 execution, under notes.html #27 (an out-of-scope defect found mid-subtask is logged, never absorbed). Nothing in motir-meta is edited by this card.

⚠️ AMENDED ON THE RECORD 2026-08-18 by the motir run MOTIR-2967 that executed this card (PR moooon-B-V/motir-ai#243). The measurement table below originally gave the corpus as 143 079 chars, yielding 1.05 %. That is the character count of the FILE src/llm/planningRulePacks.ts — identifiers, backticks and the CORPUS_ORDER table included — not of the constant SHARED_PLANNING_RULES, which is 125 476. The table now carries the re-derived figures; the original numerals are struck through beside them so the reading that was acted on stays visible. The finding is unaffected — true coverage is 1.20 %, so the card was pessimistic about its own denominator by 14 %, and the argument it makes from that number stands in every direction. Evidence: SHARED_PLANNING_RULES.length = 125 476 vs readFileSync('src/llm/planningRulePacks.ts','utf8').length = 143 079, on a clean origin/main at 034b22a; git log b763dd8..034b22a -- src/llm/planningRulePacks.ts is empty, so the file is byte-identical to what the reporter read and "corrected in between" is excluded. Filed as planner telemetry: MOTIR-2968.

The defect

The planner corpus has two homes. Only one of them can detect that its text changed.

COMPRESSION.conserve.py holds each pack against a fixed historical commit that never moves forward, so a re-worded rule surfaces as a replace and fails. That is the whole reason MOTIR-2773 and MOTIR-2934 exist — the check is sensitive, and both cards were about paying that sensitivity honestly rather than dulling it.

src/llm/planningRulePacks.ts has no equivalent, and the test that looks like one is self-referential. tests/planningRulePacks.test.ts, "INVARIANT B — disjoint cover":

const rejoined = PLANNING_RULE_PACK_ORDER.map((p) => PLANNING_RULE_PACKS[p]).join('\n');
const corpusLines = SHARED_PLANNING_RULES.split('\n');
expect(packLines.length).toBe(corpusLines.length);
expect([...packLines].sort()).toEqual([...corpusLines].sort());

Its own comment states the reason: "the exported constant IS the rebuild, so any line dropped by the partition is a line missing from it." Both sides are derived from the same packs, so they move together. Invariant B is a real and useful check — it catches a line in two packs, a line no cell reaches, a broken partition — and it is structurally incapable of noticing that a rule's words changed.

The residual protection, MEASURED rather than assumed

That comment defers to "the ~45 per-rule assertions in treeGeneration.test.ts". Measured on origin/main at 034b22a (re-derived 2026-08-18 — see the amendment note above):

string/template literals inside a .toContain(...) in tests/treeGeneration.test.ts58 (57)
total pinned characters1 511 (1 507)
SHARED_PLANNING_RULES size125 476 chars (143 079 — that was the FILE)
upper-bound coverage1.20 % (1.05 %)
median pinned literal15.5 chars (16)
snapshots / golden files / corpus hash0 / 0 / 0

And toContain is a positive-presence assertion. It proves a phrase exists; it cannot see a re-worded sentence outside the 58, a deleted sentence outside the 58, or any inserted sentence at all — nothing asserts absence. So ~99 % of the corpus the product actually plans with is unpinned in both directions.

Why this is not covered by the other home

"The text is lifted from motir-meta, which is baselined" is true and does not help: nothing compares the two. The lift is a human copy, the mirror is a SELECTION (~13.6 % of the packs) rather than a copy, and the packs have already drifted in placement — the claim-vs-pointer gate is core.md gate 2 in one home and sits under phase-deepen in the other. A baseline at the home protects the home.

The sharp edge is that invariant B READS as the conservation check. It is named for an invariant, it is grouped with the AC references, and it compares two things — so a reader confirms the corpus is protected and stops. A blind spot that reads as a verdict is worse than a visible absence, which is the same shape as notes.html's scanner-blind-spot family.

Deliberately NOT decided here — the dispositions differ by an order of magnitude in size

RESOLVED 2026-08-18: disposition 1 was chosen and applied (PR moooon-B-V/motir-ai#243). The reasoning, and the refusal of the other three, is recorded in the comment block above describe('INVARIANT B …') in tests/planningRulePacks.test.ts per AC 2.

  1. A historical baseline in motir-ai, with MOTIR-2773's classifier ported — a committed golden copy of the constant + a check that permits whole ADDED sentences and fails a replace/delete/fragment. Proven twice on this corpus; catches the actual risk (a silent re-wording). Recommended. ✅ CHOSEN.
  2. A cross-repo equality check — assert the mirror's rule text against the packs it was lifted from. It states the claim we actually want, and motir-ai cannot read motir-meta at test time, so it needs a vendored copy — which is a second conservation problem wearing the first one's clothes. If this is chosen, re-scope or SPLIT the card before building; it does not fit this estimate. ❌ Refused — worth its own card.
  3. Widen the pinned-phrase set. Not a disposition — a mitigation. It moves 1.20 % upward and can never become a claim about the whole. ❌ Refused.
  4. Accept the exposure and WRITE IT DOWN — amend invariant B's comment so it stops implying a coverage it does not have. Legitimate, and the one option that must not be taken silently. ❌ Refused as the WHOLE deliverable; done as half of it (AC 4).

Acceptance criteria

  1. The three measurements above are re-derived on a clean origin/main checkout and quoted BEFORE any edit — the literal count, the pinned-character total, the constant's size — so the fix is measured against a recorded gap and not a re-derived one.
  2. One disposition is chosen and applied, and its reasoning is recorded in a comment block at the invariant-B test in tests/planningRulePacks.test.ts, including why the others were refused.
  3. Under disposition 1 or 2: a re-wording of one word of SHARED_PLANNING_RULES FAILS the new check, and a whole appended sentence PASSES it — both proved by a test in tests/, not by description.
  4. Invariant B's own comment no longer implies the per-rule assertions cover the corpus; it states the measured share and names what the new check covers instead. Under disposition 4 this criterion is the entire deliverable.
  5. pnpm vitest run tests/planningRulePacks.test.ts tests/treeGeneration.test.ts passes, and the PR body quotes it. The full-suite number is CI's to report, not this card's.
  6. No rule TEXT in src/llm/planningRulePacks.ts is edited by this card — the deliverable is the check, and a baseline that was captured after an unnoticed edit proves nothing.

Context refs

  • src/llm/planningRulePacks.ts — the ~32 named rule constants, CORPUS_ORDER, PLANNING_RULE_PACKS, composePlanningRules, and SHARED_PLANNING_RULES derived over them.
  • tests/planningRulePacks.test.tsINVARIANT B — disjoint cover, the three it blocks and the comment that defers to treeGeneration.test.ts.
  • tests/treeGeneration.test.ts — the 58 toContain literals that are the current protection.
  • READ-ONLY reference, not edited: motir-meta's prompts/plan-rules/COMPRESSION.conserve.py (is_addition() / classify() / check()'s fixture subtraction) is the mechanism disposition 1 ports, and MOTIR-2934 is the worked example of paying its sensitivity honestly.