Two coupled planning defects, found on 2026-08-12 while running MOTIR-2789. Filed together because the second is what kept the first invisible.
MOTIR-2755 is titled "Adopt the motir_app non-bypass role — the tenant reads, the test fixtures, and the deployed runtime". Its 25 children: 20 fixture batches, a guard, an adjudication card, two small read fixes, the flag retirement, and the deployed cutover.
Nothing in that set binds the read surface. It is now measured at 55 unbound reads across 20 services — reportsService.ts and savedFiltersService.ts contain zero context wrappers in the entire file. The story would have gone done with its first named deliverable undelivered, and then MOTIR-2515 would have cut production over to a role under which most reporting surfaces return empty.
The tell, and it is cheap: a story title with a comma-separated LIST of deliverables is a coverage checklist. Read each item against the child set and name the child that owns it. Two of three had owners; the first had none. No existing gate reads a title as a promise — gate 12 (the journey audit) walks the verification_recipe, and this story's recipe was about the fixtures, so the audit passed while the title's first clause had no owner anywhere.
MOTIR-2789's scope is adjudicate the sites — write a verdict per read. But it was given this acceptance criterion:
TEST_DB_APP_ROLE=1over every migrated batch shows no remaining failure of the unbound-read family.
That is not a property of an adjudication. It is the completion condition of the repair, which is 55 sites across 20 services — now MOTIR-2796, a 34-point story. A card scoped to CATALOGUE a defect class cannot be closed against the class's ABSENCE.
This is gate 14 (AC satisfiable inside the card's own boundary) on an axis its three limbs miss. Limb (a) asks WHO performs the crossing step — the same agent, so it passes. Limb (b) asks WHICH SURFACE — the card declares no exclusion list for it to contradict. Limb (c) asks WHEN — nothing here reads on post-merge state. The criterion is unsatisfiable on a fourth axis: SIZE. It names work two orders of magnitude larger than the card it sits on, and every existing limb is blind to that.
Why it survived: it reads as the natural verification of the card's own subject. A catalogue of a defect class and the elimination of that class use the same nouns, so an AC written in those nouns looks like it belongs — and the card's own header, which is careful and well-argued about evidence, argues for the catalogue while the criterion demands the cure.
A card whose deliverable is an ENUMERATION — a catalogue, an inventory, an audit, an adjudication, a survey — may NOT carry an acceptance criterion that asserts the ABSENCE of what it enumerates. Its criteria are properties of the LIST (complete, evidence-backed, each entry disposed); the absence of the class belongs to the card that repairs it, and if no such card exists, the enumeration card's real deliverable is FILING it.
The tell is lexical and greppable: on a card whose verb is inventory / catalogue / adjudicate / audit / enumerate / survey / measure, any criterion containing "no remaining", "shows none", "is zero", "is empty", "all … are fixed" is on the wrong card.
And for defect 1:
A story title carrying a LIST of deliverables is a coverage claim — walk each item against the child set and name the owning child, at authoring time and on every re-plan. A clause with no owner is the gap; the
verification_recipewill not catch it, because a recipe written about one clause passes the journey audit while the others go unowned.
Both belong in plan-rules/core.md (gate 14 gains the SIZE axis; gate 12 or the story-slicing pack gains the title-list check) and mirrored into SHARED_PLANNING_RULES in motir-ai — the two-homes rule.
prompts/plan-rules/core.md carries both rules, each with its lexical tell, landed via a branch + PR (never a direct commit to main).motir-ai's SHARED_PLANNING_RULES carries the same text — TWO PRs, one per repo, per the two-homes rule.notes.html carries the numbered entry for both.