feat(8.0): thread commit generation through the graph-write provider contract
Graph time-travel needs an edge's existence recorded per generation so db.asOf(g) hops resolve historically correct endpoints. The metadata layer already threads brainy's commit generation per write; the graph write path did not, leaving a versioned verb-endpoint store unable to answer "which edges existed at generation g". - GraphIndexProvider.addVerb/removeVerb gain a `generation: bigint` parameter (the same watermark the storage layer stamps onto the record). A provider with a per-generation edge chain stamps the edge at that generation; the JS baseline has no such chain and accepts-and-ignores it — graph time-travel is a native-provider capability, and the open-core path serves edges as-of-now (the one documented graph time-travel limitation). - The two graph transaction operations resolve the generation via a thunk at EXECUTE time: the generation store assigns the batch generation only once the commit begins executing, after the operations are planned. The same generation is reused for an operation's rollback half. - All graph-write call sites pass the in-flight generation. Adds a spy-provider test proving the threading, execute-time resolution, and forward/rollback generation reuse. The JS index ignores the value, so behaviour is unchanged: unit 1402/1402, db-mvcc 25/25, bigint-contract relate/unrelate 10/10.
This commit is contained in:
parent
b26d3d42b3
commit
0951fa1da0
6 changed files with 195 additions and 26 deletions
|
|
@ -64,11 +64,13 @@ describe('GraphAdjacencyIndex — BigInt boundary (JS implementation)', () => {
|
|||
verb.sourceInt = sourceInt
|
||||
verb.targetInt = targetInt
|
||||
|
||||
const verbInt = await index.addVerb(verb, sourceInt, targetInt)
|
||||
// The 4th arg is the commit generation — contract parity with native
|
||||
// providers; the JS index ignores it (no per-generation edge chain).
|
||||
const verbInt = await index.addVerb(verb, sourceInt, targetInt, 1n)
|
||||
expect(typeof verbInt).toBe('bigint')
|
||||
|
||||
// Re-adding the same verb id returns the SAME interned int (stable).
|
||||
const verbIntAgain = await index.addVerb(verb, sourceInt, targetInt)
|
||||
const verbIntAgain = await index.addVerb(verb, sourceInt, targetInt, 1n)
|
||||
expect(verbIntAgain).toBe(verbInt)
|
||||
|
||||
// Both directed verb-int reads surface the int from addVerb.
|
||||
|
|
@ -88,7 +90,7 @@ describe('GraphAdjacencyIndex — BigInt boundary (JS implementation)', () => {
|
|||
const bInt = BigInt(idMapper.getOrAssign('uuid-b'))
|
||||
const verb = makeVerb(VERB_UUID_2, 'uuid-a', 'uuid-b')
|
||||
|
||||
await index.addVerb(verb, aInt, bInt)
|
||||
await index.addVerb(verb, aInt, bInt, 1n)
|
||||
|
||||
const out = await index.getNeighbors(aInt, { direction: 'out' })
|
||||
expect(out).toEqual([bInt])
|
||||
|
|
@ -113,7 +115,7 @@ describe('GraphAdjacencyIndex — BigInt boundary (JS implementation)', () => {
|
|||
const tInt = BigInt(idMapper.getOrAssign('uuid-t'))
|
||||
const verb = makeVerb(VERB_UUID_3, 'uuid-s', 'uuid-t')
|
||||
|
||||
const verbInt = await index.addVerb(verb, sInt, tInt)
|
||||
const verbInt = await index.addVerb(verb, sInt, tInt, 1n)
|
||||
// Persist the verb (vector + metadata — getVerb requires both) so
|
||||
// removeVerb can resolve its type for count updates.
|
||||
await storage.saveVerb({
|
||||
|
|
@ -129,7 +131,7 @@ describe('GraphAdjacencyIndex — BigInt boundary (JS implementation)', () => {
|
|||
createdAt: Date.now()
|
||||
})
|
||||
|
||||
await index.removeVerb(verb.id)
|
||||
await index.removeVerb(verb.id, 2n)
|
||||
|
||||
// Removed verb no longer appears in reads…
|
||||
expect(await index.getVerbIdsBySource(sInt)).toEqual([])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue