docs: RELEASES.md entry for 8.2.6 (write/index-spine hardening)

Consumer-facing summary of the Pass-1 durability + integrity release: durable
blob-write honesty, single-op history durability (PendingFlushDurabilityError),
degraded-index surfacing, full-footprint clear(), count symmetry, and loud
index-maintenance / aggregation failures. loadBinaryBlob fault-propagation is
held for the native lockstep and deliberately omitted here.
This commit is contained in:
David Snelling 2026-07-13 10:45:39 -07:00
parent 36c10c1e20
commit a873852e61

View file

@ -10,6 +10,53 @@ Full auto-generated changelog: `CHANGELOG.md` · Releases: https://github.com/so
--- ---
## v8.2.6 — 2026-07-13 (write/index-spine hardening — loud errors, never quiet losses)
Durability + integrity hardening across the write and index paths. Every fix converts a place that
could *silently* lose data, serve a partial result, or acknowledge a write that did not land into a
loud, observable failure. No breaking API changes; one new exported error.
- **A durable blob write that stored nothing is no longer acknowledged.** The atomic blob write
(`saveBinaryBlob`, used for vector/graph index segments and the native index files) uses a unique
per-writer temp file, so a rename `ENOENT` can only mean *our* temp vanished before the rename —
the bytes never landed. It previously returned success on that path; it now retries once with a
fresh temp and, if the temp vanishes again, throws instead of acknowledging a write that persisted
nothing. A downstream deployment that mmaps these files no longer finds a "successfully written"
blob missing on the next open.
- **Single-op history durability is enforced, not assumed.** The asynchronous group-commit flush
that persists single-op generation history previously swallowed a persist failure as a warning
while writes kept succeeding and their history piled up, undurable, in memory. It now tolerates a
transient blip (retry with capped backoff) and, after repeated failures, **refuses further writes**
with the new **`PendingFlushDurabilityError`** rather than promise a durability it cannot deliver.
Live canonical data is untouched; the latch self-heals the moment a flush succeeds. Callers needing
hard per-write durability should keep using `transact()` (or `flush()` after a single-op).
- **A degraded derived index is surfaced on reads, not served silently.** When a non-fatal index
rebuild fails at open, or a write commits via adopt-forward recovery with an incomplete derived
index, `find()` and `get()` now emit one loud warning per degraded window (reads still return —
canonical is the source of truth), the state folds into `checkHealth()` /
`validateIndexConsistency()`, and `repairIndex()` reconciles and clears it.
- **`clear()` removes the full derived footprint.** It previously left raw index blobs, the native
id-mapper, and column-index manifests on disk, so a cleared brain could re-read stale native state
on reopen. It now wipes them together as a set.
- **Counts stay honest across deletes.** A delete decremented the per-type breakdown but not the
scalar total, so the total inflated permanently (and won pagination). Delete now decrements both;
the invariant `total === Σ per-type` holds across any interleaving of add / visibility-flip /
delete and across a reopen.
- **Index-maintenance and aggregation failures are loud.** A partial LSM/segment load no longer
publishes the manifest's full count as if healthy; an HNSW flush that can't persist a node throws
(`HnswFlushError`) instead of returning a lying count and dropping the node; a corrupt or missing
manifest-listed column segment throws (`ColumnSegmentLoadError`) instead of silently dropping its
entities from every query; and aggregation materialization / state-load failures now warn instead
of vanishing into an empty catch.
New export: **`PendingFlushDurabilityError`** (with `.cause` and `.failedAttempts`). No other public
API change. Each fix ships with a dedicated regression test.
## v8.2.5 — 2026-07-12 (honest response when a transaction rollback can't complete) ## v8.2.5 — 2026-07-12 (honest response when a transaction rollback can't complete)
Data-integrity fix. When a transaction failed and its rollback then *also* failed to undo a Data-integrity fix. When a transaction failed and its rollback then *also* failed to undo a