Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-3294Done

A lesson is keyed like a work item — kinds, types and phases, the same selector the rule packs route on

The foundation every other story routes through: until a lesson can be keyed to the card being planned, nothing can leave the rules corpus — moving a paragraph out would only make it unreachable.

The existing category axis is dead data, proven on origin/main: selectForInjection never passes categories, captureMistake writes categories: [], and the only values in existence are hand-written topical tags on the seed rows that nothing queries. A topical tag also answers the wrong question — what a lesson is about, never which card it applies to.

So key the lesson the way the rule packs already route. MANIFEST.md's selector is pack(operation, phase, kind, type); the two corpora should share one routing model rather than invent a second:

axisvocabularymeaning
kindsepic · story · task · bug · subtaskwhich card kind the mistake happens on
typesthe fourteen-member work typewhich work type
phasesskeleton · deepenwidening — planning the children — vs deepening — writing the description

The phase axis is the one with no predecessor and it discriminates hardest. Mistakes made while laying a level's children are a different family from mistakes made while writing a card's body: the first are about shape, edges and coverage; the second about criteria, sizing and claims. Today a planner writing a description receives all the widening lessons.

All three are set-valued, empty means unconstrained — the packs' own routing is set-valued (type ∈ {code, chore, deploy}), so a migration lesson carries three types and a scalar would force it to "applies to everything".

⚠️ phase is overloaded. PlannerPhase already means onboarding_planning | regular_planning — which pass is running, not which part of the card is being written. mistakeTypes keeps that job as the consumer pre-filter; the three axes sit underneath it.

Acceptance criteria

  • Lesson carries kinds, types and phases, set-valued, defaulting to empty; empty on an axis means unconstrained on it, and the three are independent.
  • Each vocabulary matches the work item's; phases is exactly skeleton / deepen. A test pins all three so a typo fails the build rather than never matching.
  • listForInjection applies one overlap clause per axis, each omitted when its axis is empty; selectForInjection carries all three and passes them down.
  • mistakeTypes behaviour is unchanged, asserted.
  • A test proves the axes narrow independently, that a lesson tagged for three types matches a query for one of them, that a skeleton query returns widening lessons and not deepening ones, and that an unconstrained lesson reaches every query.
  • Existing rows are tagged by a forward-only, idempotent data migration; the dead categories column's disposition is recorded rather than silently repurposed.

Context refs

  • motir-ai src/services/lessonService.tsselectForInjection (no categories), captureMistake (categories: []), PlannerPhase.
  • motir-ai src/repositories/lessonRepository.ts — the && overlap clause with omit-when-empty, the pattern the three axes follow.
  • motir-ai prisma/schema.prisma — the Lesson model.
  • motir-meta prompts/plan-rules/MANIFEST.mdpack(operation, phase, kind, type).
  • motir-ai fly.toml release_command · .github/workflows/ci.yml:107.