You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
manage_adr data loss: ADRs in project_summaries are deleted during graph re-indexing #516
There is a data-loss bug in how the database dump pipeline handles the project_summaries table. When an Architecture Decision Record (ADR) is stored using the manage_adr tool, it correctly saves to the project_summaries table. However, any subsequent indexing operation (index_repository / pipeline execution) completely overwrites the SQLite database file and fails to preserve the project_summaries table, permanently deleting the ADR.
Root Cause Analysis
The issue stems from the custom SQLite writer implementation used to dump the graph buffer to disk.
In src/pipeline/pipeline.c (and internal/cbm/sqlite_writer.c), when the pipeline finishes, it opens the target .db file using fopen(path, "wb"), truncating the existing database.
The monolithic cbm_writer_finalize (and write_metadata_tables) explicitly writes an empty B-tree for the summaries_root:
While the incremental pipeline attempts to preserve file_hashes by reopening the new DB and re-inserting them, no such preservation logic exists for project_summaries.
Expected Behavior
The database dump pipeline should migrate or preserve the contents of the project_summaries table across re-indexing operations, ensuring that ADRs persist indefinitely across sessions as designed.
Actual Behavior
The project_summaries table is wiped clean on every full dump, resulting in silent data loss.
Reproduction
Steps to Reproduce
Index a repository to create the initial database.
Use the MCP tool to store an ADR: manage_adr(project="<project_name>", mode="update", content="# My ADR...")
Verify the ADR was stored: manage_adr(project="<project_name>", mode="get") (Returns the expected content).
Trigger a graph re-index (e.g., via index_repository).
Attempt to retrieve the ADR again: manage_adr(project="<project_name>", mode="get") (Returns no_adr).
Logs
Project scale (if relevant)
No response
Confirmations
I searched existing issues and this is not a duplicate.
My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.
Reproduced this on a build from source, and confirmed your root-cause analysis is correct with one refinement on the trigger that matters for the fix.
The loss only happens on the full dump path, not every re-index:
Re-index with no file changes → routes through cbm_pipeline_run_incremental, the DB isn't rewritten, ADR survives.
Re-index after a file changes or a new file is added → dump_and_persist_hashes deletes the old .db and rebuilds it from the graph buffer, ADR is wiped → manage_adr(mode="get") returns no_adr.
I confirmed it directly: store an ADR, change a source file, re-index, and the row in project_summaries is gone.
The asymmetry you spotted is exactly the cause. In dump_and_persist_hashes (pipeline.c), after cbm_gbuf_dump_to_sqlite writes a fresh DB (with an empty project_summaries, per the summaries_root btree in sqlite_writer.c), the code reopens the DB and explicitly re-persists file_hashes delete then re-upsert but there's no equivalent step for project_summaries. So hashes carry across a full re-index and ADRs don't.
The fix mirrors the file_hashes handling: read the existing project_summaries rows before the old DB is unlinked, then re-insert them after reopening the rebuilt DB. (Reading after the dump won't work the file's already been truncated/replaced by then, so the rows have to be captured up front and carried through.)
Happy to put up a PR along those lines if it's wanted.
One refinement from tracing the pipeline: the loss happens at two dump sites, not one. The full path (dump_and_persist_hashes in pipeline.c) was correctly identified here, but the incremental path loses the ADR too — when a file changed/was added, cbm_pipeline_run_incremental reaches dump_and_persist (pipeline_incremental.c), which also rewrites the DB with an empty project_summaries. Only the no-change incremental case early-returns without a dump, so the ADR survives there. Fixing just the full path would leave the bug live on any single-file edit.
The fix mirrors the existing file_hashes preservation: capture the ADR from the old DB before it is rewritten (in try_incremental_or_delete_db, the single point that opens the old DB for both paths) and re-insert it via cbm_store_adr_store after each dump. Regression tests cover both the full and incremental paths.
A fix for the reindex-swap race is now on main in 7be2bec (PR #2315 by @0xb007ab1e): a manage_adr write that lands while a reindex swaps in the new database is carried across the swap instead of being lost. Thank you for the report.
Activity
RithvikReddy0-0 commentedon Jun 20, 2026
Reproduced this on a build from source, and confirmed your root-cause analysis is correct with one refinement on the trigger that matters for the fix.
The loss only happens on the full dump path, not every re-index:
cbm_pipeline_run_incremental, the DB isn't rewritten, ADR survives.dump_and_persist_hashesdeletes the old.dband rebuilds it from the graph buffer, ADR is wiped →manage_adr(mode="get")returnsno_adr.I confirmed it directly: store an ADR, change a source file, re-index, and the row in
project_summariesis gone.The asymmetry you spotted is exactly the cause. In
dump_and_persist_hashes(pipeline.c), aftercbm_gbuf_dump_to_sqlitewrites a fresh DB (with an emptyproject_summaries, per thesummaries_rootbtree insqlite_writer.c), the code reopens the DB and explicitly re-persistsfile_hashesdelete then re-upsert but there's no equivalent step forproject_summaries. So hashes carry across a full re-index and ADRs don't.The fix mirrors the file_hashes handling: read the existing
project_summariesrows before the old DB is unlinked, then re-insert them after reopening the rebuilt DB. (Reading after the dump won't work the file's already been truncated/replaced by then, so the rows have to be captured up front and carried through.)Happy to put up a PR along those lines if it's wanted.
DeusData commentedon Jun 21, 2026
Hey, thx, will review ASAP
fix(pipeline): harden ADR preservation and add regression test (DeusD…
fix(pipeline): preserve ADR across full re-index (DeusData#516)
fix(pipeline): harden ADR preservation and add regression test (DeusD…
yasku commentedon Jun 25, 2026
Opened #623 with a fix.
One refinement from tracing the pipeline: the loss happens at two dump sites, not one. The full path (
dump_and_persist_hashesinpipeline.c) was correctly identified here, but the incremental path loses the ADR too — when a file changed/was added,cbm_pipeline_run_incrementalreachesdump_and_persist(pipeline_incremental.c), which also rewrites the DB with an emptyproject_summaries. Only the no-change incremental case early-returns without a dump, so the ADR survives there. Fixing just the full path would leave the bug live on any single-file edit.The fix mirrors the existing
file_hashespreservation: capture the ADR from the old DB before it is rewritten (intry_incremental_or_delete_db, the single point that opens the old DB for both paths) and re-insert it viacbm_store_adr_storeafter each dump. Regression tests cover both the full and incremental paths.Merge pull request #539 from RithvikReddy0-0/fix/516-adr-persistence
feat(store): external per-node annotations, keyed by qualified_name
fix(adr): store ADRs in a per-project sidecar DB, untouched by reindex
fix(adr): store ADRs in a per-project sidecar DB, untouched by reindex
fix(adr): store ADRs in a per-project sidecar DB, untouched by reindex
fix(adr): store ADRs in a per-project sidecar DB, untouched by reindex
fix(adr): store ADRs in a per-project sidecar DB, untouched by reindex
fix(adr): store ADRs in a per-project sidecar DB, untouched by reindex
fix(adr): store ADRs in a per-project sidecar DB, untouched by reindex
DeusData commentedon Oct 31, 2026
A fix for the reindex-swap race is now on main in 7be2bec (PR #2315 by @0xb007ab1e): a manage_adr write that lands while a reindex swaps in the new database is carried across the swap instead of being lost. Thank you for the report.