Skip to content

fix(index): clear the legacy embeddings table on reset and deindex#543

Open
michiot05 wants to merge 1 commit into
vouchdev:testfrom
michiot05:fix/index-reset-legacy-embeddings
Open

fix(index): clear the legacy embeddings table on reset and deindex#543
michiot05 wants to merge 1 commit into
vouchdev:testfrom
michiot05:fix/index-reset-legacy-embeddings

Conversation

@michiot05

Copy link
Copy Markdown

what changed

index_db.reset() now deletes from the legacy embeddings vector table along with every other derived table, and index_db.deindex() removes the deleted artifact's row from it — mirroring how both functions already handle embedding_index.

why

reset()'s own docstring states the contract: it "clears every derived table — FTS indexes, embedding vectors, …" because "leaving stale rows here means semantic search can return orphaned hits after a reindex." the delete list covered embedding_index but skipped the legacy embeddings table — which is the table search_embeddings actually scans at query time and _rebuild_embeddings repopulates via INSERT OR REPLACE on every vouch index.

two user-visible consequences:

  • orphaned semantic hits, permanently. delete an artifact from disk, run vouch index — the "drop and rebuild… idempotent" pass — and the deleted artifact's vector survives, so semantic search keeps returning it (snippet degraded to the bare id). the same applies to a single delete: deindex()'s docstring promises the embedding row is removed "for any kind", but it only cleared embedding_index.
  • an fsck warning that can never be cleared. _check_orphan_embeddings deliberately scans both tables ("either one drifting silently breaks semantic retrieval") and flags the stale row as orphan_embedding — but the natural remedy it points the operator at, reindexing, never touched the table, so the warning was permanent.

what would break for an existing .vouch/

nothing — state.db is a derived cache and both changed functions are already destructive-by-contract on derived rows only. kbs carrying stale legacy vectors get them swept away on their next vouch index or gated delete, which is the documented behavior.

test plan

three regression tests, all failing on the previous code:

  • tests/test_index.py::test_reset_clears_legacy_embeddings_tablereset() leaves embeddings empty
  • tests/test_index.py::test_deindex_removes_legacy_embedding_rowdeindex() removes the artifact's legacy row
  • tests/test_health.py::test_fsck_orphan_embedding_cleared_by_reindex — the operator-visible symptom: an orphan_embedding finding is actually cleared by health.rebuild_index, instead of surviving it forever

validation run locally:

python -m pytest tests/ -q --ignore=tests/embeddings   # all passed
python -m mypy src                                     # no issues in 102 files
python -m ruff check src tests                         # all checks passed

index_db.reset() promises to drop every derived table — "leaving stale
rows here means semantic search can return orphaned hits after a
reindex" — but the delete list skipped the legacy embeddings table, the
one search_embeddings actually scans and _rebuild_embeddings repopulates
on every vouch index. deindex() had the same gap: it cleared
embedding_index but left the legacy row behind.

the consequence is exactly the orphaned-hits case the docstring warns
about, made permanent: delete an artifact, reindex, and its vector still
comes back as a semantic hit, while fsck's orphan_embedding warning can
never be cleared because the natural remedy — reindexing — never touched
the table.

add the legacy table to reset()'s delete list and give deindex() the
matching per-row delete, mirroring how both already handle
embedding_index.
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@michiot05, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b9c6c3d8-b215-4d17-9966-79299867fec7

📥 Commits

Reviewing files that changed from the base of the PR and between 1c9ca7c and 5b3a6d8.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • src/vouch/index_db.py
  • tests/test_health.py
  • tests/test_index.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added docs documentation, specs, examples, and repo guidance retrieval context, search, synthesis, and evaluation tests tests and fixtures size: S 50-199 changed non-doc lines labels Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs documentation, specs, examples, and repo guidance retrieval context, search, synthesis, and evaluation size: S 50-199 changed non-doc lines tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant