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
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:
@@ -56,7 +56,7 @@ async def test_the_backfill_embeds_the_text_as_it_is_now_not_as_it_was_scanned()
|
||||
with (
|
||||
patch.object(emb, "async_session", return_value=_ctx(scan)),
|
||||
patch.object(emb, "_current_row",
|
||||
AsyncMock(return_value=(42, "T", *edited))),
|
||||
AsyncMock(return_value=(42, "T", *edited, "note", None))),
|
||||
patch.object(emb, "upsert_note_embedding", AsyncMock()) as upsert,
|
||||
patch.object(emb.asyncio, "sleep", AsyncMock()),
|
||||
):
|
||||
@@ -75,7 +75,7 @@ async def test_a_record_deleted_between_the_scan_and_the_loop_is_skipped():
|
||||
with (
|
||||
patch.object(emb, "async_session", return_value=_ctx(scan)),
|
||||
patch.object(emb, "_current_row",
|
||||
AsyncMock(side_effect=[None, (42, "T", "body")])),
|
||||
AsyncMock(side_effect=[None, (42, "T", "body", "note", None)])),
|
||||
patch.object(emb, "upsert_note_embedding", AsyncMock()) as upsert,
|
||||
patch.object(emb.asyncio, "sleep", AsyncMock()),
|
||||
):
|
||||
@@ -99,7 +99,7 @@ async def test_a_record_whose_text_outran_its_vectors_is_re_embedded():
|
||||
|
||||
with (
|
||||
patch.object(emb, "async_session", return_value=_ctx(scan)),
|
||||
patch.object(emb, "_current_row", AsyncMock(return_value=(42, "T", "b"))),
|
||||
patch.object(emb, "_current_row", AsyncMock(return_value=(42, "T", "b", "note", None))),
|
||||
patch.object(emb, "upsert_note_embedding", AsyncMock()) as upsert,
|
||||
patch.object(emb.asyncio, "sleep", AsyncMock()),
|
||||
):
|
||||
@@ -119,7 +119,7 @@ async def test_a_task_logged_since_its_vectors_is_re_embedded():
|
||||
|
||||
with (
|
||||
patch.object(emb, "async_session", return_value=_ctx(scan)),
|
||||
patch.object(emb, "_current_row", AsyncMock(return_value=(42, "T", "b"))),
|
||||
patch.object(emb, "_current_row", AsyncMock(return_value=(42, "T", "b", "note", None))),
|
||||
patch.object(emb, "upsert_note_embedding", AsyncMock()) as upsert,
|
||||
patch.object(emb.asyncio, "sleep", AsyncMock()),
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user