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).
This commit is contained in:
David Snelling 2026-06-10 10:45:45 -07:00
parent 62f6472fa0
commit 2427bb7960
17 changed files with 1164 additions and 328 deletions

View file

@ -1412,7 +1412,7 @@ export class MetadataIndexManager implements MetadataIndexProvider {
fieldsMap[field] = value
}
}
this.columnStore.addEntity(entityIntId, fieldsMap)
this.columnStore.addEntity(BigInt(entityIntId), fieldsMap)
}
// Adaptive auto-flush based on usage patterns
@ -1501,7 +1501,7 @@ export class MetadataIndexManager implements MetadataIndexProvider {
if (this.columnStore) {
const intId = this.idMapper.getInt(id)
if (intId !== undefined) {
this.columnStore.removeEntity(intId)
this.columnStore.removeEntity(BigInt(intId))
}
}
@ -2073,7 +2073,7 @@ export class MetadataIndexManager implements MetadataIndexProvider {
if (hasFilter && filteredIds.length === 0) return []
let sortedIntIds: number[]
let sortedIntIds: bigint[]
if (hasFilter) {
// Build filter bitmap for the column store
const filterBitmap = new RoaringBitmap32()
@ -2091,9 +2091,10 @@ export class MetadataIndexManager implements MetadataIndexProvider {
)
}
// Convert int IDs back to UUIDs
// Convert int IDs back to UUIDs. Number() narrowing is lossless — the
// shipped EntityIdSpaceExceeded guard caps the JS mapper at u32.
return sortedIntIds
.map(intId => this.idMapper.getUuid(intId))
.map(intId => this.idMapper.getUuid(Number(intId)))
.filter((uuid): uuid is string => uuid !== undefined)
}