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
Back to roadmap
Implemented

Decision: is a LINE still a RULE? Settle the clause unit before any rule text moves

Sub-taskopened by Zhu YueMOTIR-3892

The question. The corpus currently treats one line as one rule. Does it keep doing so after the re-cut, and if not, what replaces it as the unit a guard can address?

Where the current answer is written. motir-ai tests/treeGeneration.test.ts:

/** The whole clause a rule lives in — clauses are one line each in the constant. */ const clauseOf = (needle) => r.split('\n').find((l) => l.includes(needle));

Around a dozen assertions depend on it, including a family that pins limbs to their neighbours — "sits INSIDE the precondition rule, after the ABSENT verdict, before the claim-vs-pointer gate" — because each limb reads back to the one before it ("Everything above presumes…", "The two axes above ask…").

Why it blocks the story. Every re-cut splits a constant, which turns one line into many and re-draws rule boundaries. VERIFY_EVERY_PRECONDITION is one line of 48,839 characters — 22.9% of the 213,211-char corpus — carrying twelve numbered limbs, so under line-as-rule that whole thing is ONE rule and can be routed no finer. The conservation check cannot see a re-cut: tests/helpers/corpusConservation.ts's norm() collapses whitespace ("Line breaks are not content"), so a space becoming a newline normalises away.

⚠️ AMENDED 2026-08-29 (MOTIR-3892's own run) — the motir-ai#332 evidence this card was written on is FALSE. Measured on the attempt in motir-ai#332 (closed): splitting VERIFY_EVERY_PRECONDITION moved 13 spaces to newlines in the composed prompt, and routing its limbs turned eleven ordering guards red. Re-measured at origin/main 5b55264 against 36c24ad: VERIFY_EVERY_PRECONDITION is byte-identical in both (48,839 chars, zero internal newlines) — #332 never split it. It appended 14 new A_<TYPE>_CARDS_PRECONDITION segments beside the untouched constant (+7,533 chars of new text); the +14 newlines are those segments' own join separators. And #332 changed no test file at allgit diff --stat $(git merge-base 36c24ad origin/main) 36c24ad touches planningRulePacks.ts and three fixture BASELINES only — so no ordering guard went red. The card's conclusion stands and is re-verified directly: clauseOf's own comment states line-as-rule, and 23 call sites read that 48,839-char line as "the rule". #332 is an instance of MOTIR-3891's other finding — adding rules does not stop the wrong ones being delivered. Planning bug filed under MOTIR-1465.

The options

  1. Keep line-as-rule. Every re-cut must preserve each limb's line, so a limb only moves with its whole line. Cheapest to guard, and it caps how finely the corpus can be routed — which is the constraint that produced three reversals.
  2. Make the SEGMENT the unit. CORPUS_ORDER entries become the addressable rule; clauseOf reads a segment rather than a line; the adjacency guards are restated as segment membership (this limb is in this pack) instead of neighbour position. Costs a rewrite of the ~6 back-reference connectives so each limb stands alone.
  3. Keep both. A line stays a rule for authoring, and segments are a routing unit beneath it. Two units, and the guards have to say which one they mean.

Recommendation and its evidence

Option 2. Rung-2 evidence, from this project's shipped code: PLANNING_RULE_PACKS is already derived from CORPUS_ORDER segments, planningRuleRouting.test.ts already records per-cell SEGMENT membership rather than lines, and planningRulePacks.test.ts's DISJOINT COVER invariant is already stated over segments. The segment is already the routing unit everywhere except clauseOf, so option 2 makes one reader agree with the rest rather than introducing a new concept.

The cost is real and belongs in the record: the back-reference connectives are load-bearing prose, and rewriting them is a re-wording that the conservation baseline must be refreshed for, with the diff reviewed.

Acceptance criteria

  • The decision is RECORDED as an ADR under motir-meta, naming the chosen unit, what a guard may assert about it, and what happens to clauseOf.
  • It names each back-reference connective that must be rewritten if option 2 is chosen, by its opening phrase, so the rewrite has a fixed and countable scope rather than being discovered mid-move.
  • It states what it does NOT settle — in particular whether a pack is named for a type or for a subject, which is decided by the selector card, not here.
  • Every dependent subtask of MOTIR-3891 carries a blocked_by edge to this card.

Context refs

  • motir-ai tests/treeGeneration.test.tsclauseOf and the limb-adjacency guards.
  • motir-ai tests/planningRuleConservation.test.ts + tests/helpers/corpusConservation.ts — the word-and-length classifier that cannot see a whitespace re-cut.
  • motir-ai tests/planningRuleRouting.test.ts — per-cell segment membership, the existing segment-level guard.
  • motir-ai#332 (closed) — the attempt this card mis-described; see the amendment above for what it actually did.

Comments (0)

No comments yet — be the first to weigh in.

Sign in to comment

Reading is open to everyone; posting needs a Motir account.

Sign in