Skip to content

recall repair --execute leaves the vec index empty — semantic search silently degrades to bruteforce, and doctor reports PASS #241

Description

@edheltzel

Problem

recall repair --execute embeds missing rows but does not rebuild the sqlite-vec index. It leaves the DB in a state where embeddings is fully populated and the vector index is empty — so semantic search silently runs bruteforce instead of knn, and nothing tells you.

Its own help text says: "Rebuild FTS5 indexes and re-embed missing embeddings". It rebuilds FTS. It does not rebuild vec. A user reasonably reads "repair" as "make the DB healthy."

Reproduction (observed on a live install, 2026-07-15)

$ recall repair --execute
  Embedded 14616 row(s), skipped 7 too-short row(s), 16 failure(s).

$ # embeddings populated, vec index empty:
embeddings:            14616
vec_embeddings_rowids: 0
vec_embeddings_chunks: 0

$ recall doctor
  [PASS] Ollama reachable (semantic search)     <-- reports healthy

$ # MCP search:
Found 10 results (hybrid: FTS5 + embeddings; semantic backend: bruteforce)
                                                                 ^^^^^^^^^^

Then, separately:

$ recall embed reindex
Vector index rebuilt: 14616 entries.     (took ~1 second)

$ # now:
Found 10 results (hybrid: FTS5 + embeddings; semantic backend: knn)

The reindex takes one second. There is no performance reason to omit it from repair.

Why this matters

This is the second failure mode in the same incident (see #240). The recovery path for a dead semantic tier is backfilland you must independently know to run reindex. Nothing in repair's output, help text, or doctor indicates the vec index is empty. A user who runs the command literally named repair and then checks doctor gets an all-clear on a half-repaired DB — degraded to brute-force, silently.

Compounding: recall doctor has no vec checks at all (#226 item 3), so the gap between "repair finished" and "search is actually healthy" is invisible from every surface.

Proposed fix

  • recall repair --execute runs the vec reindex after a successful embedding pass (it costs ~1s on 14.6k rows)
  • Add --no-reindex to opt out, mirroring the existing --no-embed
  • Report the vec index state in repair's dry-run output alongside FTS (vec index: N entries, M missing) so the plan is visible before --execute
  • recall doctor reports vec index row count vs embeddings count and FAILs on mismatch (overlaps Harden vec index: zero-vector guard, index-format version stamp, doctor/stats vec observability #226 item 3)
  • Test: after repair --execute on a DB with embeddings and an empty vec index, vec_embeddings_rowids count == embeddings count

Acceptance

  1. recall repair --execute on a DB with 0 vec rows leaves the vec index fully populated.
  2. recall repair (dry-run) reports the vec index state.
  3. Semantic backend reports knn, not bruteforce, after a plain repair --execute.
  4. --no-reindex skips it.

Found during the 2026-07-15 confidence investigation. Refs #240, #226, #148.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageMaintainer needs to evaluate this issuerisk:mediumtype:bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions