`corpusConservation.ts` carries the `. **⚠️` limb of MOTIR-2969 unfixed — the port dropped clause (3) but kept clause (1), so the next rule added before a `⚠️` marker reds the mirror
Repo: motir-ai. One PR. The mirror half of MOTIR-2969, which fixed the same defect in motir-meta's COMPRESSION.conserve.py (PR #238). Separate card because it is a separate repo — ONE SUBTASK = ONE REPO = ONE PR.
The defect
tests/helpers/corpusConservation.ts is a TS port of classify / isAddition, with one stated deviation: the diff unit is a WORD, not a character. MOTIR-2969's defect has two limbs and the deviation reaches exactly one of them.
Measured against motir-ai origin/main @ c49c6eb, by running the real port:
=== the kind-story limb — a run starting inside a shared `**` marker ===
violations TODAY: 0 ← UNREACHABLE in the port, as its header claims
=== the phase-deepen limb — a run ending `. **⚠️` ===
op=insert isAddition=false
run head="The prospective limb disciplines what the"
run tail="the card predicts. **⚠️"
violations TODAY: 1 ← REPRODUCES UNCHANGED
- Clause (3) is genuinely structural at word granularity. The port drops requirement (3) on the reasoning that an edit landing inside a word can only surface as a
replaceof that word, which is already fatal. A shared**prefix is part of a word token (**Thevs**For), so the differ has nothing to consume as equal. Confirmed, not assumed. - Clause (1) ports byte-for-byte, and so does its gap.
const SENTENCE_END = /[.!?:][\s"'*)]]*$/;is character-identical to the Python, and the trailing class does not admit⚠️. A run whose last token is**⚠️` fails clause (1) however the words are aligned.
Why it is latent and not red — and why that is the reason to fix it now
classify(BASELINE, SHARED_PLANNING_RULES) on origin/main returns 0 additions, 0 violations. Nothing is broken today. But the constant carries three ⚠️ markers, and the shape that trips this is "a rule inserted immediately before one of them" — which is the single most common edit this corpus receives. The motir-meta side spent three cards (MOTIR-2773, MOTIR-2934, MOTIR-2966) paying down exactly this class one pack at a time before MOTIR-2969 fixed the generator. The mirror is at card zero of that sequence and the fix is already written.
And the mirror has a tempting wrong escape the packs do not have. Its baseline is a refreshable golden copy, so a red clears with pnpm tsx scripts/refresh-planning-rules-baseline.ts. That command is correct for a deliberate re-wording and catastrophic for this one: refreshing past a benign alignment failure also refreshes past any real re-wording sitting in the same diff, and the fixture diff a reviewer is supposed to read would be dominated by the block that triggered it.
The repair — repair (A), and it is already validated at this granularity
MOTIR-2969 chose (A) canonicalise the run before classifying it over (B) widen the clauses, and named the refusal in COMPRESSION.md § Decision 1. Port (A), not (B):
- An insertion's boundary is not a property of the text. Removing
b[j1:j2]and removingb[j1-1:j2-1]leave the identical residual wheneverb[j1-1] === b[j2-1], symmetrically to the right — over WORD tokens here exactly as over characters there. - (B) is the limb with the mirror obligation, which is the argument against choosing it in either repo: admitting
⚠️intoSENTENCE_ENDmeans the same character lands in both files or the two classifiers diverge on the one clause they still share. (A) leavesSENTENCE_ENDuntouched in both.
Measured, with a word-level alignments() spliced into this port:
phase-deepen limb — run ends `. **⚠️` violations TODAY: 1 with alignments(): 0
kind-story limb — shared `**` opener violations TODAY: 0 with alignments(): 0
SAFETY — clause appended inside a sentence violations TODAY: 1 with alignments(): 1
SAFETY — one word of surviving text altered violations TODAY: 1 with alignments(): 1
Acceptance criteria
classify(BASELINE, SHARED_PLANNING_RULES)is run and quoted BEFORE any edit, on a pristineorigin/mainworktree: 0 additions, 0 violations — so the fix is measured against a green mirror and cannot be mistaken for clearing a real red.alignments()+asAddition()are ported intotests/helpers/corpusConservation.tsover WORD tokens, mirroringCOMPRESSION.conserve.py's functions of the same names, andclassifyreports the accepted alignment rather than the raw run.SENTENCE_END,SENTENCE_STARTandMIN_WORDSare unchanged — a diff that touches any of them has chosen refused form (B) and must say so.- The port's header block records the asymmetry on the record: clause (3) is structural at word granularity, clause (1) is not, and the alignment window is what closes the second — so the next reader does not re-derive it from the Python.
tests/planningRuleConservation.test.tsgains a case for the. **⚠️shape asserting it is an ADDITION, plus a companion asserting that a word of SURVIVING text altered in the same region is still fatal — the same pairing MOTIR-2969 added toSELF_TEST/ALIGNMENT_GUARD.conserve.py's SELF_TEST table is re-run against the port and every case returns the same verdict, which is the standing evidence that the word-unit deviation is representational and not semantic. The table is now 11 cases, not nine — pull the current one.pnpm vitest run tests/planningRuleConservation.test.tsis green and quoted, and the baseline fixture is NOT refreshed by this PR (git diff --statshowstests/fixtures/sharedPlanningRules.baseline.txtuntouched).
Repeat-defect discharge (plan-rules/kind-bug.md, gate 9)
Six non-archived bug cards now sit under MOTIR-1465 against the conservation checker — MOTIR-2773, MOTIR-2934, MOTIR-2966, MOTIR-2967, MOTIR-2969, and this one — well past the threshold of three, so limb (b) is owed rather than three independent root causes. This card IS the re-shape card's second half. MOTIR-2969 made the (b) argument for motir-meta in its own words — "the corpus does not need a third sweep card; it needs the generator fixed" — and fixed the generator there. The three that preceded it were per-pack symptom sweeps of ONE root shape (an alignment-dependent verdict); MOTIR-2967 is a different root (no baseline existed at all) and does not belong to the count on shape. This card carries the same generator fix across the open-core boundary, which is what makes the family closed rather than paused.
Context refs
tests/helpers/corpusConservation.ts—isAddition(two clauses),SENTENCE_END,diffWords,classify.classifyhands the run straight toisAdditionat the same line the Python did.tests/planningRuleConservation.test.ts— the suite, theREFRESHconstant, and the constant-not-the-file assertion.motir-metaPR #238 /prompts/plan-rules/COMPRESSION.conserve.py—alignments(),as_addition(), the 11-caseSELF_TESTandALIGNMENT_GUARDto port.motir-metaprompts/plan-rules/COMPRESSION.md§ Decision 1 — the chosen repair and the refusal of (B), including the mirror-obligation argument this card reuses.- MOTIR-2969 — the
motir-metahalf; its comment thread carries the first measurement of this asymmetry.