refactor(8.0): graph analytics contract — intent names, not algorithm names

The public brain.graph.* surface and the GraphAccelerationProvider seam must name
operations by INTENT, not by the algorithm that happens to implement them — so a
plugin can swap algorithms without the contract lying. The JS fallback computes
communities via connected-components and rank via PageRank, but a native provider
(cor) is free to use Louvain/Leiden for communities and personalized-PageRank /
eigenvector-centrality for rank: same intent, different algorithm, same name.

Renamed the (not-yet-implemented) Phase-C analytics methods + their option/result
types on GraphAccelerationProvider:
- pageRank -> rank            (PageRankOptions -> RankOptions; dropped the
                               pagerank-internal damping/maxIterations/tolerance
                               knobs from the contract — each impl tunes itself)
- connectedComponents -> communities   (ComponentsOptions -> CommunitiesOptions,
                               mode:'weak'|'strong' -> directed?:boolean;
                               GraphComponents -> GraphCommunities, componentIds/
                               Count -> communityIds/Count)
- shortestPath -> path        (ShortestPathOptions -> PathOptions; weight -> by:'hops'|'weight')
- neighborhoodSample -> sample (NeighborhoodSampleOptions -> SampleOptions)
- topByDegree -> mostConnected (TopByDegreeOptions -> MostConnectedOptions)

traverse / edgesForNode / graphCursorOpen/Next/Close + GraphScores/GraphPath/
OpaqueIdSet/Subgraph are UNCHANGED — everything cor has already built is untouched;
this is a pure pre-implementation rename (cor coordinated). JSDoc reworded to state
the algorithm is the provider's choice. index.ts exports + the native-seam test
mock updated. tsc/unit 1517/integration 599 green.
This commit is contained in:
David Snelling 2026-06-22 09:54:01 -07:00
parent 96d9c0b92c
commit f3e69110f0
3 changed files with 75 additions and 59 deletions

View file

@ -194,13 +194,13 @@ export type {
GraphCursorOptions,
GraphCursorChunk,
GraphScores,
GraphComponents,
GraphCommunities,
GraphPath,
PageRankOptions,
ComponentsOptions,
ShortestPathOptions,
NeighborhoodSampleOptions,
TopByDegreeOptions
RankOptions,
CommunitiesOptions,
PathOptions,
SampleOptions,
MostConnectedOptions
} from './plugin.js'
// Export embedding functionality

View file

@ -400,69 +400,77 @@ export interface GraphCursorChunk {
done: boolean
}
/** Per-node scalar scores (pageRank, topByDegree); `scores[i]` belongs to `nodeInts[i]`. */
/** Per-node scores returned by `rank` / `mostConnected`; `scores[i]` belongs to `nodeInts[i]` (descending). */
export interface GraphScores {
nodeInts: BigInt64Array
scores: Float64Array
}
/** Connected-component labelling; `componentIds[i]` is the component of `nodeInts[i]`. */
export interface GraphComponents {
/** Community/cluster labelling; `communityIds[i]` is the group of `nodeInts[i]`. */
export interface GraphCommunities {
nodeInts: BigInt64Array
componentIds: Uint32Array
componentCount: number
communityIds: Uint32Array
communityCount: number
}
/** A shortest path: the node sequence + the edges between them (or `null` if unreachable). */
/** A path between two nodes — the node sequence + the edges between them (or `null` if unreachable). */
export interface GraphPath {
nodeInts: BigInt64Array
edgeVerbInts: BigInt64Array
/** Sum of edge weights for a weighted path; hop count for an unweighted one. */
/** Cost of the path: number of hops, or summed edge weight when `by: 'weight'`. */
cost: number
}
export interface PageRankOptions {
/** Damping factor (default 0.85). */
damping?: number
/** Max power-iterations (default ~20). */
maxIterations?: number
/** Convergence tolerance. */
tolerance?: number
excludeVisibility?: string[]
/** Return only the top-K by score (default: all). */
/**
* Options for `rank` (importance / influence ranking). INTENT-level only the
* ranking ALGORITHM is the provider's choice (the JS fallback uses PageRank; a
* native provider may use personalized PageRank, eigenvector centrality, etc.),
* so no algorithm-tuning knobs are exposed here.
*/
export interface RankOptions {
/** Return only the top-K nodes by score (default: all). */
topK?: number
}
export interface ComponentsOptions {
/** `'weak'` ignores direction (default); `'strong'` honors it. */
mode?: 'weak' | 'strong'
/** Visibility tiers to EXCLUDE from the ranked set (default `['internal','system']`). */
excludeVisibility?: string[]
}
export interface ShortestPathOptions {
/** Options for `communities` (grouping related nodes). The grouping algorithm is the provider's choice. */
export interface CommunitiesOptions {
/** Treat the graph as directed when grouping (default `false` — direction-agnostic). */
directed?: boolean
excludeVisibility?: string[]
}
/** Options for `path` (best route between two nodes). */
export interface PathOptions {
direction?: GraphTraversalDirection
/** `'edge'` = weighted by edge weight; `'hops'` = unweighted BFS (default `'hops'`). */
weight?: 'edge' | 'hops'
/** Optimize for fewest `'hops'` (default) or least summed edge `'weight'`. */
by?: 'hops' | 'weight'
/** Restrict the route to these verb-type indices (TypeIdx). */
verbTypes?: number[]
excludeVisibility?: string[]
/** Abandon the search past this many hops. */
maxDepth?: number
}
export interface NeighborhoodSampleOptions {
/** Options for `sample` (a representative neighborhood sample for dense-graph viz). */
export interface SampleOptions {
/** Max hop distance from any seed. */
depth: number
direction?: GraphTraversalDirection
/** Max neighbors to sample per node (random, seeded for reproducibility). */
/** Max neighbors kept per node (random, seeded for reproducibility). */
fanout: number
/** RNG seed so a given (seeds, opts, seed) yields a stable sample for viz. */
/** RNG seed so a given (seeds, opts, seed) yields a stable sample. */
seed?: number
excludeVisibility?: string[]
maxNodes?: number
}
export interface TopByDegreeOptions {
direction?: GraphTraversalDirection
/** Options for `mostConnected` (the most-connected nodes). */
export interface MostConnectedOptions {
/** Return the top-K most-connected nodes. */
topK: number
direction?: GraphTraversalDirection
excludeVisibility?: string[]
}
@ -538,45 +546,53 @@ export interface GraphAccelerationProvider {
graphCursorClose(handle: GraphCursorHandle): Promise<void>
/**
* @description PageRank over the visible graph (hidden tiers excluded by default,
* so importance reflects the public view).
* @param options - Damping, iterations, tolerance, visibility, optional top-K.
* @description Rank nodes by influence/importance over the VISIBLE graph (hidden
* tiers excluded by default, so the ranking reflects the public view). The
* ranking ALGORITHM is the provider's choice the JS fallback uses PageRank; a
* native provider may use personalized PageRank, eigenvector centrality, etc. The
* intent ("which nodes matter most") is the contract, not the algorithm.
* @param options - Top-K + visibility (intent-level; no algorithm tuning).
* @param generation - Optional as-of generation.
* @returns Per-node scores.
* @returns Per-node scores, descending.
*/
pageRank(options: PageRankOptions, generation?: bigint): Promise<GraphScores>
rank(options: RankOptions, generation?: bigint): Promise<GraphScores>
/**
* @description Connected components over the visible graph.
* @param options - Weak/strong, visibility.
* @description Group related nodes into communities over the VISIBLE graph. The
* grouping ALGORITHM is the provider's choice the JS fallback uses connected
* components; a native provider may use community detection (e.g. Louvain/Leiden).
* @param options - Directed-grouping toggle + visibility.
* @param generation - Optional as-of generation.
* @returns Per-node component labels + component count.
* @returns Per-node community labels + community count.
*/
connectedComponents(options: ComponentsOptions, generation?: bigint): Promise<GraphComponents>
communities(options: CommunitiesOptions, generation?: bigint): Promise<GraphCommunities>
/**
* @description Shortest path between two nodes (weighted by edge weight or hop count).
* @description Find the best path between two nodes fewest hops by default, or
* least summed edge weight with `by: 'weight'`. The pathfinding ALGORITHM is the
* provider's choice (JS fallback: BFS / Dijkstra).
* @param fromInt - Start node int.
* @param toInt - End node int.
* @param options - Direction, weighting, filters, max depth.
* @param options - Direction, cost basis (`by`), verb-type filter, max depth, visibility.
* @param generation - Optional as-of generation.
* @returns The path, or `null` if `toInt` is unreachable from `fromInt`.
*/
shortestPath(fromInt: bigint, toInt: bigint, options: ShortestPathOptions, generation?: bigint): Promise<GraphPath | null>
path(fromInt: bigint, toInt: bigint, options: PathOptions, generation?: bigint): Promise<GraphPath | null>
/**
* @description Bounded, randomly-sampled neighborhood expansion (capped fan-out per
* node) a representative subgraph for dense-graph viz. Seeded for reproducibility.
* @description A bounded, representative SAMPLE of the neighborhood around the seeds
* (capped fan-out per node) for dense-graph viz. Seeded for reproducibility.
* @param seeds - Start nodes (ints or an {@link OpaqueIdSet}).
* @param options - Depth, fan-out, RNG seed, visibility, node cap.
* @param generation - Optional as-of generation.
* @returns The sampled {@link Subgraph}.
*/
neighborhoodSample(seeds: bigint[] | OpaqueIdSet, options: NeighborhoodSampleOptions, generation?: bigint): Promise<Subgraph>
sample(seeds: bigint[] | OpaqueIdSet, options: SampleOptions, generation?: bigint): Promise<Subgraph>
/**
* @description The top-K nodes by degree over the visible graph.
* @param options - Direction, K, visibility.
* @description The top-K most-connected nodes over the VISIBLE graph. The
* connectivity metric is the provider's choice (JS fallback: edge-count degree).
* @param options - Top-K, direction, visibility.
* @param generation - Optional as-of generation.
* @returns Per-node degree scores (descending).
* @returns Per-node connectivity scores, descending.
*/
topByDegree(options: TopByDegreeOptions, generation?: bigint): Promise<GraphScores>
mostConnected(options: MostConnectedOptions, generation?: bigint): Promise<GraphScores>
}
/**

View file

@ -45,11 +45,11 @@ function makeMockAccel() {
graphCursorClose: async () => {
calls.cursorClose++
},
pageRank: async () => empty,
connectedComponents: async () => ({ nodeInts: new BigInt64Array(0), componentIds: new Uint32Array(0), componentCount: 0 }),
shortestPath: async () => null,
neighborhoodSample: async () => state.traverseResult,
topByDegree: async () => empty
rank: async () => empty,
communities: async () => ({ nodeInts: new BigInt64Array(0), communityIds: new Uint32Array(0), communityCount: 0 }),
path: async () => null,
sample: async () => state.traverseResult,
mostConnected: async () => empty
}
return { provider, state }
}