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>