Found at run time on MOTIR-2508, 2026-08-26. The correction is already applied to that card (its second criterion was amended on the record); this card is the telemetry, not the work.
MOTIR-2508's header reads "One PR, one file." Its second acceptance criterion repeats it:
vercel.jsonis deleted, and it is the ONLY file in the diff.
Its fourth acceptance criterion then requires a second file in the same pull request:
docs/decisions/application-hosting.md's two MOTIR-2508 clauses (added by MOTIR-2393's PR) are updated to past tense, so the ADR does not describe a pending step that has happened.
Both cannot hold. A run that reads them in order builds to criterion 2, ships a one-file diff, and silently fails criterion 4 — and a one-file diff is exactly what the card's own header would have told a reviewer to expect, so nothing downstream flags it.
The "ONLY file" clause is a real scope guard with a real target: vercel.json sits in a story that has been deleting Vercel residue for weeks, and the clause exists to stop a run sweeping in .vercel/, .gitignore entries or the frozen scripts/plan-seed prose that also names the file. What it should say is that vercel.json is the only file deleted, and the only change outside docs/ — which is compatible with criterion 4 and guards the same thing.
The card was carved out of MOTIR-2393 at run time (motir-core#1983). Its header sentence and criterion 2 were written when the card's whole content was the deletion; the ADR criterion was appended afterwards, because that same run had just added the two MOTIR-2508 clauses to application-hosting.md and knew they would need sweeping. Neither pass re-read the other's clause. The general shape: a card that grows a second deliverable after it is authored owes a re-read of every clause that COUNTS its deliverables — "one file", "one PR", "the only change", a bulleted table with a total.
vercel.json is the only file DELETED, and the only change outside docs/", and its header sentence corrected to match. The contradiction is stated in place rather than quietly dropped.motir-core, which is why this card is pinned motir-ai.Strip every local identifier from the takeaway — repository, work-item key, file path, rule pack, gate number, surface, person, date — and what is left is:
A clause that COUNTS a card's deliverables — one file, one PR, the only change, a table with a total — is a claim about the whole card, so it goes stale the moment the card grows a deliverable. Adding one is two edits: the new criterion, and the re-read of every counting clause.
That is still a complete, actionable takeaway any project's planner could hit, so by the strip-the-identifiers test it is GLOBAL. A global lesson is a curated row — motir-ai src/seed/lessons.base.ts plus its generated migration — and is deliberately not writable by add_lesson. Writing it into this tenant's store instead would help this project and hide the pattern from every other one, which is the misread the test exists to prevent.
motir-ai src/seed/lessons.base.ts carrying the takeaway above, with sourceRef MOTIR-3607, and title / body / howToApply naming no repository, work-item key, file path or date (the seed suite asserts it) — the specifics go in why.phases: ['deepen'] (a counting clause is written while a body is), and kinds / types left unconstrained, because the failure is about how a card is WORDED and not about what it ships.pnpm lessons:generate-migration is run and the emitted migration is committed in the same pull request as the seed row.