mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
87b9793c53
Co-authored-by: David <17119984+fengdwx@users.noreply.github.com>
263 lines
8.9 KiB
TypeScript
263 lines
8.9 KiB
TypeScript
import { digestCanonicalValue } from "../canonicalDigest.js";
|
|
import { uniqueSorted } from "../canonicalOrdering.js";
|
|
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
|
import type { Evidence } from "../evidence.js";
|
|
import {
|
|
createJavaScriptApplicationGraph,
|
|
type JavaScriptApplicationGraph,
|
|
} from "./javascriptApplicationGraph.js";
|
|
import type {
|
|
ApplicationEdge,
|
|
ApplicationNode,
|
|
} from "./javascriptApplicationGraphSchemas.js";
|
|
import {
|
|
applicationFeatureTraceResultSchema,
|
|
type ApplicationFeatureTraceResult,
|
|
type TraceApplicationFeatureInput,
|
|
} from "./javascriptFeatureTraceSchemas.js";
|
|
import {
|
|
findApplicationFeatureSeeds,
|
|
type ApplicationFeatureSeedMatch,
|
|
} from "./javascriptFeatureSeed.js";
|
|
import {
|
|
traverseApplicationFeature,
|
|
type ApplicationFeatureTraversal,
|
|
} from "./javascriptFeatureTraversal.js";
|
|
import { buildJavaScriptNativeHandoffs } from "./javascriptNativeHandoff.js";
|
|
|
|
/** Pure, authenticated inputs projected by the application service. */
|
|
export interface ApplicationFeatureTraceProjectionInput {
|
|
readonly sourceEvidenceId: string;
|
|
readonly graph: JavaScriptApplicationGraph;
|
|
readonly nativeEvidence: readonly Evidence[];
|
|
readonly seed: TraceApplicationFeatureInput["seed"];
|
|
readonly direction: TraceApplicationFeatureInput["direction"];
|
|
}
|
|
|
|
/** Trace a literal feature through its complete reachable, authority-preserving JAG. */
|
|
export const traceApplicationFeature = (
|
|
input: ApplicationFeatureTraceProjectionInput,
|
|
): ApplicationFeatureTraceResult => {
|
|
const allSeedMatches = findApplicationFeatureSeeds(
|
|
input.graph.nodes,
|
|
input.seed,
|
|
);
|
|
const seedMatches = allSeedMatches;
|
|
if (seedMatches.length === 0) return noMatchResult(input);
|
|
const traversal = traverseApplicationFeature(
|
|
input.graph,
|
|
seedMatches.map(({ node_id: id }) => id),
|
|
input.direction,
|
|
);
|
|
const graph = traceGraph(input, seedMatches, traversal);
|
|
const paths = terminalPaths(traversal, seedMatches);
|
|
const nativeHandoffs = buildJavaScriptNativeHandoffs(
|
|
traversal.nodes,
|
|
traversal.edges,
|
|
input.nativeEvidence,
|
|
);
|
|
const evidenceLinks = uniqueSorted([
|
|
input.sourceEvidenceId,
|
|
...nativeHandoffs.flatMap(({ evidence_ids: ids }) => ids),
|
|
]);
|
|
const semantic = {
|
|
source_evidence_id: input.sourceEvidenceId,
|
|
source_graph_id: input.graph.graph_id,
|
|
seed: input.seed,
|
|
direction: input.direction,
|
|
seed_matches: seedMatches,
|
|
graph,
|
|
paths,
|
|
native_handoffs: nativeHandoffs,
|
|
summary: {
|
|
matched_seeds: seedMatches.length,
|
|
traced_nodes: traversal.nodes.length,
|
|
traced_edges: traversal.edges.length,
|
|
terminal_paths: paths.length,
|
|
native_handoffs: nativeHandoffs.length,
|
|
...factSummary(traversal.nodes, traversal.edges),
|
|
},
|
|
coverage: {
|
|
status: traceCoverageStatus(input.graph),
|
|
source_graph_status: input.graph.coverage.status,
|
|
total_seed_matches: allSeedMatches.length,
|
|
},
|
|
evidence_links: evidenceLinks,
|
|
limitations: traceLimitations(input.graph),
|
|
};
|
|
return applicationFeatureTraceResultSchema.parse({
|
|
...semantic,
|
|
trace_id: `jatr_${digestCanonicalValue(semantic, "Feature trace")}`,
|
|
});
|
|
};
|
|
|
|
const noMatchResult = (
|
|
input: ApplicationFeatureTraceProjectionInput,
|
|
): ApplicationFeatureTraceResult => {
|
|
const semantic = {
|
|
source_evidence_id: input.sourceEvidenceId,
|
|
source_graph_id: input.graph.graph_id,
|
|
seed: input.seed,
|
|
direction: input.direction,
|
|
seed_matches: [],
|
|
graph: null,
|
|
paths: [],
|
|
native_handoffs: [],
|
|
summary: {
|
|
matched_seeds: 0,
|
|
traced_nodes: 0,
|
|
traced_edges: 0,
|
|
terminal_paths: 0,
|
|
native_handoffs: 0,
|
|
observed_facts: 0,
|
|
inferred_facts: 0,
|
|
unknown_facts: 0,
|
|
unavailable_facts: 0,
|
|
},
|
|
coverage: {
|
|
status: "no-match" as const,
|
|
source_graph_status: input.graph.coverage.status,
|
|
total_seed_matches: 0,
|
|
},
|
|
evidence_links: [input.sourceEvidenceId],
|
|
limitations: uniqueSorted([
|
|
"No graph entity matched the literal seed; this is not evidence that the feature is absent.",
|
|
...sourceCoverageLimitations(input.graph),
|
|
]),
|
|
};
|
|
return applicationFeatureTraceResultSchema.parse({
|
|
...semantic,
|
|
trace_id: `jatr_${digestCanonicalValue(semantic, "Feature trace")}`,
|
|
});
|
|
};
|
|
|
|
const traceGraph = (
|
|
input: ApplicationFeatureTraceProjectionInput,
|
|
seedMatches: readonly ApplicationFeatureSeedMatch[],
|
|
traversal: ApplicationFeatureTraversal,
|
|
): JavaScriptApplicationGraph => {
|
|
return createJavaScriptApplicationGraph({
|
|
schema: "JavaScriptApplicationGraph",
|
|
root_node_ids: uniqueSorted(seedMatches.map(({ node_id: id }) => id)),
|
|
nodes: traversal.nodes,
|
|
edges: traversal.edges,
|
|
coverage: traceGraphCoverage(input.graph),
|
|
limitations: uniqueSorted([
|
|
...input.graph.limitations,
|
|
"Trace edges retain their source authority; graph connectivity does not prove runtime execution or reachability.",
|
|
]),
|
|
});
|
|
};
|
|
|
|
const traceGraphCoverage = (
|
|
source: JavaScriptApplicationGraph,
|
|
): JavaScriptApplicationGraph["coverage"] => {
|
|
return source.coverage.status === "complete"
|
|
? { status: "complete", truncated: false, omitted_count: 0, limits: [] }
|
|
: source.coverage;
|
|
};
|
|
|
|
const terminalPaths = (
|
|
traversal: ApplicationFeatureTraversal,
|
|
seeds: readonly ApplicationFeatureSeedMatch[],
|
|
): ApplicationFeatureTraceResult["paths"] => {
|
|
const seedIds = new Set(seeds.map(({ node_id: id }) => id));
|
|
const edgeById = new Map(traversal.edges.map((edge) => [edge.edge_id, edge]));
|
|
return traversal.nodes
|
|
.filter(
|
|
(node) =>
|
|
isTerminal(node) &&
|
|
(!seedIds.has(node.node_id) || traversal.nodes.length === 1),
|
|
)
|
|
.map((node) => pathTo(node, traversal.predecessors, edgeById))
|
|
.filter((path): path is NonNullable<typeof path> => path !== null)
|
|
.sort((left, right) =>
|
|
compareUnicodeCodePoints(left.path_id, right.path_id),
|
|
);
|
|
};
|
|
|
|
const pathTo = (
|
|
node: ApplicationNode,
|
|
predecessors: ApplicationFeatureTraversal["predecessors"],
|
|
edgeById: ReadonlyMap<string, ApplicationEdge>,
|
|
): ApplicationFeatureTraceResult["paths"][number] | null => {
|
|
const nodeIds = [node.node_id];
|
|
const edgeIds: string[] = [];
|
|
let current = node.node_id;
|
|
while (predecessors.has(current)) {
|
|
const predecessor = predecessors.get(current);
|
|
if (predecessor === undefined) break;
|
|
nodeIds.push(predecessor.previousNodeId);
|
|
edgeIds.push(predecessor.edgeId);
|
|
current = predecessor.previousNodeId;
|
|
}
|
|
nodeIds.reverse();
|
|
edgeIds.reverse();
|
|
const edges = edgeIds
|
|
.map((edgeId) => edgeById.get(edgeId))
|
|
.filter((edge): edge is ApplicationEdge => edge !== undefined);
|
|
if (edges.length !== edgeIds.length) return null;
|
|
const semantic = {
|
|
start_node_id: nodeIds[0] ?? node.node_id,
|
|
end_node_id: node.node_id,
|
|
end_kind: node.kind,
|
|
node_ids: nodeIds,
|
|
edge_ids: edgeIds,
|
|
authorities: uniqueSorted(edges.map(({ evidence }) => evidence.authority)),
|
|
contains_inference: edges.some(
|
|
({ evidence }) => evidence.state === "inferred",
|
|
),
|
|
};
|
|
return {
|
|
...semantic,
|
|
path_id: `jatp_${digestCanonicalValue(semantic, "Feature trace")}`,
|
|
};
|
|
};
|
|
|
|
const isTerminal = (node: ApplicationNode): boolean =>
|
|
["endpoint", "storage", "native-addon", "native-export", "unknown"].includes(
|
|
node.kind,
|
|
) ||
|
|
(node.kind === "javascript-expression" &&
|
|
node.observations.some((o) => o.properties.step_kind === "terminal"));
|
|
|
|
const factSummary = (
|
|
nodes: readonly ApplicationNode[],
|
|
edges: readonly ApplicationEdge[],
|
|
) => {
|
|
const states = [
|
|
...nodes.flatMap(({ observations }) =>
|
|
observations.map(({ evidence }) => evidence.state),
|
|
),
|
|
...edges.map(({ evidence }) => evidence.state),
|
|
];
|
|
return {
|
|
observed_facts: states.filter((state) => state === "observed").length,
|
|
inferred_facts: states.filter((state) => state === "inferred").length,
|
|
unknown_facts: states.filter((state) => state === "unknown").length,
|
|
unavailable_facts: states.filter((state) => state === "unavailable").length,
|
|
};
|
|
};
|
|
|
|
const traceCoverageStatus = (
|
|
graph: JavaScriptApplicationGraph,
|
|
): ApplicationFeatureTraceResult["coverage"]["status"] =>
|
|
graph.coverage.status === "complete" ? "complete-within-source" : "partial";
|
|
|
|
const traceLimitations = (graph: JavaScriptApplicationGraph): string[] =>
|
|
uniqueSorted([
|
|
"A trace reports graph relationships, not proof that code executed or that a feature is reachable in every state.",
|
|
"Static, native, passive-runtime, inferred, and unknown facts retain their original authority in the returned graph.",
|
|
"Native handoffs never open a binary or invoke a provider automatically; snapshot reuse remains provider/profile/target exact.",
|
|
...sourceCoverageLimitations(graph),
|
|
]);
|
|
|
|
const sourceCoverageLimitations = (
|
|
graph: JavaScriptApplicationGraph,
|
|
): string[] =>
|
|
graph.coverage.status === "complete"
|
|
? []
|
|
: [
|
|
"The source application graph is incomplete; unmatched seeds and frontiers remain unknown.",
|
|
];
|