Planning bug: a story planned building a column that already shipped, and a `manual` card the pipeline can do — the lesson forbidding the second was IN the store, where the runbook planner cannot read it
Discovered 2026-08-21 by Yue, on MOTIR-3296: "we will add new lessons in the future, so we should add lesson with db migration which will run automatically in CI." Verifying that instruction surfaced a second, larger defect in the same plan.
Defect 1 — a negative asserted, not grepped
MOTIR-3294 was authored claiming the lesson store has "no way to say which kind or type of card a lesson applies to", and its children planned a schema change and migration to add one. All of it already ships:
| planned as new | on origin/main |
|---|---|
| a set-valued category column | Lesson.categories String[] |
| set-membership filtering | AND "categories" && ARRAY[…]::text[] |
| empty ⇒ universal | the clause is OMITTED when no categories are supplied |
| categorized rows | ['ordering','design','task-type','tooling'] on seed rows |
The real gap is two orders smaller: the values are topical rather than structural, and SelectForInjectionInput has no categories field, so the planner's one shared helper cannot reach a filter that exists one layer below it.
This is an occurrence of phase-deepen.md's NEGATIVE limb — a negative is the only claim you can write without looking at anything — not a new rule. Bump that limb's warrant count; do not add a sibling rule. One git grep categories on the model would have closed it.
Defect 2 — a manual card the pipeline can do, forbidden by a lesson already in the store
MOTIR-3307 was authored type: manual, executor: human: run the seed against the deployed database. fly.toml's release_command is pnpm prisma migrate deploy, so the pipeline applies data migrations on every release with no operator.
lessons.base.ts already carries the lesson that forbids exactly this:
"check whether the service's deploy pipeline already runs migrations automatically on release … ship environment-specific DATA as a forward-only, guarded, idempotent data migration rather than a hand-run script … Reserve manual/human for work the pipeline genuinely cannot do — dashboards, secrets, third-party accounts — not for data a migration can carry."
It could not reach the planner who needed it. The runbook searches notes.html (the MOTIR-819 rule); this lesson lives in the product's retrieval store, which the runbook never queries. A lesson that is correct, on-point and unreachable is the exact failure MOTIR-3293 exists to fix, demonstrated against the plan for that epic.
Acceptance criteria
- The NEGATIVE limb's warrant count is bumped with this occurrence; no new rule is added for defect 1.
run.md's mistakes-corpus step (MOTIR-819) states that the runbook's lesson search coversnotes.htmlONLY, and that the product's store holds lessons the runbook cannot see — so a planner authoring againstmotir-aireadslessons.base.tstoo, until the epic makes one corpus of them.- The store's deploy-pipeline lesson is present in
notes.htmlas well, or the card records that it is and names the entry. notes.htmlcarries the matching entry for this incident, with both defects.- Lands via a branch + PR; no direct commit to
main.
Context refs
motir-aiprisma/schema.prisma—Lesson.categories String[], the column planned as new work.motir-aisrc/repositories/lessonRepository.ts— the overlap clause and its omit-when-empty behaviour.motir-aisrc/seed/lessons.base.ts— the deploy-pipeline lesson, and the rows already carrying categories.motir-aifly.tomlrelease_command·.github/workflows/ci.yml:107.motir-metaprompts/plan-rules/phase-deepen.md— the NEGATIVE limb this is an occurrence of.motir-metaprompts/run.md— the MOTIR-819 mistakes-corpus step that searchesnotes.htmlalone.