MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3607

Planning bug: MOTIR-2508 asserts "ONE PR, one file" in two criteria and then requires a second file in a third

Done
Description

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.

What the card said

MOTIR-2508's header reads "One PR, one file." Its second acceptance criterion repeats it:

  • vercel.json is 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.

Why the card is right in substance and wrong in wording

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.

How it got here

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.

The fix

  • Already applied, 2026-08-26, by the run that found it: MOTIR-2508's criterion 2 amended on the record to "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.
  • Remaining, and it is this card's whole deliverable: the LESSON row. It is not in motir-core, which is why this card is pinned motir-ai.

⚠️ The lesson is GLOBAL, not tenant — the scope was decided, not defaulted

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.

Acceptance criteria

  • A row is added to 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.
  • Its routing axes are set deliberately rather than left empty: 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.
  • MOTIR-2508's second acceptance criterion no longer contradicts its fourth — already true; verified by reading the card, which is where the amendment landed.

Context refs

  • MOTIR-2508 — the card, and where the amendment is recorded.
  • MOTIR-2509 — the earlier planning bug from the same carve-out, which is what created MOTIR-2508.