feat(embeddings): record the embedding contract and refuse a mismatched config (#189)

Stored vectors are only searchable with the backend, model, width and
prefixes that built them, and nothing enforced that: changing [embedding]
(or backend) without reembed_embeddings silently broke similarity search,
and the per-row dim check missed same-width model or prefix swaps.

- embedding_contract: one row (backend, model, dim, both prefixes). The
  server checks it once after migrations; the src/bin tools check it on
  their first embed. No row yet = record the running config.
- On a mismatch every embed (embed_many, so queries and documents) fails
  with a message naming each changed field and the fix. Mixing vector
  spaces corrupts the corpus for good; refusing only pauses semantic
  search until the config is put back or the corpus re-embedded.
- reembed_embeddings bypasses the guard and records the contract after a
  full run (all four tables, no --limit, not --dry-run).
- A DB error in the check leaves embedding allowed and is logged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cameron CordesandClaude Opus 5.5 committed 2026-11-03 12:06:54 -05:00
1 parent 3ccce38505
commit 070f20b295
8 files changed
+374 -1

No files matched your search

+13
View File
@@ -41,6 +41,18 @@ diesel::table! {
}
}
diesel::table! {
embedding_contract (id) {
id -> Integer,
backend -> Text,
model -> Text,
dim -> Integer,
query_prefix -> Text,
document_prefix -> Text,
recorded_at -> BigInt,
}
}
diesel::table! {
enhance_jobs (id) {
id -> Text,
@@ -429,6 +441,7 @@ diesel::allow_tables_to_appear_in_same_query!(
calendar_events,
clip_failures,
daily_conversation_summaries,
embedding_contract,
enhance_jobs,
entities,
entity_facts,