feat: generation fact log — after-image commit records, dual-written at every commit point
Every committed generation now also appends a FACT — an after-image commit record (what each touched entity/relationship became, or a body-less tombstone for a removal) — to an append-only, crc32c-framed segment log under _generations/facts/. The before-image history and the canonical tree remain authoritative; the fact log gives consumers ONE sequential, self-verifying stream (index heals, incremental replays) in place of a per-entity directory walk. - Wire format: positional msgpack facts [generation, timestamp, ops, meta, blobHashes]; op = [kind u8, id bin16, record | nil tombstone]; 32-byte segment header (magic, formatVersion, firstGeneration, zeroed+verified reserved); length+crc32c frame per fact; zero-padded segment names so lexicographic order == generation order; JSON manifest with an atomic rename flip, manifest-first rotation. - Commit protocol: facts append+fsync BEFORE the commit point inside the existing durability window, so a crash can only leave the log AHEAD of committed truth — open() truncates back (torn tails detected by CRC). Absent generation = never committed; a scan can never see an uncommitted fact. transact() facts are durable-on-return; single-op facts ride the group-commit flush exactly like buffered history. A fact-append failure fails the write, loudly — a silent gap would be a lie a later replay discovers. - New public surface: brain.scanFacts() (sequential batches with heal telemetry: head/segments/approx up front, per-batch generation range + bytes + segment id, loud abort on gaps, summary cross-check) and brain.factSegmentPaths() (immutable sealed segments for zero-copy consumers; the mutable tail excluded). Exported types CommitFact, FactOp, FactScanBatch, FactScanHandle. - Storage: optional binary raw-byte primitives (appendRawBytes, readRawBytes, writeRawBytes, rawByteSize) on StorageAdapter — feature-detected; filesystem + memory adapters implement them; an adapter without them hosts no fact log. Fact segments are byte-copied (never hard-linked) into snapshots. The _generations/facts/ namespace is registered as a protected family (rebuildable: false): no sweeper or GC may delete under it. - New crc32c (Castagnoli) utility with RFC known-answer tests.
This commit is contained in:
parent
92299f27be
commit
38b0041464
13 changed files with 1493 additions and 4 deletions
|
|
@ -423,6 +423,22 @@ export interface GenerationStorage {
|
|||
/** Read all lines of `_system/tx-log.jsonl` (empty array if absent). */
|
||||
readTxLogLines(): Promise<string[]>
|
||||
|
||||
/**
|
||||
* OPTIONAL binary raw-byte primitives — the substrate for the generation
|
||||
* fact log's append-only CRC-framed segments. Feature-detected: a storage
|
||||
* layer that omits them hosts no fact log (dual-write is skipped; readers
|
||||
* fall back to canonical enumeration). Paths are used VERBATIM (no
|
||||
* suffixing). Append durability rides `syncRawObjects` at the commit
|
||||
* barrier, exactly like the staged history files.
|
||||
*/
|
||||
appendRawBytes?(path: string, bytes: Uint8Array): Promise<void>
|
||||
/** Read a raw binary file whole; absent → null; a real fault throws. */
|
||||
readRawBytes?(path: string): Promise<Uint8Array | null>
|
||||
/** Replace a raw binary file atomically (tmp → fsync → rename). */
|
||||
writeRawBytes?(path: string, bytes: Uint8Array): Promise<void>
|
||||
/** Byte size of a raw binary file, or null when absent. */
|
||||
rawByteSize?(path: string): Promise<number | null>
|
||||
|
||||
/**
|
||||
* OPTIONAL temporal-blob contract (implemented by blob-aware storage; the
|
||||
* generation store treats the hashes as opaque strings). Extract the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue