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-3299Done

Lesson gains kinds, types and phases — mirroring the work item and the rule-pack selector; the topical categories are dead data

The existing categories String[] is dead data. Proven on origin/main:

  • selectForInjection builds { aiProjectId, queryText, mistakeTypes: [phase, 'planning_craft'], limit }it never passes categories, so no planning selection has ever filtered on them.
  • captureMistake writes categories: [] — every runtime-captured lesson carries none.
  • The only values that exist are the hand-written topical tags on the seed rows (ordering, design-system, task-type), which nothing queries.

A topical tag also answers the wrong question. It says what a lesson is about; retrieval needs to know which card it applies to.

So give the lesson the same three axes the rule packs already route on. MANIFEST.md's selector is pack(operation, phase, kind, type) — the two corpora should share one routing model rather than invent a second:

columnvocabularymeaning
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)

Set-valued, empty means applies-to-all. The corpus's own routing is set-valued — type ∈ {code, chore, deploy} loads type-migration.md — so a migration lesson carries all three types. A scalar could not express it, and the fallback would be "applies to everything", which is the over-injection this epic removes.

⚠️ phase is an overloaded word here. PlannerPhase already exists in lessonService and means onboarding_planning | regular_planning — which pass is running, not which part of the card is being written. Do not reuse that type or that name; mistakeTypes keeps its current job as the consumer pre-filter.

Ship the columns and tag the existing rows in a forward-only, idempotent data migrationrelease_command runs prisma migrate deploy, so it reaches production on release.

Acceptance criteria

  • Lesson carries kinds, types and phases, all set-valued, all defaulting to empty; empty on an axis means unconstrained on it.
  • Each vocabulary matches the work item's, and phases is exactly skeleton / deepen; a test pins all three so a typo fails the build rather than silently never matching.
  • The new axes are independent — a lesson may constrain one and leave the others empty.
  • A forward-only, idempotent data migration adds values to the existing rows; it no-ops on a fresh database and on a re-run.
  • The dead categories column is left inert and its disposition recorded — dropped in a later card, or kept with a stated reason. It is not silently repurposed.
  • prisma migrate deploy applies cleanly on the test database.

Context refs

  • motir-ai src/services/lessonService.tsselectForInjection (no categories), captureMistake (categories: []), and the PlannerPhase name to avoid.
  • motir-ai src/repositories/lessonRepository.ts — the existing && overlap clause, the model for the new axes' filtering.
  • motir-ai prisma/schema.prisma — the Lesson model.
  • motir-meta prompts/plan-rules/MANIFEST.mdpack(operation, phase, kind, type), the selector these mirror.
  • motir-ai fly.toml release_command · .github/workflows/ci.yml:107.