The fact_embeddings cache contract
fact_embeddings is a SQLite table that caches canonical embedding vectors and their model metadata alongside each fact. It is a best-effort cache, not the authoritative record of which facts have a vector in LanceDB — that authority is LanceDB itself.
Why this distinction matters
Before this contract was made explicit, storage diagnostics compared fact_embeddings row counts directly against LanceDB row counts and treated any gap as “storage drift” or corruption. In practice, fact_embeddings coverage legitimately lags LanceDB for several reasons:
- Structured (keyed) facts are never expected to have a vector. Task-ledger entries and other
key/valuefacts are addressable by key, not meant to be semantically searchable, and are excluded from both the vectorless-backlog repair path and the “expected to have a vector” population used by diagnostics (backends/facts-db/stats.ts’sactiveUnstructuredFactWhereClause). - Shadow-table re-index doesn’t write the cache during migration. Writing
fact_embeddingsbefore a shadow table swap commits would let SQLite claim an embedding state the active Lance table doesn’t actually have yet — the atomicity of the shadow-table approach requires deferring cache writes until after the swap succeeds. A re-index now runs a best-effort backfill pass (services/vector-maintenance.ts’sbackfillEmbeddingCacheFromVectorStore) immediately after a successful swap, but that pass can partially fail or be skipped without failing the re-index.
The contract
fact_embeddingscoverage may legitimately be lower than LanceDB’s canonical vector count. This is normal, not a health failure.- Storage-sync diagnostics (
services/storage-sync-diagnostics.ts) treat ID-set alignment between SQLite’s expected-vector population (active, unstructured facts) and LanceDB’s unique IDs as the actionable “does this fact have a vector” signal — notfact_embeddingsrow counts. canonicalEmbeddings(thefact_embeddingsrow count) is reported for operator visibility only, labeled as cache coverage. It never gatesverify/doctorpass/fail status.- Repair/reembed/re-index paths that create or rebuild a canonical vector should still write
fact_embeddingswhen they can (viaservices/vector-maintenance.ts’sstoreCanonicalVectorForFact) so the cache converges toward LanceDB over time — it just isn’t required to be perfectly in sync at every moment.