Decision: is a LINE still a RULE? Settle the clause unit before any rule text moves
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): splittingRe-measured atVERIFY_EVERY_PRECONDITIONmoved 13 spaces to newlines in the composed prompt, and routing its limbs turned eleven ordering guards red.origin/main5b55264against36c24ad:VERIFY_EVERY_PRECONDITIONis byte-identical in both (48,839 chars, zero internal newlines) — #332 never split it. It appended 14 newA_<TYPE>_CARDS_PRECONDITIONsegments 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 all —git diff --stat $(git merge-base 36c24ad origin/main) 36c24adtouchesplanningRulePacks.tsand 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
- 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.
- Make the SEGMENT the unit.
CORPUS_ORDERentries become the addressable rule;clauseOfreads 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. - 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 toclauseOf. - 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_byedge to this card.
Context refs
motir-aitests/treeGeneration.test.ts—clauseOfand the limb-adjacency guards.motir-aitests/planningRuleConservation.test.ts+tests/helpers/corpusConservation.ts— the word-and-length classifier that cannot see a whitespace re-cut.motir-aitests/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.