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

No lesson CORRECTION can ever ship — the generator emits an UPDATE that its own delivery guard rejects as destructive

Found while running MOTIR-3391 (the kind-leaf / kind-bug mirror card), which is the first card in the corpus's history to try to CORRECT an existing BASE_LESSONS row rather than add one. It cannot.

What happens

scripts/generate-lessons-migration.ts has a fully-built, fully-documented CORRECTION path. Change any field of an existing row and re-run it:

[lessons:migration] wrote prisma/migrations/<ts>_lessons_base_data/migration.sql
  — 0 new row(s) (0 global, 0 tenant), 1 correction(s), 1 of which re-embed.

The emitted SQL is an UPDATE "Lesson" SET … WHERE "sourceRef" = '…', ending in "embedding" = NULL so the release's embedding sweep re-ranks the row. The generated header describes exactly that, and calls itself a forward-only data migration.

tests/lessonsMigrationDelivery.test.ts then fails it:

× every insert is ON CONFLICT ("sourceRef") DO NOTHING, and none computes an embedding
  a lesson data migration must only INSERT: UPDATE "Lesson" SET

The guard filters statement-initial /^\s*(DELETE|TRUNCATE|DROP|ALTER|UPDATE)\b/i and asserts the list is empty. UPDATE is in that list, and the correction path emits nothing else.

Why it has never fired

No correction had ever been generated. Re-running the generator on origin/main reports "nothing to emit — all 182 curated rows are already carried by a generated migration, at their current content", so the drifted branch had never produced a statement for the guard to see. Every migration in the directory is inserts only.

The two mechanisms disagree, and the guard is the one that is wrong

  • The generator's header: "corrected rows are handed back to the release's embedding sweep, whose work list is exactly 'rows with no vector'".
  • src/seed/lessons.base.ts's MERGED_ENTRIES comment instructs authors to do this: "Where a merged entry sharpens the covering row, sharpen that row's CONTENT — the drift/refresh path carries content changes safely."
  • tests/lessonsBaseSeed.test.ts has passing tests for exactly this behaviour — "refreshes a lesson whose curated content DRIFTED — a re-seed applies a corpus correction (+ re-embed)".

So three shipped mechanisms tell an author to correct a row, and the delivery guard silently makes the correction undeliverable.

The guard's own stated rationale is about DELETION, not about updates: "Forward-only: a data migration that deletes is not a delivery, it is a reset." UPDATE was swept into the pattern alongside the genuinely destructive verbs, and nothing distinguished them because no correction existed to notice.

Acceptance criteria

  • tests/lessonsMigrationDelivery.test.ts accepts the correction shape the generator actually emits, and ONLY that shape: an UPDATE "Lesson" keyed on WHERE "sourceRef" = …. An UPDATE with no sourceRef predicate, or one touching another table, still fails.
  • DELETE / TRUNCATE / DROP / ALTER remain rejected outright — the guard's deletion rationale is unchanged and its protection is not weakened to pass.
  • A test proves an UPDATE that does NOT key on "sourceRef" is still rejected, so the narrowing is asserted rather than assumed.
  • The guard's "embedding" assertion is reconciled with the correction shape: a correction legitimately writes "embedding" = NULL, which the current expect(text).not.toMatch(/"embedding"/) also rejects. Permit the literal NULL write and keep rejecting a vector literal.
  • A correction generated from a real edit to src/seed/lessons.base.ts passes the whole suite end to end, demonstrated by the test rather than asserted in prose.
  • The two corrections this bug blocked are applied in the same PR or in a named follow-up: notes.html #319's body gains the usefulness-is-not-the-test arm, and its kinds: ['bug'] is widened to unconstrained.

Context refs

  • motir-ai tests/lessonsMigrationDelivery.test.ts — the destructive filter, and the "embedding" assertion beside it.
  • motir-ai scripts/generate-lessons-migration.ts — the drifted branch, hashes(), and the generated header describing the UPDATE contract.
  • motir-ai src/seed/lessons.base.tsMERGED_ENTRIES' instruction to sharpen a covering row's content, and the notes.html #319 row carrying the blocked widening in a comment.
  • motir-ai src/seed/embedMissingLessons.ts — the sweep that consumes a nulled vector.
  • motir-ai tests/lessonsBaseSeed.test.ts — the two passing drift/re-embed tests the delivery guard contradicts.