The planner's two corpora — RULES say what is RIGHT, LESSONS say what went WRONG and are retrieved per kind/type
A RULE tells the planner what is RIGHT — the method: lay the titles first, then deepen; a card carries both bodies; a story is a vertical slice. It is incident-agnostic, it fires every time, and it is always in front of the planner.
A LESSON tells the planner what went WRONG — one incident and its takeaway. It is categorized, and when the planner deepens a card of a given kind/type it decides the words to search for and pulls the ones that apply.
Today the rules corpus is mostly lessons. Measured on origin/main 2026-08-20, counting cautionary register (the tell, the trap, fixture, missed, failed, reads as) against prescriptive register (imperative LIST / CARRY / VERIFY / NAME, in THIS order, MUST carry):
| pack | lines | wrong-register | right-register | ratio |
|---|---|---|---|---|
core.md | 811 | 63 | 22 | 2.9x |
phase-deepen.md | 685 | 61 | 2 | 30.5x |
kind-bug.md | 176 | 22 | 0 | 22x |
op-replan.md | 238 | 18 | 0 | 18x |
kind-container.md | 51 | 0 | 2 | 0x |
type-code.md | 59 | 1 | 0 | 1x |
| TOTAL | 3 513 | 253 | 33 | 7.7x |
The inversion is exact: the packs that are prescriptive are the small ones, and the 685-line pack that is 41% of the shipped SHARED_PLANNING_RULES and 49% of the largest deepen cell runs 30:1 against.
The cost lands on customers, not on the runbook. motir-meta is one person's working file; SHARED_PLANNING_RULES is injected into every plan the product generates — avg deepen cell 115 636 chars, ~29k tokens, of which 51% of cells (48 of 94) carry ~42k chars of code-shaped checks they cannot use: a plan/task/copy pass is handed rules about grepping origin/main for call sites and DTOs, and so are task/translate, task/design, task/research and every epic/story deepen.
The target shape
Each of the 86 rule units has a positive form buried in it. BOTH content axes is 100 lines; the rule inside it is descriptionMd is what to do, explanationMd is why it matters, both required at deepen — the other ~95 lines are how people got it wrong.
- 86 rules stated positively at 5-8 lines each ≈ 500-700 lines, against 3 513 today.
- The ~2 800 lines of "what went wrong" become categorized lessons, retrieved by query at deepen time.
The retrieval half is largely built already: selectForInjection({ aiProjectId, queryText, phase, limit }) embeds the query, filters by category, ranks by cosine distance, renders title + howToApply, and injects nothing on an empty selection. Three gaps close it — categories too coarse to key on kind/type, a queryText set by the handler rather than chosen for the card, and a store that stops at notes.html #158 while the corpus is at #353.
Acceptance criteria
- Lessons carry a kind/type category and selection filters on it; a
designdeepen and acodedeepen retrieve different sets. - The deepen pass composes its own lesson query from the card in front of it.
notes.html#159-#353 are distilled into the store, categorized, and the seed has RUN against the deployed database.phase-deepenis restated in positive form and its cautionary body lives in the lesson store; the pack's cautionary-register count drops from 61 and its prescriptive count rises from 2.- The remaining packs follow the same shape.
- No guidance is deleted: every migrated paragraph is retrievable, and the story that migrates it says where each one went.
Context refs
motir-aisrc/services/lessonService.ts—selectForInjection,listForInjection,MistakeType, the 90-day retirement clock.motir-aisrc/llm/planningRulePacks.ts—CORPUS_ORDER, the 34 rule constants,composePlanningRules.motir-aisrc/seed/lessons.base.ts— 72 rows, last written 2026-07-26, stops atnotes.html#158.motir-metaprompts/plan-rules/— the 15 packs, 3 513 lines;CORPUS-MAINTENANCE.mdholds RULES vs LESSONS and is loaded by no planning pass.motir-metanotes.html— 353 entries, the lesson corpus of record, retrieved by the MOTIR-819 rule at every run.