Filed from motir run MOTIR-3296 (2026-08-21), which stopped at guard #4 before creating a worktree. The re-plan is plan cmt2wqx0f0005i3n8baz5uia9.
MOTIR-3296 scoped a corpus backfill as notes.html #159–#353, on the premise — stated in its first paragraph — that src/seed/lessons.base.ts "stops at notes.html #158".
"Stops at #158" is true and means nothing like what the scope needed it to mean. The seed's coverage is a SET, not a prefix: its sourceRefs account for #1–#111 ∪ {#156, #157, #158}. The maximum is 158; the set has a 44-entry hole at #112–#155 that has never been seeded and never excluded.
Measured on origin/main, motir-ai @ f682d19:
grep -c "sourceRef:" src/seed/lessons.base.ts → 70 (the card said 72)
distinct #n across all sourceRefs → 112, max 158
missing from 1..158 (minus EXCLUDED_ENTRIES {1,19}) → 112..155
notes.html entries (motir-meta origin/main) → 355, contiguous (the card said 353)
Three numbers in the premise, and the arithmetic on the one that mattered was answering a different question. A max() over a set is a real measurement; treating it as a coverage boundary is an inference, and the inference was never written down as one — so no later reader had anything to check.
tests/lessonsBaseSeed.test.ts:83 asserts seeded ∪ EXCLUDED == 1..111 exhaustively, then for (const n of [156, 157, 158]). #112–#155 is exempt by construction, and so is every entry written from now on. The corpus drifted for 25 days under a green suite. The card and the test made the same mistake independently — both wrote "the newest N" where the invariant needed "all of them".MOTIR-1948 — todo, ready, under epic MOTIR-2200, filed 2026-07-31 — covers the same backfill and the totality-guard fix, and had been kept current by five of Yue's comments routing individual entries into it. It also had the range right, naming #112–#119, #121–#155, #159–… explicitly.
plan-rules/core.md gate 8's TAKER limb already asks "does any EXISTING card already claim this deliverable?" on every card AUTHORED, not only on a re-plan. It was not run. One search_work_items for lessons.base.ts — a path in MOTIR-3296's own context refs — returns MOTIR-1948 in the first page.
Note the compounding: the duplicate card was also the correction. Finding it would have fixed the population in the same read.
Gate 2's QUANTITY limb requires that a number in a criterion be produced by a measurement run at the criterion's own scope. This case passes that reading and still fails, because the measurement WAS run — max(sourceRef) — and was arithmetically true of the wrong question. The gap is a type error rather than a missing measurement:
A COVERAGE claim may not be stated as a BOUNDARY. "Stops at N", "up to date through N", "covers everything before N" assert an interval; coverage of a corpus is a SET, and the two agree only if the set is contiguous — which is a separate fact requiring a separate check. Write coverage as the set (or as
total − covered), and derive the population fromsource \ covered, never fromsource > max(covered).
The same shape generalizes past corpora: a migration "already applied through version N", a locale catalog "translated up to key N", a test suite "covering everything since release N". In each, the gap-in-the-middle is invisible to a check written against the head.
plan-rules/, in the pack that owns gate 2's QUANTITY limb, as a limb of it rather than a new gate — with the discharge stated as derive the population by set difference, and never from a maximum.SHARED_PLANNING_RULES needs the same limb is decided and stated — mirrored, or explicitly not, with the reason (the two-homes rule).notes.html lesson for this incident exists and is cited.cmt2wqx0f0005i3n8baz5uia9.motir-ai src/seed/lessons.base.ts, tests/lessonsBaseSeed.test.ts:83 — the set, and the test that exempts its hole.motir-meta prompts/plan-rules/core.md — gate 2's QUANTITY limb, and gate 8's TAKER limb.