feat: provider access to the fact log + shared stamp verifier via internals
Three additive surfaces for native index providers, which hold only `storage` and must never construct their own fact-log reader (the log's open path is writer-side — it reconciles by truncating/rewriting): - Fact-scan capability on the storage adapter (the getBinaryBlobPath pattern): the host brain wires a closure over its LIVE fact log at init; providers call storage.scanFacts()/factLogHeadGeneration()/ factSegmentPaths() and fall back to the enumeration walk on null. Restore/reopen swaps the underlying log transparently. - The family-stamp trio (readFamilyStamp/writeFamilyStamp/ verifyFamilyStamp + types) exported from @soulcraft/brainy/internals, so 'one verifier' is literally one function shared with the native side, never two synchronized copies. - Rollup invariants widened to number|string: content fingerprints (e.g. a per-tree SHA-256) are valid invariant values; strict equality either way, and a type mismatch reads as incoherence, never a pass.
This commit is contained in:
parent
70886da548
commit
352e356fd5
7 changed files with 153 additions and 3 deletions
|
|
@ -140,5 +140,24 @@ describe('entity-tree family stamp', () => {
|
|||
expect(verifyFamilyStamp(enumerated, 2, { 'a.bin': 128 })).toEqual({ state: 'coherent' })
|
||||
expect(verifyFamilyStamp(enumerated, 2, { 'a.bin': 64 }).state).toBe('incoherent')
|
||||
expect(verifyFamilyStamp(enumerated, 2, {}).state).toBe('incoherent') // missing member
|
||||
|
||||
// Rollup invariants may be STRING fingerprints (e.g. a per-tree SHA-256):
|
||||
// strict equality either way; a type mismatch reads as incoherence.
|
||||
const fingerprinted: FamilyStamp = {
|
||||
family: 'z',
|
||||
generation: 1,
|
||||
committedAt: new Date().toISOString(),
|
||||
sourceGeneration: 3,
|
||||
members: { mode: 'rollup', invariants: { treeDigest: 'abc123', rows: 42 } }
|
||||
}
|
||||
expect(verifyFamilyStamp(fingerprinted, 3, { treeDigest: 'abc123', rows: 42 })).toEqual({
|
||||
state: 'coherent'
|
||||
})
|
||||
expect(verifyFamilyStamp(fingerprinted, 3, { treeDigest: 'deadbeef', rows: 42 }).state).toBe(
|
||||
'incoherent'
|
||||
)
|
||||
expect(verifyFamilyStamp(fingerprinted, 3, { treeDigest: 'abc123', rows: '42' }).state).toBe(
|
||||
'incoherent' // type mismatch never passes
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -108,6 +108,25 @@ describe('fact log dual-write (memory adapter)', () => {
|
|||
}
|
||||
})
|
||||
|
||||
it('the storage fact-scan capability serves a provider holding only `storage`', async () => {
|
||||
// An index provider receives `storage` — never the brain — and reaches the
|
||||
// fact log through the host-wired capability (it must never construct its
|
||||
// own fact-log reader: the log's open path is writer-side).
|
||||
const id = await brain.add({ data: 'via storage', type: 'document', metadata: { s: 1 } })
|
||||
await brain.remove(id)
|
||||
|
||||
const storage = brain.storage
|
||||
expect(typeof storage.scanFacts).toBe('function')
|
||||
expect(storage.factLogHeadGeneration()).toBe(brain.scanFacts()!.headGeneration)
|
||||
|
||||
const viaStorage: CommitFact[] = []
|
||||
for await (const b of storage.scanFacts()!.batches()) viaStorage.push(...b.facts)
|
||||
const viaBrain: CommitFact[] = []
|
||||
for await (const b of brain.scanFacts()!.batches()) viaBrain.push(...b.facts)
|
||||
expect(viaStorage.map((f) => f.generation)).toEqual(viaBrain.map((f) => f.generation))
|
||||
expect(storage.factSegmentPaths()).toEqual(brain.factSegmentPaths())
|
||||
})
|
||||
|
||||
it('scan telemetry carries the frozen shape end-to-end', async () => {
|
||||
for (let i = 0; i < 5; i++) await brain.add({ data: `t${i}`, type: 'document', metadata: { i } })
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue