test(8.0): cover the native graph seam + make provider resolution factory-tolerant

The brain.graph.subgraph/export NATIVE routing (graphSubgraphNative /
graphExportNative / hydrateNativeSubgraph + provider resolution) had ZERO brainy
CI coverage — in production it's exercised only cross-layer against cor's engine,
so a columnar return-shape or hydration-alignment drift would pass brainy CI
silently. This registers a faithful MOCK GraphAccelerationProvider returning a
columnar Subgraph built from the brain's REAL ints, locking the seam:
- subgraph() routes native (traverse called) and hydrates node int->id, the
  nodeDepth column aligned to the node column, node type via batchGet, and edge
  verb-int->Relation via verbIntsToIds + getVerbsBatchCached.
- node<->depth alignment is preserved when a node int does NOT resolve
  (deleted/unknown) — the unresolvable int is dropped, not collapsed (which would
  shift every later depth). This is the exact hydration risk the release audit flagged.
- export() routes to the graph cursor, hydrates chunks, and ALWAYS closes the handle.

Also fixes a latent contract bug found writing the test: graphAccelerationProvider()
only accepted a ready instance, so a provider registered as a (storage)=>provider
FACTORY (the convention graphIndex/metadataIndex/vector use) would fail the duck-test
and the native path would silently never engage. Now resolves instance OR factory,
cached. Covered by the factory-registration test.
This commit is contained in:
David Snelling 2026-06-22 09:34:09 -07:00
parent 89c4b7016c
commit 29410bc568
2 changed files with 206 additions and 2 deletions

View file

@ -352,6 +352,12 @@ export class Brainy<T = any> implements BrainyInterface<T> {
// Plugin system
private pluginRegistry = new PluginRegistry()
/**
* Resolved native graph-acceleration provider, cached on first `brain.graph.*`
* access. `undefined` = not yet resolved; `null` = resolved, none registered
* (use the TS fallback); otherwise the provider instance.
*/
private _graphAccelProvider: GraphAccelerationProvider | null | undefined = undefined
// Sub-APIs (lazy-loaded)
private _nlp?: NaturalLanguageProcessor
@ -3186,8 +3192,25 @@ export class Brainy<T = any> implements BrainyInterface<T> {
/** Resolve the optional native graph-acceleration provider (feature-detected). */
private graphAccelerationProvider(): GraphAccelerationProvider | undefined {
const provider = this.pluginRegistry.getProvider<unknown>('graphAcceleration')
return isGraphAccelerationProvider(provider) ? provider : undefined
if (this._graphAccelProvider !== undefined) {
return this._graphAccelProvider ?? undefined
}
// Resolve the optional 'graphAcceleration' provider once and cache it. Accept
// EITHER a ready instance OR a `(storage) => provider` factory (the convention
// the graphIndex/metadataIndex/vector providers use) — a registered factory
// would otherwise fail the isGraphAccelerationProvider duck-test and the native
// path would silently never engage.
const raw = this.pluginRegistry.getProvider<unknown>('graphAcceleration')
let resolved: unknown = raw
if (typeof raw === 'function' && !isGraphAccelerationProvider(raw)) {
try {
resolved = (raw as (storage: StorageAdapter) => unknown)(this.storage)
} catch {
resolved = undefined
}
}
this._graphAccelProvider = isGraphAccelerationProvider(resolved) ? resolved : null
return this._graphAccelProvider ?? undefined
}
/**