Summary
SetReadyBit() reads PRAGMA user_version, OR-s a flag, then writes back via separate statements with no atomic wrapper. Two parallel cdidx runs (e.g. CI + a local rebuild) can each read 0, then write 0|FlagA and 0|FlagB; whichever runs second overwrites the other's flag.
Evidence
src/CodeIndex/Database/DbWriter.cs:2028-2035 — SetReadyBit reads then writes user_version in two separate statements.
Impact
fold_ready / graph_table_available / issues_table_available may report false even after both runs completed successfully.
- Status / readiness output silently misleads the user about what trust paths are available.
Proposed direction
- Wrap the read-modify-write in a single transaction (
BEGIN IMMEDIATE … COMMIT).
- Or: switch to a separate metadata table with a row-level UPSERT for each flag, since
user_version was never designed as a bitset.
Repro env
- Branch:
main @ 2ee912d (release v1.21.0)
Summary
SetReadyBit()readsPRAGMA user_version, OR-s a flag, then writes back via separate statements with no atomic wrapper. Two parallelcdidxruns (e.g. CI + a local rebuild) can each read 0, then write0|FlagAand0|FlagB; whichever runs second overwrites the other's flag.Evidence
src/CodeIndex/Database/DbWriter.cs:2028-2035—SetReadyBitreads then writesuser_versionin two separate statements.Impact
fold_ready/graph_table_available/issues_table_availablemay reportfalseeven after both runs completed successfully.Proposed direction
BEGIN IMMEDIATE … COMMIT).user_versionwas never designed as a bitset.Repro env
main@ 2ee912d (release v1.21.0)