Files
rea/src/domain/javascript/javascriptRuntimeReconciliationGraph.ts
T
morluto cf346368c4 perf(javascript): reuse graph buckets and query indexes
Append overlapping observations to node buckets instead of recopying groups, and share one node index across semantic seed admission and traversal. Preserve merged provenance and absent-seed behavior.
2026-10-11 01:54:36 +08:00

255 lines
8.7 KiB
TypeScript

import {
createJavaScriptApplicationGraph,
createJavaScriptApplicationNode,
type JavaScriptApplicationGraph,
} from "./javascriptApplicationGraph.js";
import type {
ApplicationEdge,
ApplicationNode,
} from "./javascriptApplicationGraphSchemas.js";
import type { ParsedStaticLayer } from "./javascriptRuntimeReconciliationParsing.js";
import type { ParsedRuntimeCapture } from "./javascriptRuntimeReconciliationCaptureParsing.js";
import { uniqueSorted } from "../canonicalOrdering.js";
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
import type { RuntimeProjection } from "./javascriptRuntimeReconciliationRuntime.js";
interface ReconciledGraphInput {
readonly layers: readonly ParsedStaticLayer[];
readonly captures: readonly ParsedRuntimeCapture[];
readonly runtime: RuntimeProjection;
readonly reconciliationEdges: readonly ApplicationEdge[];
readonly omittedReconciliationItems: number;
}
interface GraphCoverageFacts {
readonly projectionOmissions: number;
readonly knownOmissions: number;
readonly omissionsKnown: boolean;
readonly inputStatus: "complete" | "partial" | "truncated";
readonly outputTruncated: boolean;
}
/** Combined graph plus omissions introduced while merging its observations. */
export interface ReconciledApplicationGraphProjection {
readonly graph: JavaScriptApplicationGraph;
readonly omittedGraphItems: number;
}
/** Merge source graphs, runtime nodes, and inferred mappings into one valid JAG. */
export const buildReconciledApplicationGraph = (
input: ReconciledGraphInput,
): ReconciledApplicationGraphProjection => {
const merged = mergeNodes([
...input.layers.flatMap(({ graph }) => graph.nodes),
...input.runtime.nodes,
]);
const allEdges = uniqueEdges([
...input.layers.flatMap(({ graph }) => graph.edges),
...input.runtime.edges,
...input.reconciliationEdges,
]);
const bounded = boundGraphContent(input, merged, allEdges);
const facts = graphCoverageFacts(input, bounded.omittedItems);
const graph = createJavaScriptApplicationGraph({
schema: "JavaScriptApplicationGraph",
root_node_ids: bounded.rootNodeIds,
nodes: bounded.nodes,
edges: bounded.edges,
coverage: graphCoverage(input, facts, bounded.nodes.length),
limitations: uniqueSorted([
...input.layers.flatMap(({ graph }) => graph.limitations),
"Runtime presence is bounded to passive capture windows and does not prove feature execution.",
"Cross-layer observed_as edges are inferences; digest equality proves byte identity only.",
"Source-map relationships retain their original static authority and are not promoted into runtime matches.",
...(facts.inputStatus === "truncated"
? [
"One or more source graphs or passive captures were incomplete or truncated.",
]
: []),
...(facts.outputTruncated
? [
"The reconciled graph omitted entities or classifications during projection.",
]
: []),
]),
});
return {
graph,
omittedGraphItems: bounded.omittedItems,
};
};
const boundGraphContent = (
input: ReconciledGraphInput,
nodes: readonly ApplicationNode[],
edges: readonly ApplicationEdge[],
): {
readonly rootNodeIds: string[];
readonly nodes: ApplicationNode[];
readonly edges: ApplicationEdge[];
readonly omittedItems: number;
} => {
const allRoots = preferredRootIds(input);
const rootNodeIds = allRoots;
const sortedNodes = [...nodes].sort((left, right) =>
compareUnicodeCodePoints(left.node_id, right.node_id),
);
const retainedNodes = sortedNodes;
const retainedIds = new Set(retainedNodes.map(({ node_id: id }) => id));
const retainedEdges = [...edges]
.sort((left, right) =>
compareUnicodeCodePoints(left.edge_id, right.edge_id),
)
.filter(
({ source_node_id: source, target_node_id: target }) =>
retainedIds.has(source) && retainedIds.has(target),
);
return {
rootNodeIds,
nodes: retainedNodes,
edges: retainedEdges,
omittedItems: 0,
};
};
const preferredRootIds = (input: ReconciledGraphInput): string[] => {
const runtime = [...input.runtime.targetNodeByEvidenceId.values()].map(
({ node_id: id }) => id,
);
const application = input.layers
.filter(({ role }) => role === "application")
.flatMap(({ graph }) => graph.root_node_ids);
const supplemental = input.layers
.filter(({ role }) => role !== "application")
.flatMap(({ graph }) => graph.root_node_ids);
return [...new Set([...runtime, ...application, ...supplemental])];
};
const graphCoverageFacts = (
input: ReconciledGraphInput,
omittedNodeObservations: number,
): GraphCoverageFacts => {
const projectionOmissions =
input.runtime.omittedEntities +
input.omittedReconciliationItems +
omittedNodeObservations;
const captureTruncated = input.captures.some(({ inspection }) =>
inspection.completeness.conditions.includes("truncated"),
);
const captureHasUnknownOmissions = input.captures.some(
({ inspection }) => inspection.completeness.unavailable_sections.length > 0,
);
const inputTruncated =
input.layers.some(({ graph }) => graph.coverage.truncated) ||
captureTruncated;
const inputComplete =
input.layers.every(({ graph }) => graph.coverage.status === "complete") &&
input.captures.every(
({ inspection }) =>
inspection.completeness.status === "complete_within_window",
);
const outputTruncated = projectionOmissions > 0;
const sourceOmissions = input.layers.reduce(
(total, { graph }) => total + (graph.coverage.omitted_count ?? 0),
0,
);
const omissionsKnown =
!captureTruncated &&
!captureHasUnknownOmissions &&
input.layers.every(({ graph }) => graph.coverage.omitted_count !== null);
return {
projectionOmissions,
knownOmissions: projectionOmissions + sourceOmissions,
omissionsKnown,
inputStatus: inputTruncated
? "truncated"
: inputComplete
? "complete"
: "partial",
outputTruncated,
};
};
const graphCoverage = (
input: ReconciledGraphInput,
facts: GraphCoverageFacts,
nodeCount: number,
): JavaScriptApplicationGraph["coverage"] =>
facts.inputStatus === "complete" && !facts.outputTruncated
? { status: "complete", truncated: false, omitted_count: 0, limits: [] }
: facts.inputStatus === "truncated" || facts.outputTruncated
? {
status: "partial",
truncated: true,
omitted_count: facts.omissionsKnown ? facts.knownOmissions : null,
limits: [
...(facts.inputStatus === "truncated"
? [
{
name: "runtime-reconciliation-input-coverage",
value: input.layers.length + input.captures.length,
unit: "items" as const,
},
]
: []),
...(facts.outputTruncated
? [
{
name: "runtime-reconciliation-output-limits",
value: nodeCount,
unit: "items" as const,
},
]
: []),
],
}
: {
status: "partial",
truncated: false,
omitted_count: facts.omissionsKnown ? facts.knownOmissions : null,
limits: [],
};
const mergeNodes = (nodes: readonly ApplicationNode[]): ApplicationNode[] => {
const grouped = new Map<string, ApplicationNode[]>();
for (const node of nodes) {
const group = grouped.get(node.node_id);
if (group === undefined) grouped.set(node.node_id, [node]);
else group.push(node);
}
const merged = [...grouped.values()].map((group) => {
const first = group[0];
if (first === undefined) throw new TypeError("Empty JAG node group");
if (
group.some(
({ kind, identity }) =>
kind !== first.kind ||
JSON.stringify(identity) !== JSON.stringify(first.identity),
)
)
throw new TypeError("Colliding JAG node identifiers disagree");
const observations = [
...new Map(
group
.flatMap(({ observations: values }) => values)
.map((observation) => [observation.observation_id, observation]),
).values(),
].sort((left, right) =>
compareUnicodeCodePoints(left.observation_id, right.observation_id),
);
return createJavaScriptApplicationNode({
kind: first.kind,
identity: first.identity,
observations: observations.map(
({ observation_id: _id, identifier_strategy: _strategy, ...value }) =>
value,
),
});
});
return merged;
};
const uniqueEdges = (edges: readonly ApplicationEdge[]): ApplicationEdge[] => [
...new Map(edges.map((edge) => [edge.edge_id, edge])).values(),
];