MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3905

Global lessons added since the day-one seed batch do not RETRIEVE — two rows queried near-verbatim are absent from the ranked set, and every global row `search_lessons` returns carries a `cmr11*` id

To Do
Description

Type: code · Executor: coding_agent · Repo pinned motir-ai — where the seed source, the migration generator and embedMissingLessons all live. If the diagnosis lands in motir-core's search seam instead, re-pin before building; the pin is a claim, not a finding.

Found 2026-08-29 while running MOTIR-3876, whose planning defect is described by a curated global lesson that the run could not retrieve. Filed here because it carries no dependency edge into that story — the project owes this fix, that story does not.

The measurement (two rows, near-verbatim queries, reproducible)

motir-ai src/seed/lessons.base.ts on origin/main holds 221 scope: 'global' rows.

  1. search_lessons(projectKey: 'MOTIR', limit: 50), queried with the near-verbatim body of the row whose sourceRef is MOTIR-3794 ("A container cannot hold the card that verifies the deployment its own merge produces…") returned 50 rows. The target was absent. Worst distance in the window: 0.751 — so it was not merely ranked below the cut, it was not in the ranked set at all.
  2. Of those 50: 45 global rows, and every single one carries a cmr11… id — the original day-one seed batch. The other 5 are tenant rows, three of them recent (cmta2k…, cmt9xy…, cmteai…), which are written through add_lesson / the capture path.
  3. Repeated at limit: 5 with the near-verbatim title of the MOTIR-3805 row ("A precondition that is a RELEASE cannot be carried by a dependency edge…"): five rows, all cmr11…, target absent, best distance 0.474 on an unrelated row.

So: a row written through the MCP retrieves; a row written through the seed migration does not. That is the shape of the defect, and it is the shape of a lot of work — every global lesson authored since the day-one batch, which is what the sourceRef: 'MOTIR-<n>' convention exists for.

What is NOT established, and must be the first deliverable

The CAUSE. Two candidates, and this card must distinguish them before it changes anything:

  • The rows are in the store without embeddings. scripts/generate-lessons-migration.ts says the release runs src/seed/embedMissingLessons.ts "immediately after", and embedMissingLessons.ts's own header calls an unembedded row a silent failure — so the failure mode is anticipated in the code and the backfill may simply not be running, or may be failing without failing the release.
  • The migrations have not been applied. Five *_lessons_base_data migrations landed between 2026-08-28 18:19 and 2026-08-29 00:01; fly.toml's release_command applies them, so this requires a release not to have happened — check the deployment, not the repository.

Read it from the deployed database, inside a machine (DATABASE_URL_UNPOOLED): count Lesson rows by scope, and count how many have a null embedding. That single query separates the two candidates and is the reproduction this card is built on.

Why it matters more than a missing row

The corpus has two halves, and only one of them is working. Lessons are still being WRITTEN — carefully, with a curation discipline, a totality suite and a per-row routing decision — and every one of them since the seed batch has been written into a corpus that no planner can read. Every motir run and every planning pass is calling search_lessons, getting outcome: "matched" and a plausible ranked list, and being handed the day-one corpus while believing it has been handed the whole one. A retrieval that returns the wrong rows is indistinguishable from one that returns the right ones, which is why this stood.

It has already cost at least one concrete failure: the MOTIR-3794 row states the exact defect that shipped into MOTIR-3876 a day later — the container that cannot hold the card verifying its own merge's deployment — and neither the plan pass that authored the story nor the run that refused it could retrieve it. See MOTIR-3904.

Acceptance criteria

  • The reproduction runs first and is quoted: the deployed-database counts (total Lesson rows, by scope, and how many carry no embedding), read inside a machine, with the timestamp — before any fix. The two candidate causes above are decided by that reading, and the losing one is stated as excluded.
  • The fix makes a row delivered by a generated *_lessons_base_data migration retrievable by search_lessons, whatever the cause turns out to be — the backfill actually running on release, or the release actually happening, or both.
  • A regression guard exists that fails when a lesson is reachable in the store but not in a search. Not a test that the backfill function works in isolation: the property that broke is written ⇒ retrievable, and it broke while every unit around it stayed green.
  • The existing backlog is backfilled, and the proof is a query, not a run of the script: search_lessons returns the MOTIR-3794 row for a query naming a container that cannot hold a post-deploy child, and the MOTIR-3805 row for one naming a precondition that is a release. Both are quoted in the close-out.
  • The close-out states how many global rows were unretrievable at the time of the fix, so the blast radius is on the record rather than inferred from this card's title.
  • ≥90% coverage on the files this PR touches.

Context refs

  • motir-ai/src/seed/lessons.base.ts — the 221 global rows and the sourceRef: 'MOTIR-<n>' convention for new ones
  • motir-ai/src/seed/embedMissingLessons.ts — the backfill, and its own note that an unembedded row is a silent failure
  • motir-ai/scripts/generate-lessons-migration.ts — the generator, and its claim that the release runs the backfill immediately after
  • motir-ai/prisma/migrations/*_lessons_base_data/ — five between 2026-08-28 and 2026-08-29
  • motir-ai/fly.toml — the release_command that applies them
  • motir-core lib/mcp/tools/searchLessons.tsPOST /v1/lessons/search — the read path, if the cause is on that side
  • MOTIR-3904 — the planning bug this was found beside; its defect is the one the unreachable row describes
  • MOTIR-3466 — the story that shipped search_lessons