feat: committedGeneration capability + pinned durability/stability contracts

- storage.committedGeneration(): the committed watermark exposed on the
  fact-scan capability, so a provider compares its stamp's
  sourceGeneration against the store's truth without parsing the
  private manifest format (the capability source now carries both the
  live fact log and the committed closure).
- Pinned contract tests: fsync-before-ack (transact passes today; the
  single-op path is pinned via it.fails as the documented target —
  group commit must become LATENCY batching, ack waiting on the shared
  fsync, never durability skipping; the pin flips red when that lands
  so there is no cliff to discover) and scan-stability-under-rotation
  (a scan snapshot yields exactly its facts — no gaps, duplicates, or
  bleed-in — while segments rotate beneath it; the reclaim-during-scan
  variant lands with fact compaction, which does not exist yet).
- FactLog accepts a rotateBytes option so tests exercise real rotation.
This commit is contained in:
David Snelling 2026-07-15 12:44:52 -07:00
parent e4f37cd32b
commit d1ecee10f0
6 changed files with 177 additions and 11 deletions

View file

@ -1005,9 +1005,12 @@ export class Brainy<T = any> implements BrainyInterface<T> {
// so a provider's heal can switch from the enumeration walk to one
// sequential fact scan whenever the log exists.
if (typeof (this.storage as BaseStorage).setFactScanSource === 'function') {
;(this.storage as BaseStorage).setFactScanSource(
() => this.generationStore?.getFactLog() ?? null
)
;(this.storage as BaseStorage).setFactScanSource({
factLog: () => this.generationStore?.getFactLog() ?? null,
// The committed watermark, exposed as a capability so providers
// never parse the store's private manifest format.
committedGeneration: () => this.generationStore?.committedGeneration() ?? 0
})
}
// Entity-tree stamp coherence: compare the stamped sourceGeneration +

View file

@ -1182,6 +1182,15 @@ export interface StorageAdapter {
*/
factLogHeadGeneration?(): number | null
/**
* @description OPTIONAL (rides the fact-scan capability): the COMMITTED
* generation watermark the manifest truth a projection's
* `sourceGeneration` compares against. Exposed as a capability so a
* provider never parses the store's private manifest format. `null` when
* the capability is unwired.
*/
committedGeneration?(): number | null
/**
* @description OPTIONAL (rides the fact-scan capability): the immutable,
* sealed fact-segment file paths covering `fromGeneration` the zero-copy

View file

@ -294,6 +294,8 @@ function parseSegment(
*/
export class FactLog {
private readonly storage: FactLogStorage
/** Rotation threshold (bytes); tests may lower it to exercise rotation. */
private readonly rotateBytes: number
private manifest: FactsManifest = {
formatVersion: FACTS_FORMAT_VERSION,
segments: [],
@ -309,8 +311,9 @@ export class FactLog {
/** Segment paths appended since the last sync (the fsync batch). */
private readonly dirtySegments = new Set<string>()
constructor(storage: FactLogStorage) {
constructor(storage: FactLogStorage, options?: { rotateBytes?: number }) {
this.storage = storage
this.rotateBytes = options?.rotateBytes ?? SEGMENT_ROTATE_BYTES
}
/** The highest committed generation the log holds (0 = empty). */
@ -405,7 +408,7 @@ export class FactLog {
}
if (this.manifest.tailSegment === null) {
await this.startTail(fact.generation)
} else if (this.tailBytes >= SEGMENT_ROTATE_BYTES) {
} else if (this.tailBytes >= this.rotateBytes) {
await this.rotate(fact.generation)
}
const frame = encodeFrame(fact)

View file

@ -851,11 +851,19 @@ export abstract class BaseStorage extends BaseStorageAdapter {
// fact-log reader themselves — the log's open path is writer-side.
// ==========================================================================
/** The host-wired fact-scan source (a closure over the live fact log). */
private _factScanSource: (() => import('../db/factLog.js').FactLog | null) | null = null
/** The host-wired fact-scan source (closures over the live generation state). */
private _factScanSource: {
factLog: () => import('../db/factLog.js').FactLog | null
committedGeneration: () => number
} | null = null
/** HOST-ONLY: wire (or clear) the fact-scan capability's source. */
public setFactScanSource(source: (() => import('../db/factLog.js').FactLog | null) | null): void {
public setFactScanSource(
source: {
factLog: () => import('../db/factLog.js').FactLog | null
committedGeneration: () => number
} | null
): void {
this._factScanSource = source
}
@ -866,19 +874,29 @@ export abstract class BaseStorage extends BaseStorageAdapter {
kinds?: Array<'noun' | 'verb'>
batchSize?: number
}): import('../db/factLog.js').FactScanHandle | null {
const log = this._factScanSource?.()
const log = this._factScanSource?.factLog()
return log ? log.scanFacts(options) : null
}
/** The fact log's head generation, or `null` when no fact log exists. */
public factLogHeadGeneration(): number | null {
const log = this._factScanSource?.()
const log = this._factScanSource?.factLog()
return log ? log.headGeneration() : null
}
/**
* The COMMITTED generation watermark (the manifest truth) the value a
* projection's `sourceGeneration` compares against. Exposed here so a
* provider never parses the store's private manifest format. `null` when
* the capability is unwired (no host, or a bare adapter).
*/
public committedGeneration(): number | null {
return this._factScanSource ? this._factScanSource.committedGeneration() : null
}
/** Sealed fact-segment paths (zero-copy handoff); empty when none. */
public factSegmentPaths(options?: { fromGeneration?: number }): string[] {
const log = this._factScanSource?.()
const log = this._factScanSource?.factLog()
return log ? log.segmentPaths(options) : []
}