open-brainy/src/db
David Snelling a963a744cc fix(generations): a sealed segment may only declare the generations it holds
Diagnosis of the "packed history is damaged" narration that fires on every
run of the affected stores. It is a WRITER defect, and the reader's refusal
was the symptom rather than the cause.

A sealed segment declares one contiguous range [firstGeneration,
lastGeneration], and every reader treats that range as containment:
coveringSegment is an interval test, hasGeneration returns true for anything
inside it, and open() seeds committedRanges from it.

repackHistory handed fold() a SPARSE batch. Three filters punch holes in its
candidate list mid-run — a generation absent from committedRanges never
appears, one still in the pending buffer is skipped, one whose tx.json will
not read is skipped — and fold() then computed the range from the first and
last survivor, claiming every generation in between. The next open merged
that mis-declared range back into committedRanges, re-admitting the hole as
committed history, so the following auto-compaction pass asked the packed
tier for a frame that was never written and failed. Re-merged at every open,
which is why it repeated on every run.

Confirmed against a forensic fixture: generation directories 1..2503 present
except exactly one, 1416; and its fact-log segment already showed the tell —
seg-...1410.bfl declaring 1410..1940 (531 generations) while recording 530
facts.

Three changes:

  - repackHistory folds each contiguous RUN as its own segment
    (`contiguousRuns`), so ranges describe exactly what the segments contain.
  - fold() REFUSES a non-contiguous batch, naming the gap and its width. The
    density law is now mechanical, so no future caller can reintroduce it. A
    refusal loses nothing: the generations stay live and readable.
  - Stores already carrying the damage heal instead of wedging. A segment
    whose declared span exceeds its frame count is SPARSE; `actualRanges()`
    reads the real generation list from its sidecar so open() never re-admits
    the holes, and readFrame reports such a hole as unpacked with a narration
    naming the segment, rather than throwing. A DENSE segment missing a frame
    is still loud damage — that one means the manifest and sidecar disagree.

Pins: nine unit cases (refusal and its message, honest ranges for separately
folded runs, a reconstructed pre-fix sparse segment serving its real frames
while reporting holes as unpacked, holes excluded from actualRanges, and the
dense-segment damage path still throwing) plus an end-to-end case that
deletes a generation directory and drives the real sequence — ordinary
close()-time repacking folds over the hole, then reopen and compact must both
complete. Verified red without the fix: the segment declared an
11-generation span while holding 10 frames.

(cherry picked from commit 9a888c37e9)
2026-08-31 10:47:08 -07:00
..
db.ts feat(namespace): NO SPECIAL NAMES + storage fidelity — the ruled completion of the field-addressing law 2026-08-03 16:59:32 -07:00
errors.ts chore: rename to @soulcraftlabs/brainy for Open Brainy on The Source 2026-08-27 17:07:09 -07:00
factLog.ts fix(recovery): the fold streams and narrates; the checkpoint chain arms at the flip 2026-08-18 12:53:50 -07:00
factLogFormat.ts fix(log): pad-frame construction is total; the at-ack sync-failure compensation splits by phase — a production adoption's two write-path defects, cured at their roots 2026-08-12 16:09:48 -07:00
familyStamp.ts fix(recovery): a torn generation-log tail is a terminal verdict, never a wait 2026-08-31 10:47:08 -07:00
faultInjectionStorage.ts feat(log): fact-log format v2 codec — record envelope, type registry, genesis, sector seals; fault-injection shim 2026-08-10 09:29:06 -07:00
fieldAddressing.ts feat(query): the sparse-store cut — where on a never-carried field serves operator truth, never a refusal 2026-08-12 15:57:19 -07:00
generationSegments.ts fix(generations): a sealed segment may only declare the generations it holds 2026-08-31 10:47:08 -07:00
generationStore.ts fix(generations): a sealed segment may only declare the generations it holds 2026-08-31 10:47:08 -07:00
logAuthority.ts fix(adoption): the baseline backfill runs to completion — one call adopts a pre-log baseline of any size 2026-08-17 12:53:04 -07:00
portableGraph.ts feat: includeHidden — export carries every visibility tier for migration-grade canon completeness 2026-07-27 11:22:25 -07:00
stableEqual.ts feat(8.0): temporal range verbs — diff, history, since(gen|Date), asOf{exclusive}, transactionLog window 2026-06-19 13:21:02 -07:00
types.ts perf(generations): discover generations by directory name, not by walking the log 2026-08-28 11:09:05 -07:00
whereMatcher.ts feat(namespace): egress guard + validation speak the law — whereMatcher's resolver reads system.* from the record and bare names from the metadata bag only (the bare-system switch is dead); validateFindParams refuses cursor/includeRelations/writeOnly typed (accepted-and-ignored dies as a class), validates order, and parses every orderBy address 2026-08-03 15:51:14 -07:00