brainy/src/storage
David Snelling a2f4f6a550 fix: non-destructive, crash-resumable restore
restore() removed the entire live brain directory and then fs.cp'd the
snapshot in, so any copy failure left the store destroyed with only a partial
copy. The sharpest edge: fs.cp materialized the holes of sparse mmap blob
files, so a snapshot that fits on disk could balloon and ENOSPC mid-copy — the
recovery tool destroying the brain it was asked to recover.

Rewrite restoreFromDirectory as stage → verify → atomic swap:
- Copy the snapshot into a _restore_staging area BEFORE touching live data,
  sparse-aware (copyTreeSparse/copyFileSparse skip all-zero 4 MiB chunks, so a
  mostly-hole store restores at its true allocated size, not its apparent size).
- On any copy failure (ENOSPC included) remove only the half-written staging
  area and throw — the live store is left exactly as it was.
- Only after the copy succeeds, fsync a completion marker naming the staged
  entries, then swapStagedRestoreIn(): an idempotent per-entry
  rm-old + rename-staged-in (same-filesystem renames that cannot ENOSPC),
  removing stale live entries the snapshot lacks.
- completeInterruptedRestore(), wired into init() right after the root dir is
  ensured, finishes a crash mid-swap FORWARD (resume a committed swap) or
  discards an uncommitted staging area (live still authoritative).

_restore_staging is excluded from snapshots. persist() (hard-link snapshot)
was already safe and is unchanged; no public API change.

Regression (tests/integration/restore-nondestructive.test.ts): a forced copy
failure leaves live data fully intact and cleans staging; a normal restore
round-trips to the snapshot; an interrupted-but-committed restore completes on
reopen; an uncommitted staging area is discarded on open; the sparse copy is
byte-identical with allocated blocks far below apparent size.
2026-07-12 09:10:35 -07:00
..
adapters fix: non-destructive, crash-resumable restore 2026-07-12 09:10:35 -07:00
baseStorage.ts feat: temporal VFS — file content joins the Model-B immutability model 2026-07-10 16:43:48 -07:00
binaryDataCodec.ts feat(8.0)!: delete fork/branch/commit/history/versions — superseded by the Db API 2026-06-10 15:22:47 -07:00
blobStorage.ts feat: temporal VFS — file content joins the Model-B immutability model 2026-07-10 16:43:48 -07:00
brainFormat.ts feat(8.0): version-handshake marker (formatInfo + indexEpoch) for whole-brain auto-upgrade 2026-06-30 09:53:10 -07:00
enhancedClearOperations.ts chore(8.0)!: drop browser support, cloud SDKs, legacy pipeline, dead threading 2026-06-09 16:38:30 -07:00
operationalModes.ts refactor(8.0)!: remove distributed clustering subsystem — inert/orphaned, scale is single-process + native provider 2026-06-15 10:37:39 -07:00
sharding.ts fix(8.0): accept application-supplied entity ids, not just UUIDs 2026-06-16 10:40:40 -07:00
storageFactory.ts feat(8.0): API simplification — remove neural()/Db.search, one storage path key, integration→0 2026-06-20 13:31:11 -07:00