Skip to content

fix(purge): #886 name every store purge leaves deleted, with its own restore cost - #894

Merged
jasonssdev merged 1 commit into
mainfrom
fix/purge-names-every-dropped-store-886
Aug 27, 2026
Merged

fix(purge): #886 name every store purge leaves deleted, with its own restore cost#894
jasonssdev merged 1 commit into
mainfrom
fix/purge-names-every-dropped-store-886

Conversation

@jasonssdev

Copy link
Copy Markdown
Owner

The defect

openkos purge deletes five derived stores and rebuilds two, so three are left deleted — and the warning named one. findings.db and insight_questions.db were destroyed in silence. In the reported session that cost 11 persisted contradiction verdicts, 9 edge suggestions and 7 identity adjudications, minutes after contradictions reported 11 of 11 candidate(s) served from persisted findings; 0 judged fresh.

#142's own justification for the vectors warning — "warn every time so an operator is never left assuming dense retrieval is still intact" — was never applied to the other two.

Why one line would not do

The three restore costs are genuinely different, so a shared "run openkos reindex" would misprice two of them:

store cost
vectors.db a full re-embed via openkos reindex (#698 wording preserved)
findings.db an LLM call per verdict on the next contradictions / adjudicate / suggest-relations. reindex restores none of them.
insight_questions.db free, and nothing to run — a miss re-embeds on the next save

What changed

  • One structure pairs each dropped store with its own cost. The sidecar sweep, the count and the notice all walk it, so a fourth store cannot reach the delete set while missing from the disclosure — that drift is the defect.
  • A store is reported only when this purge actually destroyed it: it existed before and is verifiably gone after. Absence alone is not loss (a workspace that never ran curate has no findings.db to lose), and a failed unlink must not be announced as a drop.
  • The existence probe cannot raise. Path.exists() re-raises EACCES, and these probes run after the irreversible rewrite, so an unguarded one would crash away the entire success report. It fails closed: a store whose absence cannot be verified is not claimed as destroyed.
  • Orphan -wal/-shm sidecars are swept — but only for stores that actually went. Sweeping the sidecars of a live database is worse than litter: a -wal holds committed pages not yet checkpointed back.
  • The disclosure is emitted from a finally, so post-erasure bookkeeping failing cannot swallow the only record of what was destroyed.

Correction to the issue

The issue states purge destroyed "the operator's own recorded rulings (two declined identity merges)". It does not, and a test now pins that. All three findings.db tenants hold MACHINE-computed verdicts, while a --decline or --keep-distinct ruling is written under the bundle's decision subtree and committed with the bundle.

The notice says so — qualified. A ruling naming a purged concept is expunged in the same rewrite pass, which is the erasure working, not a gap. An unqualified promise would read as the erasure having missed something.

Review

Receipt review-34230384f7c253bf, four lenses, APPROVED with zero corrections consumed, over six candidates all fixed pre-capture. The rounds found, among others:

  • a sidecar sweep that ran even when the store's own unlink had failed;
  • a notice that announced stores it had not verified were gone;
  • a hard-coded count beside a derived list — the same drift, one line higher;
  • three of my own tests whose assertions did not match their names, including one that checked "keeps the sidecars" without looking at a sidecar.

Declared, not fixed

A TOCTOU between the delete and the sweep's re-check was raised as CRITICAL and rejected: this is a single-process CLI operating on a local workspace during an already-irreversible operation, with no concurrent writer to race. The fail-closed probe means an unverifiable state is treated as "still present" either way.

Verification

5761 passed, 1 skipped · coverage 96.80% (gate 90) · ruff check, ruff format --check, mypy . clean over 275 files · 36/36 harness self-tests.

Closes #886

…restore cost

`purge` deletes five derived stores and rebuilds two, so three are left
deleted -- and the warning named one. `findings.db` and
`insight_questions.db` were destroyed in silence. In the reported session
that cost 11 persisted contradiction verdicts, 9 edge suggestions and 7
identity adjudications, minutes after `contradictions` reported "11 of 11
candidate(s) served from persisted findings; 0 judged fresh".

The three restore costs are genuinely different, so one shared "run
`openkos reindex`" line would misprice two of them: a reindex restores no
findings verdict at all, and the question-embedding cache needs nothing run.

- One structure pairs each dropped store with its own cost. The sidecar
  sweep, the count and the notice all walk it, so a fourth store cannot
  reach the delete set while missing from the disclosure.
- A store is reported only when this purge actually DESTROYED it: it
  existed before and is verifiably gone after. Absence alone is not loss --
  a workspace that never ran `curate` has no `findings.db` to lose -- and a
  failed `unlink` must not be announced as a drop.
- The existence probe cannot raise. `Path.exists()` re-raises EACCES, and
  these probes run after the irreversible rewrite, so an unguarded one
  would crash away the entire success report. It fails CLOSED: a store
  whose absence cannot be verified is not claimed as destroyed.
- Orphan `-wal`/`-shm` sidecars are swept, but only for stores that
  actually went. Sweeping the sidecars of a live database is worse than
  litter: a `-wal` holds committed pages not yet checkpointed back.
- The disclosure is emitted from a `finally`, so post-erasure bookkeeping
  failing cannot swallow the only record of what was destroyed.

CORRECTION to the issue: it states purge destroyed "the operator's own
recorded rulings (two declined identity merges)". It does not. All three
`findings.db` tenants hold MACHINE-computed verdicts, while a `--decline`
or `--keep-distinct` ruling is written under the bundle's decision subtree
and committed with the bundle. The notice says so, qualified -- a ruling
naming a purged concept IS expunged in the same rewrite pass, which is the
erasure working, not a gap.

Review receipt review-34230384f7c253bf, four lenses, APPROVED with zero
corrections consumed, over six candidates all fixed pre-capture.

Closes #886
@jasonssdev
jasonssdev merged commit a8304ff into main Aug 27, 2026
7 checks passed
@jasonssdev
jasonssdev deleted the fix/purge-names-every-dropped-store-886 branch August 27, 2026 06:19
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.

purge leaves three stores deleted but warns about only one, silently discarding paid-for verdicts and operator rulings

1 participant