Skip to content

fix(index): rebuild state.db atomically so a bad file can't wipe it#258

Open
abyssumining wants to merge 1 commit into
vouchdev:testfrom
abyssumining:fix/159-atomic-rebuild-index
Open

fix(index): rebuild state.db atomically so a bad file can't wipe it#258
abyssumining wants to merge 1 commit into
vouchdev:testfrom
abyssumining:fix/159-atomic-rebuild-index

Conversation

@abyssumining

@abyssumining abyssumining commented Jun 22, 2026

Copy link
Copy Markdown

rebuild_index reset state.db before reading any artifact, so one unreadable
file (bad YAML, a truncated write, a half-validated bundle import) aborted the
whole rebuild and left the search index empty for the healthy claims too, with
no way back until the next clean rebuild.

Build into a temp DB next to state.db and rename it into place only after
everything is written, so a failure leaves the existing index untouched.
Unreadable artifacts are skipped with a warning instead of taking the index
down. doctor() also grows an index_blown check so a KB damaged before this fix
is easy to spot.

Closes #159

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Index rebuild now uses atomic temporary files instead of direct database modification
    • vouch doctor now includes an index_blown check for damaged knowledge bases
    • Unreadable artifacts are skipped with warnings rather than failing the entire rebuild
  • New Features

    • Health status commands now report audit event counts and index presence
    • Enhanced index damage detection with recovery guidance

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: afb5f338-ab4b-4e77-abc5-93bd71aa3e85

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Fixes a critical bug where rebuild_index() wiped state.db before reading artifacts. A new open_db_at(path) context manager in index_db.py enables writing to a temp file. rebuild_index() now parses artifacts directly from disk, indexes into the temp DB, and atomically renames it into place, skipping invalid artifacts with warnings. doctor() gains an index_blown check comparing DB row counts against on-disk files.

Changes

Atomic Index Rebuild and Blown-Index Detection

Layer / File(s) Summary
open_db_at context manager
src/vouch/index_db.py
Adds open_db_at(path: Path) as a new context manager that creates parent dirs, connects to an explicit SQLite file, applies SCHEMA, commits on exit, and closes in finally — the primitive the atomic rebuild depends on.
Atomic rebuild_index() and _write_embeddings
src/vouch/health.py
Rewrites rebuild_index() to write into a .db.tmp temp file via open_db_at, parse claims/, pages/, and entities/ directly from disk, skip and collect unreadable artifacts, call _write_embeddings on the same connection, atomically swap with os.replace, and clean up the temp file on failure. Replaces _rebuild_embeddings(store) with _write_embeddings(conn, *, claims, pages, entities) that operates on already-parsed objects in batches of 64.
doctor() blown-index detection
src/vouch/health.py
Extends doctor() to compare index_db.stats() counts against on-disk artifact file counts when state.db exists, emitting an index_blown warning finding when they diverge.
Regression tests and changelog
tests/test_index.py, CHANGELOG.md
Adds three tests covering corrupt-YAML skip with warning, hard-failure atomicity with no .db.tmp leftover, and doctor() detecting index_blown. Adds corresponding CHANGELOG.md entries.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 Hoppity-hop, I fixed the index woes,
No more blank searches when a YAML blows!
I write to a temp file, then swap it in place,
A broken artifact won't black-hole the base.
vouch doctor now spots when the index is blown —
Atomic and safe, every KB I've known! 🗂️

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: implementing atomic rebuilding of state.db to prevent data loss when a bad artifact file causes failure.
Linked Issues check ✅ Passed All primary objectives from issue #159 are met: atomic index rebuilding implemented, unreadable artifacts are skipped with warnings, existing index is preserved on failure, and index_blown diagnostic check is added.
Out of Scope Changes check ✅ Passed All changes directly address issue #159: atomic rebuild via temp file, error handling for corrupted artifacts, index_blown diagnostic, and regression tests. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

@plind-junior plind-junior changed the base branch from main to test June 22, 2026 07:14
@plind-junior

Copy link
Copy Markdown
Collaborator

@abyssumining Thanks for the contribution. Would you fix the conflict on health.py?

@abyssumining abyssumining force-pushed the fix/159-atomic-rebuild-index branch from 734d141 to 805555f Compare June 22, 2026 07:42
@abyssumining

Copy link
Copy Markdown
Author

thanks @plind-junior, rebased onto test and resolved the health.py conflict so it now sits on top of the lint/fsck refactor, with the atomic temp-db + os.replace rebuild and the skip-and-warn kept intact. tests, mypy and ruff are green locally.

rebuild_index reset state.db before reading any artifact, so one unreadable
file (bad YAML, a truncated write, a half-validated bundle import) aborted the
whole rebuild and left the search index empty for the healthy claims too, with
no way back until the next clean rebuild.

Build into a temp DB next to state.db and rename it into place only after
everything is written, so a failure leaves the existing index untouched.
Unreadable artifacts are skipped with a warning instead of taking the index
down. doctor() also grows an index_blown check so a KB damaged before this fix
is easy to spot.

Closes vouchdev#159
@abyssumining abyssumining force-pushed the fix/159-atomic-rebuild-index branch from 805555f to 1fa4735 Compare June 22, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: rebuild_index() destroys state.db before reading artifacts — any parse error leaves the KB with a permanently blank search index

2 participants