refactor(notes): a snippet's and lesson's stored title is its name; the trigger joins it only in the embedded document (milestone 427)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / integration (push) Successful in 52s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m35s
CI & Build / Build & push image (push) Successful in 32s

The title was `subject — trigger` because the stored title WAS the
embedded one, and the join is what makes these kinds rank on the
situation they apply to (#2485). Every surface that shows a title then
showed the trigger too -- menus, lists and search rows ran to kilobytes.

- embeddings.document_title(title, note_type, data, body) joins the
  trigger from `data` (body fallback) at embed time. Idempotent: an
  un-migrated composed title comes out the same, never doubled. The
  embed path, the startup backfill and the dedup gate's semantic signal
  all use it, so the embedded text -- and every vector -- is unchanged.
- Writers store the subject: snippet create/update (service, REST, MCP)
  and lesson_document. Both compose_title helpers are removed.
- Readers: dedup takes `data`; the menus strip the embedded title from a
  passage; list rows project `when_to_use`, which SnippetListView reads.
- 0108 rewrites existing rows on an exact `' — ' || <own trigger>`
  suffix with raw SQL, leaving updated_at alone so the backfill does not
  re-embed the corpus for identical vectors. Downgrade recomposes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 16:48:28 -04:00
co-authored by Claude Opus 5.5
parent bb632c4196
commit 66e21a6c60
24 changed files with 358 additions and 189 deletions
+9 -10
View File
@@ -212,10 +212,10 @@ def fake_snippet(**attrs) -> MagicMock:
def fake_lesson(**attrs) -> MagicMock:
"""A stand-in lesson: a note whose `note_type` is what makes it one.
The title carries the trigger because `compose_title` builds it that way —
`{what}{when it applies}` — so a menu line rendering only the title is
already showing the reader when this lesson applies. Tests that used a bare
title here would be testing a record the product cannot create.
The title is the subject alone and the trigger lives in `data`, because
that is the record the product creates (milestone 427) — the trigger joins
the title only in the embedded document. A default whose title carried the
trigger would be testing a row only an un-migrated database holds.
The check fields and `arose_from_id` are explicitly None for the reason
`fake_snippet`'s `data` is: `update_note` reads `verify_with` and
@@ -223,12 +223,11 @@ def fake_lesson(**attrs) -> MagicMock:
auto-created MagicMock attribute is truthy — so a default lesson driven
through the update path would take a branch no real record takes.
"""
attrs.setdefault(
"title",
"Give absolutely-positioned siblings an explicit stacking order"
"placing two absolutely-positioned elements in the same area",
)
attrs.setdefault("data", {"when_to_apply": "two absolute siblings overlap"})
attrs.setdefault("title", "Give absolutely-positioned siblings an explicit stacking order")
attrs.setdefault("data", {
"what": "Give absolutely-positioned siblings an explicit stacking order",
"when_to_apply": "placing two absolutely-positioned elements in the same area",
})
attrs.setdefault("status", None)
attrs.setdefault("arose_from_id", None)
attrs.setdefault("verify_with", None)