feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
/ * *
* @module bigint - contract . test
* @description 8.0 u64 provider contract — BigInt boundary of the JS
* GraphAdjacencyIndex and the coordinator that drives it .
*
* Covers :
* - bigint round - trip through the JS graph index : ` addVerb ` returns a stable
* verb int , ` getVerbIdsBySource ` / ` getVerbIdsByTarget ` return that int , and
* ` verbIntsToIds ` maps it back to the verb - id string ( null for unknown ints )
* - ` getNeighbors ` speaks entity ints both ways via the shared idMapper
* - unmapped entity ints / UUIDs produce empty reads , never errors
* - a missing idMapper fails loudly ( wiring bug , not a silent fallback )
2026-06-11 14:51:00 -07:00
* - coordinator - level behavior : relate → related / neighbors → unrelate
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
* works end - to - end over the BigInt boundary ( public API unchanged )
* /
import { describe , it , expect , beforeEach , afterEach } from 'vitest'
import { Brainy } from '../../../src/brainy.js'
import { GraphAdjacencyIndex } from '../../../src/graph/graphAdjacencyIndex.js'
import { EntityIdMapper } from '../../../src/utils/entityIdMapper.js'
import { MemoryStorage } from '../../../src/storage/adapters/memoryStorage.js'
import { NounType , VerbType } from '../../../src/types/graphTypes.js'
import type { GraphVerb } from '../../../src/coreTypes.js'
// Verb ids are UUID-shaped by contract in 8.0 (storage enforces the shape on
// every save; cortex keys verb-int interning on the raw UUID bytes).
const VERB_UUID_1 = '11111111-1111-4111-8111-111111111111'
const VERB_UUID_2 = '22222222-2222-4222-8222-222222222222'
const VERB_UUID_3 = '33333333-3333-4333-8333-333333333333'
/** Build a minimal GraphVerb for direct index-level tests. */
function makeVerb ( id : string , sourceId : string , targetId : string ) : GraphVerb {
return {
id ,
sourceId ,
targetId ,
vector : [ ] ,
type : VerbType . RelatedTo ,
verb : VerbType.RelatedTo
}
}
describe ( 'GraphAdjacencyIndex — BigInt boundary (JS implementation)' , ( ) = > {
let storage : MemoryStorage
let idMapper : EntityIdMapper
let index : GraphAdjacencyIndex
beforeEach ( async ( ) = > {
storage = new MemoryStorage ( )
await storage . init ( )
idMapper = new EntityIdMapper ( { storage , storageKey : 'test:graph:idMapper' } )
await idMapper . init ( )
index = new GraphAdjacencyIndex ( storage , { } , idMapper )
} )
afterEach ( async ( ) = > {
await index . close ( )
} )
it ( 'addVerb returns a stable verb int; reads return it; verbIntsToIds maps back' , async ( ) = > {
const sourceInt = BigInt ( idMapper . getOrAssign ( 'uuid-source' ) )
const targetInt = BigInt ( idMapper . getOrAssign ( 'uuid-target' ) )
const verb = makeVerb ( VERB_UUID_1 , 'uuid-source' , 'uuid-target' )
verb . sourceInt = sourceInt
verb . targetInt = targetInt
2026-06-16 09:41:59 -07:00
// 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 , 1 n )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
expect ( typeof verbInt ) . toBe ( 'bigint' )
// Re-adding the same verb id returns the SAME interned int (stable).
2026-06-16 09:41:59 -07:00
const verbIntAgain = await index . addVerb ( verb , sourceInt , targetInt , 1 n )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
expect ( verbIntAgain ) . toBe ( verbInt )
// Both directed verb-int reads surface the int from addVerb.
const bySource = await index . getVerbIdsBySource ( sourceInt )
expect ( bySource ) . toContain ( verbInt )
const byTarget = await index . getVerbIdsByTarget ( targetInt )
expect ( byTarget ) . toContain ( verbInt )
// Batch reverse resolution is order-preserving with null for unknowns.
const resolved = await index . verbIntsToIds ( [ verbInt , 999 _999n ] )
expect ( resolved ) . toEqual ( [ VERB_UUID_1 , null ] )
} )
it ( 'getNeighbors speaks entity ints in both directions' , async ( ) = > {
const aInt = BigInt ( idMapper . getOrAssign ( 'uuid-a' ) )
const bInt = BigInt ( idMapper . getOrAssign ( 'uuid-b' ) )
const verb = makeVerb ( VERB_UUID_2 , 'uuid-a' , 'uuid-b' )
2026-06-16 09:41:59 -07:00
await index . addVerb ( verb , aInt , bInt , 1 n )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
const out = await index . getNeighbors ( aInt , { direction : 'out' } )
expect ( out ) . toEqual ( [ bInt ] )
const incoming = await index . getNeighbors ( bInt , { direction : 'in' } )
expect ( incoming ) . toEqual ( [ aInt ] )
const both = await index . getNeighbors ( aInt )
expect ( both ) . toContain ( bInt )
} )
it ( 'reads for unmapped entity ints return empty without error' , async ( ) = > {
// 424242 was never assigned by the mapper — no UUID behind it.
const ghost = 424 _242n
expect ( await index . getNeighbors ( ghost ) ) . toEqual ( [ ] )
expect ( await index . getVerbIdsBySource ( ghost ) ) . toEqual ( [ ] )
expect ( await index . getVerbIdsByTarget ( ghost ) ) . toEqual ( [ ] )
} )
it ( 'verb ints stay resolvable after removeVerb (interning is append-only)' , async ( ) = > {
const sInt = BigInt ( idMapper . getOrAssign ( 'uuid-s' ) )
const tInt = BigInt ( idMapper . getOrAssign ( 'uuid-t' ) )
const verb = makeVerb ( VERB_UUID_3 , 'uuid-s' , 'uuid-t' )
2026-06-16 09:41:59 -07:00
const verbInt = await index . addVerb ( verb , sInt , tInt , 1 n )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
// Persist the verb (vector + metadata — getVerb requires both) so
// removeVerb can resolve its type for count updates.
await storage . saveVerb ( {
id : verb.id ,
vector : [ ] ,
connections : new Map < number , Set < string > > ( ) ,
verb : VerbType.RelatedTo ,
sourceId : verb.sourceId ,
targetId : verb.targetId
} )
await storage . saveVerbMetadata ( verb . id , {
verb : VerbType.RelatedTo ,
createdAt : Date.now ( )
} )
2026-06-16 09:41:59 -07:00
await index . removeVerb ( verb . id , 2 n )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
// Removed verb no longer appears in reads…
expect ( await index . getVerbIdsBySource ( sInt ) ) . toEqual ( [ ] )
// …but its int still reverse-resolves (append-only interning).
expect ( await index . verbIntsToIds ( [ verbInt ] ) ) . toEqual ( [ VERB_UUID_3 ] )
} )
it ( 'fails loudly when the entityIdMapper is not wired' , async ( ) = > {
const bare = new GraphAdjacencyIndex ( storage )
await expect ( bare . getNeighbors ( 1 n ) ) . rejects . toThrow ( /entityIdMapper not wired/ )
await bare . close ( )
} )
} )
describe ( 'Coordinator — relate/related end-to-end over the BigInt boundary' , ( ) = > {
let brain : Brainy
beforeEach ( async ( ) = > {
brain = new Brainy ( { requireSubtype : false , storage : { type : 'memory' } } )
await brain . init ( )
} )
afterEach ( async ( ) = > {
await brain . close ( )
} )
2026-06-11 14:51:00 -07:00
it ( 'relate → related → neighbors round-trips (public API unchanged)' , async ( ) = > {
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
const personId = await brain . add ( {
data : { name : 'Ada' } ,
type : NounType . Person
} )
const projectId = await brain . add ( {
data : { name : 'Analytical Engine' } ,
type : NounType . Thing
} )
const relId = await brain . relate ( {
from : personId ,
to : projectId ,
type : VerbType . WorksWith
} )
2026-06-11 14:51:00 -07:00
// related resolves verb ints back to verb-id strings internally.
const relations = await brain . related ( { from : personId } )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
expect ( relations ) . toHaveLength ( 1 )
expect ( relations [ 0 ] . id ) . toBe ( relId )
expect ( relations [ 0 ] . to ) . toBe ( projectId )
// neighbors() routes through getNeighbors(bigint) + int → UUID mapping.
const out = await brain . neighbors ( personId , { direction : 'outgoing' } )
expect ( out ) . toContain ( projectId )
const incoming = await brain . neighbors ( projectId , { direction : 'incoming' } )
expect ( incoming ) . toContain ( personId )
} )
it ( 'relate feeds the verb-int warm cache; duplicate detection still works' , async ( ) = > {
const a = await brain . add ( { data : { name : 'A' } , type : NounType . Thing } )
const b = await brain . add ( { data : { name : 'B' } , type : NounType . Thing } )
const first = await brain . relate ( { from : a , to : b , type : VerbType . RelatedTo } )
// The AddToGraphIndexOperation callback recorded the verb int → id pair.
const warmCache : Map < bigint , string > = ( brain as any ) . verbIntWarmCache
expect ( [ . . . warmCache . values ( ) ] ) . toContain ( first )
// Duplicate check runs through getVerbIdsBySource(bigint) + resolution.
const duplicate = await brain . relate ( { from : a , to : b , type : VerbType . RelatedTo } )
expect ( duplicate ) . toBe ( first )
} )
it ( 'unrelate removes the edge from BigInt-boundary reads' , async ( ) = > {
const a = await brain . add ( { data : { name : 'A' } , type : NounType . Thing } )
const b = await brain . add ( { data : { name : 'B' } , type : NounType . Thing } )
const relId = await brain . relate ( { from : a , to : b , type : VerbType . RelatedTo } )
await brain . unrelate ( relId )
2026-06-11 14:51:00 -07:00
const relations = await brain . related ( { from : a } )
feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror):
- getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and
return entity/verb ints as bigint[]
- new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7
identity-fingerprint design — verb ids are UUIDs by contract, so the
provider-side interning is losslessly reversible)
- addVerb(verb, sourceInt, targetInt) returns the interned verb int;
removeVerb(verbId) joins the contract
Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on
writes, getInt on reads (unmapped UUID -> empty result without calling the
provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry
insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb
returns and resolver results. GraphVerb gains derived sourceInt/targetInt
(populated at add time, never persisted). findConnectedSubtype gains a
native fast path that routes single-type single-subtype outgoing BFS
through the provider when available.
JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed
internally: entity ints resolve through the shared entity-id mapper (threaded
in by the coordinator on init/fork/checkout), verb ints come from an
in-process append-only interning map re-derived from storage on
rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/
removeEntity take bigint, sortTopK/filteredSortTopK return bigint[].
relate() now rejects a caller-supplied id with a teaching error — verb ids
are brainy-generated UUIDs by contract in 8.0 (previously a passed id was
silently ignored). No Roaring64 provider-boundary decode site exists yet;
the JS-internal column store stays Roaring32 and the Treemap decoder lands
with the first consumer of provider-returned filter buffers.
Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
2026-06-10 10:45:45 -07:00
expect ( relations ) . toHaveLength ( 0 )
} )
it ( 'neighbors() of an unknown UUID returns empty without error' , async ( ) = > {
const result = await brain . neighbors ( '00000000-0000-4000-8000-000000000000' )
expect ( result ) . toEqual ( [ ] )
} )
it ( 'relate() rejects a caller-supplied verb id with a teaching error (8.0 contract)' , async ( ) = > {
const a = await brain . add ( { data : { name : 'A' } , type : NounType . Thing } )
const b = await brain . add ( { data : { name : 'B' } , type : NounType . Thing } )
// RelateParams has no `id` field — untyped callers passing one used to be
// silently ignored. 8.0 teaches: verb ids are brainy-generated UUIDs.
await expect (
brain . relate ( { from : a , to : b , type : VerbType . RelatedTo , id : 'my-custom-id' } as any )
) . rejects . toThrow ( /Verb ids are UUIDs by contract in 8\.0/ )
} )
} )