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:
@@ -248,6 +248,7 @@ async def find_duplicate_note(
|
||||
note_type: str = "note",
|
||||
code: str = "",
|
||||
locations: list[dict] | None = None,
|
||||
data: dict | None = None,
|
||||
) -> DuplicateMatch | None:
|
||||
"""Best near-duplicate of (title, body) within the same owner + project +
|
||||
kind, or None. Title match first (cheap, exact), then — for snippets — the
|
||||
@@ -258,6 +259,11 @@ async def find_duplicate_note(
|
||||
`code` and `locations` are the snippet's structured fields. They are ignored
|
||||
for every other kind, and passing them is what lets the gate compare
|
||||
ARTEFACTS rather than descriptions of artefacts (#2518).
|
||||
|
||||
`data` is the candidate's structured mirror. For a snippet or lesson it
|
||||
carries the trigger, which the TITLE no longer does (milestone 427): the
|
||||
title check compares names, and the semantic check rebuilds the embedded
|
||||
document from `data`.
|
||||
"""
|
||||
norm = " ".join((title or "").split()).lower()
|
||||
|
||||
@@ -309,7 +315,11 @@ async def find_duplicate_note(
|
||||
# section. Capped so one pathological paste can't turn a save into
|
||||
# dozens of searches — a duplicate past the cap is the duplicate
|
||||
# report's job, not the gate's.
|
||||
for query in embeddings_svc.chunk_document(title, body)[:_GATE_MAX_CHUNKS]:
|
||||
# The EMBEDDED title (milestone 427): a snippet or lesson is stored
|
||||
# under its name and embedded under `name — trigger`, so the query
|
||||
# document is built the way the corpus was, from `data`.
|
||||
doc_title = embeddings_svc.document_title(title, note_type, data, body)
|
||||
for query in embeddings_svc.chunk_document(doc_title, body)[:_GATE_MAX_CHUNKS]:
|
||||
# Scope the semantic check the same way as the title check: a record
|
||||
# in project P compares only to P; a project-less (orphan) record
|
||||
# compares only to other orphans (orphan_only), NOT across every
|
||||
|
||||
Reference in New Issue
Block a user