Found by writing #461's read-only test the strict way, before assuming the documented wording was true.
What the run actually does
A plain runDoctor({ cwd }) — no --fix — modifies:
.git/commitlore/index.db-shm
and under --fix creates both:
.git/commitlore/index.db-shm
.git/commitlore/index.db-wal
index.db itself is byte-identical in both cases.
Why it is not a defect in the code
Those are SQLite's own bookkeeping for a WAL database. Opening a WAL database creates them — for readers as much as writers — because a reader needs the shared-memory index. They carry no committed data.
Making the run literally write-free would mean opening the index outside WAL for doctor, which trades a documentation problem for the concurrency one #420 was about (concurrent hooks falling back to a full scan for want of a busy timeout). Not worth it.
Why it is a defect in the document
PRD §8.2 says zero writes, without the exception. That is the same class this whole milestone exists for: a statement that reads as stronger than what the code does. A reader who checks the claim finds it false, and the correct response is to fix the claim rather than to quietly scope the test around it.
What to change
§8.2 should state the invariant that carries the meaning:
A plain run modifies no repository content and no CommitLore state. index.db is byte-identical after a run. SQLite's -shm/-wal sidecars are created or touched by opening a WAL database at all and are excluded, because they hold no committed data.
test/doctor-invariants.test.ts already asserts exactly that pair — the filtered inventory diff plus an explicit index.db equality — and records the discrepancy in a header comment pointing here.
Out of scope
Changing how the index is opened. Ticket #465's evidence work and #472's budget both touch these paths; neither should absorb this.
Found by writing #461's read-only test the strict way, before assuming the documented wording was true.
What the run actually does
A plain
runDoctor({ cwd })— no--fix— modifies:and under
--fixcreates both:index.dbitself is byte-identical in both cases.Why it is not a defect in the code
Those are SQLite's own bookkeeping for a WAL database. Opening a WAL database creates them — for readers as much as writers — because a reader needs the shared-memory index. They carry no committed data.
Making the run literally write-free would mean opening the index outside WAL for doctor, which trades a documentation problem for the concurrency one #420 was about (concurrent hooks falling back to a full scan for want of a busy timeout). Not worth it.
Why it is a defect in the document
PRD §8.2 says zero writes, without the exception. That is the same class this whole milestone exists for: a statement that reads as stronger than what the code does. A reader who checks the claim finds it false, and the correct response is to fix the claim rather than to quietly scope the test around it.
What to change
§8.2 should state the invariant that carries the meaning:
test/doctor-invariants.test.tsalready asserts exactly that pair — the filtered inventory diff plus an explicitindex.dbequality — and records the discrepancy in a header comment pointing here.Out of scope
Changing how the index is opened. Ticket #465's evidence work and #472's budget both touch these paths; neither should absorb this.