Planning bug: a coverage card enumerated uncovered arms as work items without asking which of them a test can REACH — three of five bullets were dead code
Repo · motir-meta (the rule change) · found by the run of MOTIR-3123, PR moooon-B-V/motir-core#2159.
MOTIR-3123 turned a v8 coverage report into an acceptance criterion — "every arm listed above has a named test" — over a list of five bullets read straight off coverage-final.json. The list is factually correct: all five arms were at zero. Three of them are also UNREACHABLE, so a third of the card's acceptance asked for fixtures the code cannot be driven into.
What is unreachable, and why
lib/services/planValidityService.ts, against buildProjection on origin/main:
- the parent-ready cascade's
if (!child) continue—childrenByParentis derived from the FINALnodesmap, so every child id it holds resolves; if (seen.has(key)) continuein the subtree walk and in the forest walk — a member's blocker set is aSetof ids and every projected node's identifier is distinct, so one walk cannot produce the same<item> <blocker>key twice.
The same invariant kills three arms the card did not list (if (!blocker) continue ×3) and one more (addBlocker's if (!member) return). Only the addBlocker de-duplication is live, because there one member is reached through several probes — and the card did not distinguish it from the two dead ones, describing all three as "the de-duplication that stops one blocker being reported twice".
The shape of the miss
This is notes.html #175 — verify a card's NEGATIVE premises exactly as hard as its positive ones — arriving through a channel #175 does not name. #175's fixture was prose ("the DB permits a cycle"). Here the false premise is a number: 0 in a branch-coverage column reads as nobody has tested this, and it is equally consistent with nothing can test this. A coverage tool measures execution; it has no opinion about reachability, and its output is the most authoritative-looking input a test card can be built from. Nearest neighbours: #231 (a measurement from an instrument nobody had tried to break became three cards' premises) and #307 (a number whose two halves came from different objects).
Cost was small — one run, discharged by asserting the invariant instead (notes.html #175 corollary 2) plus three one-line v8 ignore directives citing that test. It is filed because the pattern is cheap to repeat: vitest.config.ts gates ~200 files, and "add file X to the gate and cover its red arms" is a card shape this project writes routinely.
The correction
A type: test card whose criteria are derived from a coverage report says, per arm, whether a test can REACH it — and where it cannot, the criterion is the INVARIANT that makes it dead, not the fixture. The authoring-side home is plan-rules/type-test.md; the run-side backstop is run.md's falsified-premise rule, which already says report-don't-re-scope but is written for a card's prose.
Acceptance
The LESSON half already shipped —
notes.html#325 ("a coverage zero is not evidence a test can reach the arm"), motir-meta PR #260, written by the run that filed this bug, asrun.mdrequires. What is left is the RULES half, which is why this card carries no id in that PR's title.
motir-meta/prompts/plan-rules/type-test.mdcarries the rule: a coverage-derived criterion owes a reachability verdict per arm, sorted into defensive arms (if (!x) continue,?? 0, an unreachabledefault:, a walk's re-entrancy guard — uncovered because unreachable far more often than because anyone forgot them) and rule-bearing arms (a filter, an early return, a comparator). Where an arm is dead, the criterion is the invariant that kills it plus an ignore directive citing the test that asserts it — never a fixture nobody can build.- The same rule says a guard appearing twice is not necessarily the same guard: name an arm by what makes it FIRE, not by its text. (Fixture: the identical three words were dead in two walks of
planValidityServiceand load-bearing in a third.) plan-rules/MANIFEST.mdroutes to it, and the correspondingSHARED_PLANNING_RULESmirror inmotir-aiis updated if that pack is mirrored there — a rule has two homes.