refactor(javascript): bind application links only at graph finish
Remove the empty application-node index from incremental projection state. The completed application graph now owns the only binding pass, and indexing returns the map directly instead of a wrapper. Validation: 76 builder/composition tests and 19 filesystem, CLI-output, and SDK MCP trace boundary tests pass. Identity budgets and streaming/full-projection parity remain covered; no throughput improvement is claimed.
This commit is contained in:
parent
f9ea9a9268
commit
8ba0a887ff
|
|
@ -355,7 +355,7 @@ const semanticNodeFor = (roleKey: string) => {
|
|||
unpacked: false,
|
||||
text: { included: true, value: "" },
|
||||
};
|
||||
const state = createSemanticGraphProjectionState({ nodes: [] });
|
||||
const state = createSemanticGraphProjectionState();
|
||||
return constructSemanticGraphNode(
|
||||
file,
|
||||
{
|
||||
|
|
@ -565,7 +565,7 @@ it("does not mark a file truncated when it exactly fills its budget", () => {
|
|||
it("only records node loss when the budget rejects a new identity", () => {
|
||||
// The truncation signal must record blocked node creation, not a zero
|
||||
// remaining budget: a file using exactly its share drops nothing.
|
||||
const state = createSemanticGraphProjectionState({ nodes: [] });
|
||||
const state = createSemanticGraphProjectionState();
|
||||
state.fileNodeBudget = 2;
|
||||
state.fileNodesDropped = false;
|
||||
const first = addSemanticGraphNode(state, semanticNodeFor("first"));
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ export interface JavaScriptSemanticGraphProjection {
|
|||
/** Accumulate semantic nodes while each source IR is still file-local. */
|
||||
export const createJavaScriptSemanticGraphProjection =
|
||||
(): JavaScriptSemanticGraphProjection => {
|
||||
const state = emptyState({ nodes: [] });
|
||||
const state = emptyState();
|
||||
const fingerprints: JavaScriptSemanticFingerprint[] = [];
|
||||
let truncatedFiles = 0;
|
||||
const projectSourceSteps = function* (
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ export interface SemanticGraphProjectionState {
|
|||
readonly unknowns: Map<string, JavaScriptSemanticGraphUnknown>;
|
||||
readonly evidenceContexts: JavaScriptSemanticEvidenceContextRegistry;
|
||||
readonly roots: Set<string>;
|
||||
readonly applicationNodeIdsByLocation: ReadonlyMap<string, readonly string[]>;
|
||||
/**
|
||||
* Remaining node budget for the file currently being projected.
|
||||
*
|
||||
|
|
@ -69,32 +68,29 @@ export interface SemanticRelationConstructionInput {
|
|||
readonly properties?: Readonly<Record<string, JsonValue>>;
|
||||
}
|
||||
|
||||
/** Create empty projection state with structural application-node mappings. */
|
||||
export const createSemanticGraphProjectionState = (
|
||||
applicationGraph: Pick<JavaScriptApplicationGraph, "nodes">,
|
||||
): SemanticGraphProjectionState => {
|
||||
const index = indexApplicationNodes(applicationGraph.nodes);
|
||||
return {
|
||||
/** Create empty projection state; application-node links are bound at finish. */
|
||||
export const createSemanticGraphProjectionState =
|
||||
(): SemanticGraphProjectionState => ({
|
||||
nodes: new Map(),
|
||||
relations: new Map(),
|
||||
unknowns: new Map(),
|
||||
evidenceContexts: new JavaScriptSemanticEvidenceContextRegistry(),
|
||||
roots: new Set(),
|
||||
applicationNodeIdsByLocation: index.identifiers,
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
/** Bind retained semantic identities after all application observations exist. */
|
||||
export const bindSemanticGraphApplicationNodes = (
|
||||
state: SemanticGraphProjectionState,
|
||||
applicationGraph: Pick<JavaScriptApplicationGraph, "nodes">,
|
||||
): void => {
|
||||
const index = indexApplicationNodes(applicationGraph.nodes).identifiers;
|
||||
const applicationNodeIdsByLocation = indexApplicationNodes(
|
||||
applicationGraph.nodes,
|
||||
);
|
||||
for (const [identifier, node] of state.nodes) {
|
||||
state.nodes.set(identifier, {
|
||||
...node,
|
||||
application_node_ids: [
|
||||
...(index.get(
|
||||
...(applicationNodeIdsByLocation.get(
|
||||
applicationLocationKey(
|
||||
node.identity.artifact_sha256,
|
||||
node.identity.module_path,
|
||||
|
|
@ -122,7 +118,7 @@ export const constructSemanticGraphNode = (
|
|||
role_key: input.roleKey,
|
||||
},
|
||||
function_node_id: input.functionNodeId,
|
||||
application_node_ids: matchingApplicationNodeIds(file, input, state),
|
||||
application_node_ids: [],
|
||||
label: input.label === null ? null : javascriptDisplayText(input.label),
|
||||
properties: input.properties ?? {},
|
||||
evidence: observedSemanticEvidence(file, input.location),
|
||||
|
|
@ -247,9 +243,7 @@ export const addSemanticFallbackRoot = (
|
|||
|
||||
const indexApplicationNodes = (
|
||||
nodes: JavaScriptApplicationGraph["nodes"],
|
||||
): {
|
||||
readonly identifiers: ReadonlyMap<string, readonly string[]>;
|
||||
} => {
|
||||
): ReadonlyMap<string, readonly string[]> => {
|
||||
const identifiers = new Map<string, Set<string>>();
|
||||
for (const node of nodes) {
|
||||
for (const observation of node.observations) {
|
||||
|
|
@ -280,19 +274,9 @@ const indexApplicationNodes = (
|
|||
return [key, sorted];
|
||||
}),
|
||||
);
|
||||
return { identifiers: indexed };
|
||||
return indexed;
|
||||
};
|
||||
|
||||
const matchingApplicationNodeIds = (
|
||||
file: JavaScriptArtifactFile,
|
||||
input: SemanticNodeConstructionInput,
|
||||
state: SemanticGraphProjectionState,
|
||||
): string[] => [
|
||||
...(state.applicationNodeIdsByLocation.get(
|
||||
applicationLocationKey(file.sha256, file.path, input.location),
|
||||
) ?? []),
|
||||
];
|
||||
|
||||
const addApplicationNodeIdentifier = (
|
||||
identifiers: Map<string, Set<string>>,
|
||||
key: string,
|
||||
|
|
|
|||
Loading…
Reference in New Issue