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

Lessons reach production by DATA MIGRATION, not a hand-run seed — the generator, and the embedding sweep that makes an inserted row rankable

Replaces the hand-run seed. Nothing here is manual.

fly.toml's release_command is pnpm prisma migrate deploy and ci.yml runs it too, so a data migration reaches production on every release with no operator. That is the delivery path for lessons — now and for every lesson added later, which is the point: adding one should be a normal reviewable change, not an ops chore somebody remembers.

Two halves, and the second is what makes the first work.

1. The generator. A script reads lessons.base.ts — which stays the single authored, typed, reviewable source — and emits a forward-only data migration inserting its rows, ON CONFLICT ("sourceRef") DO NOTHING. The generated migration is committed like any other. Adding a lesson then means: write the row, run the generator, commit both.

2. The embedding sweep. listForInjection ranks by cosine distance against embedding, and setEmbedding is called only from the create and edit paths — nothing embeds a row that arrived by SQL. A migration-inserted row is therefore present, healthy and permanently unrankable, which is a silent failure: the store's row count looks right and the lesson never appears.

So add an idempotent sweep that embeds every lesson whose embedding is null, batched through the existing embedding service, and run it where the release already goes. It must be safe to run repeatedly and must no-op when nothing is missing.

Acceptance criteria

  • A generator emits a forward-only migration from lessons.base.ts, inserting rows with ON CONFLICT ("sourceRef") DO NOTHING; the generated file is committed.
  • Re-running prisma migrate deploy against a seeded database inserts nothing and removes nothing, asserted.
  • The migration no-ops cleanly on a fresh database and does not require the gateway — no embedding is computed inside it.
  • A sweep embeds every lesson with a null embedding, batched, idempotent, and a no-op when none are missing.
  • A test proves the round trip: a row inserted by the migration is NOT returned by listForInjection before the sweep and IS returned after it.
  • No card in this story is manual; the deployed row count is verifiable from a normal release rather than from an operator's terminal.

Context refs

  • motir-ai fly.toml release_command, .github/workflows/ci.yml:107prisma migrate deploy on release and in CI.
  • motir-ai src/repositories/lessonRepository.tssetEmbedding (raw ::vector write) and the cosine ranking query.
  • motir-ai src/services/lessonService.ts:118, :638 — the only two callers of setEmbedding today.
  • motir-ai src/services/embeddingService.ts — the batch path, whose comment notes a backfill is a handful of round-trips rather than one per row.
  • motir-ai src/seed/lessons.base.ts, src/seed/seedBaseLessons.ts, scripts/seed-lessons.ts — the authored source and the seed this supersedes.