From a8c58c6605ac37a8b504167732affc5ce4fc772f Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Tue, 29 Sep 2026 11:22:31 +0800 Subject: [PATCH] refactor(analysis): remove caller-selected result caps --- bridge/hopper_bridge.py | 40 +---- .../ApplicationWorkflowEvidenceResolver.ts | 10 -- src/application/CapabilityInventory.test.ts | 2 - .../JavaScriptApplicationWorkflowService.ts | 2 - .../ManagedReconstructionService.test.ts | 2 +- src/application/RealHopperAssertions.ts | 2 +- src/domain/hopperValues.test.ts | 3 + src/domain/hopperValues.ts | 9 - ...riptApplicationVersionComparisonSchemas.ts | 1 - .../javascriptExportShapeComparisonSchemas.ts | 1 - .../javascriptSemanticGraphTraversal.test.ts | 47 ++++- src/domain/javascriptSemanticQuery.ts | 164 +----------------- .../javascriptSemanticQueryAssessment.ts | 53 ++---- src/domain/javascriptSemanticQuerySchemas.ts | 37 +--- src/domain/managedMemberComparison.ts | 1 - .../managedNativeVerificationSchemas.ts | 1 - src/domain/managedReconstruction.ts | 1 - src/domain/managedRuntimeCorrelation.ts | 1 - src/domain/sourceToBundleComparison.test.ts | 43 ++--- src/domain/sourceToBundleComparison.ts | 112 +----------- src/domain/sourceToBundleComparisonSchemas.ts | 39 +---- src/domain/sourceToBundleSignals.ts | 24 +-- src/domain/webBundleAnalysis.ts | 1 - 23 files changed, 120 insertions(+), 476 deletions(-) diff --git a/bridge/hopper_bridge.py b/bridge/hopper_bridge.py index 8d4938753e..0560321ab8 100644 --- a/bridge/hopper_bridge.py +++ b/bridge/hopper_bridge.py @@ -255,18 +255,14 @@ def _containing_procedure(document, address): return (procedure, None) if procedure is not None else (None, "not_in_procedure") -def _instruction_addresses(procedure, limit=None): +def _instruction_addresses(procedure): result = [] seen = set() segment = procedure.getSegment() - truncated = False for block in procedure.basicBlockIterator(): address = block.getStartingAddress() end = block.getEndingAddress() while address < end and address not in seen: - if limit is not None and len(result) >= limit: - truncated = True - return result, truncated seen.add(address) instruction = segment.getInstructionAtAddress(address) if instruction is None: @@ -276,7 +272,7 @@ def _instruction_addresses(procedure, limit=None): if length <= 0: break address += length - return result, truncated + return result def _procedure_references(document, params): @@ -284,7 +280,7 @@ def _procedure_references(document, params): direction = params.get("direction", "outgoing") if direction not in ("incoming", "outgoing"): raise ValueError("direction must be incoming or outgoing") - addresses, _ = _instruction_addresses(procedure) + addresses = _instruction_addresses(procedure) edges = set() for address in addresses: segment = _segment(document, address) @@ -408,26 +404,6 @@ def _unavailable(reason): return {"available": False, "reason": reason} -def _offset(params, name): - value = params.get(name, 0) - if not isinstance(value, int) or isinstance(value, bool) or value < 0: - raise ValueError("%s must be a non-negative integer" % name) - return value - - -def _bounded(items, offset, limit, total=None, scan_truncated=False): - selected = items[offset:offset + limit] - known_total = len(items) if total is None and not scan_truncated else total - has_more = offset + len(selected) < len(items) - return { - "items": selected, - "total": known_total, - "returned": len(selected), - "truncated": scan_truncated or has_more, - "next_offset": offset + len(selected) if has_more else None, - } - - def _name_map(document): result = {} for segment in document.getSegmentsList(): @@ -454,8 +430,8 @@ def _render_instruction(segment, address): ) -def _assembly(procedure, limit=None): - """Render bounded assembly while guarding against malformed instruction cycles.""" +def _assembly(procedure): + """Render assembly while guarding against malformed instruction cycles.""" lines = [] segment = procedure.getSegment() seen = set() @@ -470,8 +446,6 @@ def _assembly(procedure, limit=None): line = _render_instruction(segment, address) if line is not None: lines.append(line) - if limit is not None and len(lines) >= limit: - return "\n".join(lines) length = instruction.getInstructionLength() if length <= 0: break @@ -482,7 +456,7 @@ def _assembly(procedure, limit=None): def _read_function_instructions(document, params): """Read all raw instructions without invoking decompilation.""" procedure = _procedure(document, params.get("procedure")) - addresses, _ = _instruction_addresses(procedure) + addresses = _instruction_addresses(procedure) segment = procedure.getSegment() items = [] for address in addresses: @@ -502,7 +476,7 @@ def _read_function_instructions(document, params): def _analyze_function(document, params): """Collect the complete function dossier for agent callers.""" procedure = _procedure(document, params.get("procedure")) - addresses, instruction_scan_truncated = _instruction_addresses(procedure) + addresses = _instruction_addresses(procedure) blocks = [] all_blocks = list(procedure.basicBlockIterator()) for block in all_blocks: diff --git a/src/application/ApplicationWorkflowEvidenceResolver.ts b/src/application/ApplicationWorkflowEvidenceResolver.ts index a0e012b2b1..1328f767fe 100644 --- a/src/application/ApplicationWorkflowEvidenceResolver.ts +++ b/src/application/ApplicationWorkflowEvidenceResolver.ts @@ -165,9 +165,6 @@ export const resolveCompareApplicationVersionsRequestValidated = ( ...input.right_native_observations, ...rightNative.value, ], - ...(input.unknown_registry_approved === undefined - ? {} - : { unknown_registry_approved: input.unknown_registry_approved }), }; const parsed = compareApplicationVersionsInputSchema.safeParse(raw); return parsed.success @@ -196,10 +193,6 @@ export const resolveCompareSourceToBundleRequestValidated = ( const raw = { reference: input.reference, application: application.value, - limits: input.limits, - ...(input.unknown_registry_approved === undefined - ? {} - : { unknown_registry_approved: input.unknown_registry_approved }), }; const parsed = compareSourceToBundleInputSchema.safeParse(raw); return parsed.success @@ -244,9 +237,6 @@ export const resolveCompareJavaScriptExportShapesRequestValidated = ( left_export_name: input.left_export_name, right_module_path: input.right_module_path, right_export_name: input.right_export_name, - ...(input.unknown_registry_approved === undefined - ? {} - : { unknown_registry_approved: input.unknown_registry_approved }), }; const parsed = compareJavaScriptExportShapesInputSchema.safeParse(raw); return parsed.success diff --git a/src/application/CapabilityInventory.test.ts b/src/application/CapabilityInventory.test.ts index db23ec5418..c10d337baf 100644 --- a/src/application/CapabilityInventory.test.ts +++ b/src/application/CapabilityInventory.test.ts @@ -32,8 +32,6 @@ const status = ( availability_code: capability.available ? null : (capability.availability_code ?? null), - input_contract_version: 1, - output_contract_version: 1, effects: { mutates_artifact: false, launches_process: false, diff --git a/src/application/JavaScriptApplicationWorkflowService.ts b/src/application/JavaScriptApplicationWorkflowService.ts index eb2b63a805..18a4e51073 100644 --- a/src/application/JavaScriptApplicationWorkflowService.ts +++ b/src/application/JavaScriptApplicationWorkflowService.ts @@ -154,14 +154,12 @@ export const compareSourceToBundleEvidenceValidated = ( rootArtifactSha256: application.rootArtifactSha256, graph: application.graph, }, - limits: input.limits, }); return ok( createSourceToBundleComparisonEvidence( { reference_root_sha256: input.reference.root_sha256, application_evidence_id: application.evidence.evidence_id, - limits: input.limits, }, result, ), diff --git a/src/application/ManagedReconstructionService.test.ts b/src/application/ManagedReconstructionService.test.ts index e891479d80..d4afa734d2 100644 --- a/src/application/ManagedReconstructionService.test.ts +++ b/src/application/ManagedReconstructionService.test.ts @@ -147,7 +147,7 @@ describe("managed decompiler reconstruction import", () => { ).toThrow(/does not match/u); }); - it("wraps imported reconstruction in Evidence v2", () => { + it("wraps imported reconstruction in Evidence", () => { const evidence = importManagedReconstructionEvidence(exampleInput()); expect(evidence.ok).toBe(true); diff --git a/src/application/RealHopperAssertions.ts b/src/application/RealHopperAssertions.ts index 65d1d8e018..737581fa45 100644 --- a/src/application/RealHopperAssertions.ts +++ b/src/application/RealHopperAssertions.ts @@ -8,7 +8,7 @@ export const firstProcedureAddress = (input: unknown): string => { .array(z.object({ address: addressSchema })) .min(1) .parse(input)[0]; - if (first === undefined) throw new TypeError("Procedure page was empty"); + if (first === undefined) throw new TypeError("Procedure inventory was empty"); return first.address; }; diff --git a/src/domain/hopperValues.test.ts b/src/domain/hopperValues.test.ts index 7c2e3dc1c8..f55806a332 100644 --- a/src/domain/hopperValues.test.ts +++ b/src/domain/hopperValues.test.ts @@ -28,6 +28,9 @@ describe("Hopper boundary values", () => { expect(parseNames({ names: [{ address: "0x2", name: "label" }] }).ok).toBe( true, ); + expect(parseNames({ items: [{ address: "0x2", value: "label" }] }).ok).toBe( + false, + ); expect(parseRelatedAddresses({ callers: ["0x3"] }, "callers")).toEqual({ ok: true, value: ["0x3"], diff --git a/src/domain/hopperValues.ts b/src/domain/hopperValues.ts index e3d385dd62..d6e149fed9 100644 --- a/src/domain/hopperValues.ts +++ b/src/domain/hopperValues.ts @@ -221,15 +221,6 @@ export const parseNames = ( value: JsonValue, ): Result => { const unwrapped = unwrapProperty(value, "names"); - const page = z - .object({ - items: z.array(z.object({ address: z.string(), value: z.string() })), - }) - .safeParse(unwrapped); - if (page.success) - return ok( - page.data.items.map(({ address, value: name }) => ({ address, name })), - ); const records = addressedNamesSchema.safeParse(unwrapped); if (records.success) return ok(records.data); const map = addressedNameMapSchema.safeParse(unwrapped); diff --git a/src/domain/javascriptApplicationVersionComparisonSchemas.ts b/src/domain/javascriptApplicationVersionComparisonSchemas.ts index ca02dda1cf..5f98ab1f0e 100644 --- a/src/domain/javascriptApplicationVersionComparisonSchemas.ts +++ b/src/domain/javascriptApplicationVersionComparisonSchemas.ts @@ -15,7 +15,6 @@ export const compareApplicationVersionsInputSchema = z right: evidenceSchema, left_native_observations: z.array(evidenceSchema).default([]), right_native_observations: z.array(evidenceSchema).default([]), - unknown_registry_approved: z.literal(true).optional(), }) .superRefine((input, context) => { if (input.left.evidence_id === input.right.evidence_id) diff --git a/src/domain/javascriptExportShapeComparisonSchemas.ts b/src/domain/javascriptExportShapeComparisonSchemas.ts index 56b47b2f52..24aa02de19 100644 --- a/src/domain/javascriptExportShapeComparisonSchemas.ts +++ b/src/domain/javascriptExportShapeComparisonSchemas.ts @@ -63,7 +63,6 @@ export const compareJavaScriptExportShapesInputSchema = z left_export_name: selectorTextSchema, right_module_path: selectorTextSchema, right_export_name: selectorTextSchema, - unknown_registry_approved: z.literal(true).optional(), }) .superRefine((input, context) => { if (input.left.evidence_id === input.right.evidence_id) diff --git a/src/domain/javascriptSemanticGraphTraversal.test.ts b/src/domain/javascriptSemanticGraphTraversal.test.ts index b21a76a6bd..54401a6f3b 100644 --- a/src/domain/javascriptSemanticGraphTraversal.test.ts +++ b/src/domain/javascriptSemanticGraphTraversal.test.ts @@ -252,7 +252,7 @@ it("returns every relevant unknown inline", () => { direction: "forward-influence", }); expect(result).toMatchObject({ - summary: { relevant_unknowns: 1, retained_unknowns: 1 }, + summary: { relevant_unknowns: 1 }, }); expect(result.unknowns).toHaveLength(1); }); @@ -274,6 +274,51 @@ it("returns the complete deterministic forward influence result inline", () => { expect(first.relations).toHaveLength(3); }); +it("traverses paths beyond the former fixed depth ceiling", () => { + const original = fixtureGraph(); + const request = original.nodes.find(({ kind }) => kind === "request"); + if (request === undefined) throw new TypeError("Missing request node"); + const chain = Array.from({ length: 70 }, (_, index) => + node("binding", `long-chain-${index}`), + ); + const chainRelations = chain.map((target, index) => + createJavaScriptSemanticGraphRelation({ + source_node_id: chain[index - 1]?.node_id ?? request.node_id, + target_node_id: target.node_id, + relation: "reads", + resolution: "resolved", + properties: {}, + evidence: evidence("inferred"), + }), + ); + const { graph_id: _graphId, ...input } = original; + const graph = createJavaScriptSemanticGraph({ + ...input, + nodes: [...original.nodes, ...chain], + relations: [...original.relations, ...chainRelations], + coverage: { + ...original.coverage, + families: original.coverage.families.map((family) => + family.family === "data-flow" + ? { + ...family, + retained_relations: + family.retained_relations + chainRelations.length, + } + : family, + ), + }, + }); + + const result = queryJavaScriptSemanticGraph(graph, { + seed: { kind: "literal", value: "TOKEN" }, + direction: "forward-influence", + }); + + expect(result.summary.traversed_nodes).toBe(74); + expect(result.nodes).toHaveLength(74); +}); + it("keeps relevant dynamic frontiers unknown", () => { const result = queryJavaScriptSemanticGraph(fixtureGraph(true), { seed: { kind: "literal", value: "TOKEN" }, diff --git a/src/domain/javascriptSemanticQuery.ts b/src/domain/javascriptSemanticQuery.ts index 0d2a54e65f..9ff6168a5f 100644 --- a/src/domain/javascriptSemanticQuery.ts +++ b/src/domain/javascriptSemanticQuery.ts @@ -12,44 +12,26 @@ import { type JavaScriptSemanticQueryInput, type JavaScriptSemanticQueryResult, } from "./javascriptSemanticQuerySchemas.js"; +import { assessJavaScriptSemanticQuery } from "./javascriptSemanticQueryAssessment.js"; import { - assessJavaScriptSemanticQuery, - type JavaScriptSemanticQueryFrontier, -} from "./javascriptSemanticQueryAssessment.js"; -import { - canonicalJavaScriptSemanticQueryJson as canonicalJson, javaScriptSemanticQueryIdentifier as queryIdentifier, resolveJavaScriptSemanticQuerySeeds as resolveSeeds, } from "./javascriptSemanticQueryIdentity.js"; -const TRAVERSAL_LIMITS = { - max_seed_matches: 1_000, - max_nodes: 50_000, - max_edges: 100_000, - max_depth: 64, - max_functions: 10_000, - max_modules: 10_000, - max_unknowns: 10_000, -} as const; - interface TraversalEntry { readonly relation: JavaScriptSemanticGraphRelation; readonly nextNodeId: string; } -type QueryFrontier = JavaScriptSemanticQueryFrontier; - interface Traversal { readonly nodeIds: Set; readonly relationIds: Set; readonly functionIds: Set; readonly modules: Set; - readonly frontier: QueryFrontier[]; } interface SeedAdmission { readonly nodeIds: string[]; - readonly frontier: QueryFrontier[]; } interface QueryResult { @@ -57,7 +39,7 @@ interface QueryResult { readonly retainedRelations: JavaScriptSemanticGraphRelation[]; } -/** Run one deterministic bounded traversal over a verified semantic graph. */ +/** Run one deterministic traversal over a verified semantic graph. */ export const queryJavaScriptSemanticGraph = ( graph: JavaScriptSemanticGraph, rawInput: unknown, @@ -65,24 +47,17 @@ export const queryJavaScriptSemanticGraph = ( const input = javaScriptSemanticQueryInputSchema.parse(rawInput); const queryId = queryIdentifier(graph, input); const seeds = resolveSeeds(graph, input); - const admission = admitSeeds(graph, seeds, input); + const admission = admitSeeds(graph, seeds); const retainedSeeds = admission.nodeIds; const adjacency = buildAdjacency(graph.relations, input); - const traversal = traverse(graph, retainedSeeds, adjacency, input); - const frontier = uniqueFrontier([ - ...traversal.frontier, - ...admission.frontier, - ]); + const traversal = traverse(graph, retainedSeeds, adjacency); const result = createQueryResult(graph, traversal); const allRelevantUnknowns = relevantUnknownFrontiers( graph, traversal.nodeIds, input, ); - const relevantUnknowns = allRelevantUnknowns.slice( - 0, - TRAVERSAL_LIMITS.max_unknowns, - ); + const relevantUnknowns = allRelevantUnknowns; const candidateRelations = relevantCandidateRelationCount( graph, traversal.nodeIds, @@ -96,13 +71,10 @@ export const queryJavaScriptSemanticGraph = ( const assessment = assessJavaScriptSemanticQuery({ graph, totalSeeds: seeds.length, - retainedSeeds: retainedSeeds.length, expectedMatches: expectedMatches.length, hasExpectation: input.expected !== null, - frontier, unknowns: relevantUnknowns, candidateRelations, - unknownsTruncated: allRelevantUnknowns.length > relevantUnknowns.length, }); return javaScriptSemanticQueryResultSchema.parse({ query_id: queryId, @@ -117,13 +89,11 @@ export const queryJavaScriptSemanticGraph = ( expected_match_node_ids: expectedMatches.map(({ node_id }) => node_id), summary: { total_seed_matches: seeds.length, - retained_seed_matches: retainedSeeds.length, traversed_nodes: traversal.nodeIds.size, traversed_relations: traversal.relationIds.size, traversed_functions: traversal.functionIds.size, traversed_modules: traversal.modules.size, relevant_unknowns: allRelevantUnknowns.length, - retained_unknowns: relevantUnknowns.length, }, coverage: assessment.coverage, limitations: assessment.limitations, @@ -146,68 +116,15 @@ const createQueryResult = ( const admitSeeds = ( graph: JavaScriptSemanticGraph, seeds: readonly string[], - input: JavaScriptSemanticQueryInput, ): SeedAdmission => { const nodes = new Map(graph.nodes.map((node) => [node.node_id, node])); const retained: string[] = []; - const functions = new Set(); - const modules = new Set(); - const frontier: QueryFrontier[] = []; for (const nodeId of seeds) { const node = nodes.get(nodeId); if (node === undefined) continue; - const reason = seedBlockedReason(node, { - retained, - functions, - modules, - input, - }); - if (reason !== null) { - frontier.push({ node_id: nodeId, depth: 0, reason }); - continue; - } retained.push(nodeId); - retainOwners(node, functions, modules); } - return { nodeIds: retained, frontier: uniqueFrontier(frontier) }; -}; - -interface SeedBounds { - readonly retained: readonly string[]; - readonly functions: ReadonlySet; - readonly modules: ReadonlySet; - readonly input: JavaScriptSemanticQueryInput; -} - -interface TraversalBounds { - readonly nodes: ReadonlyMap; - readonly nodeIds: ReadonlySet; - readonly relationIds: ReadonlySet; - readonly functionIds: ReadonlySet; - readonly modules: ReadonlySet; - readonly input: JavaScriptSemanticQueryInput; -} - -const seedBlockedReason = ( - node: JavaScriptSemanticGraphNode, - bounds: SeedBounds, -): QueryFrontier["reason"] | null => { - if (bounds.retained.length >= TRAVERSAL_LIMITS.max_seed_matches) - return "max-seed-matches"; - if (bounds.retained.length >= TRAVERSAL_LIMITS.max_nodes) return "max-nodes"; - const functionId = functionOwner(node); - if ( - functionId !== null && - !bounds.functions.has(functionId) && - bounds.functions.size >= TRAVERSAL_LIMITS.max_functions - ) - return "max-functions"; - if ( - !bounds.modules.has(node.identity.module_path) && - bounds.modules.size >= TRAVERSAL_LIMITS.max_modules - ) - return "max-modules"; - return null; + return { nodeIds: retained }; }; const buildAdjacency = ( @@ -300,43 +217,25 @@ const traverse = ( graph: JavaScriptSemanticGraph, seeds: readonly string[], adjacency: ReadonlyMap, - input: JavaScriptSemanticQueryInput, ): Traversal => { const nodes = new Map(graph.nodes.map((node) => [node.node_id, node])); const nodeIds = new Set(seeds); const relationIds = new Set(); const functionIds = new Set(); const modules = new Set(); - const frontier: QueryFrontier[] = []; + const queue = [...nodeIds]; for (const nodeId of nodeIds) retainOwners(nodes.get(nodeId), functionIds, modules); - const depths = new Map([...nodeIds].map((nodeId) => [nodeId, 0])); - const queue = [...nodeIds]; - const bounds: TraversalBounds = { - nodes, - nodeIds, - relationIds, - functionIds, - modules, - input, - }; for (let offset = 0; offset < queue.length; offset += 1) { const current = queue[offset]; if (current === undefined) continue; - const depth = depths.get(current) ?? 0; for (const entry of adjacency.get(current) ?? []) { - const reason = blockedReason(entry, depth, bounds); - if (reason !== null) { - frontier.push({ node_id: current, depth, reason }); - continue; - } relationIds.add(entry.relation.relation_id); if (nodeIds.has(entry.nextNodeId)) continue; const node = nodes.get(entry.nextNodeId); if (node === undefined) continue; nodeIds.add(entry.nextNodeId); retainOwners(node, functionIds, modules); - depths.set(entry.nextNodeId, depth + 1); queue.push(entry.nextNodeId); } } @@ -345,66 +244,21 @@ const traverse = ( relationIds, functionIds, modules, - frontier: uniqueFrontier(frontier), }; }; -const blockedReason = ( - entry: TraversalEntry, - depth: number, - bounds: TraversalBounds, -): QueryFrontier["reason"] | null => { - if (depth >= TRAVERSAL_LIMITS.max_depth) return "max-depth"; - if ( - !bounds.relationIds.has(entry.relation.relation_id) && - bounds.relationIds.size >= TRAVERSAL_LIMITS.max_edges - ) - return "max-edges"; - if (bounds.nodeIds.has(entry.nextNodeId)) return null; - if (bounds.nodeIds.size >= TRAVERSAL_LIMITS.max_nodes) return "max-nodes"; - const node = bounds.nodes.get(entry.nextNodeId); - if (node === undefined) return null; - const functionId = functionOwner(node); - if ( - functionId !== null && - !bounds.functionIds.has(functionId) && - bounds.functionIds.size >= TRAVERSAL_LIMITS.max_functions - ) - return "max-functions"; - if ( - !bounds.modules.has(node.identity.module_path) && - bounds.modules.size >= TRAVERSAL_LIMITS.max_modules - ) - return "max-modules"; - return null; -}; - const retainOwners = ( node: JavaScriptSemanticGraphNode | undefined, functions: Set, modules: Set, ): void => { if (node === undefined) return; - const functionId = functionOwner(node); + const functionId = + node.kind === "function" ? node.node_id : node.function_node_id; if (functionId !== null) functions.add(functionId); modules.add(node.identity.module_path); }; -const functionOwner = (node: JavaScriptSemanticGraphNode): string | null => - node.kind === "function" ? node.node_id : node.function_node_id; - -const uniqueFrontier = (frontier: readonly QueryFrontier[]): QueryFrontier[] => - [ - ...new Map( - frontier.map((item) => [ - `${item.node_id}\0${String(item.depth)}\0${item.reason}`, - item, - ]), - ).values(), - ].sort((left, right) => - compareCodePoints(canonicalJson(left), canonicalJson(right)), - ); - const relevantUnknownFrontiers = ( graph: JavaScriptSemanticGraph, nodeIds: ReadonlySet, diff --git a/src/domain/javascriptSemanticQueryAssessment.ts b/src/domain/javascriptSemanticQueryAssessment.ts index bc7082476d..100fa29c20 100644 --- a/src/domain/javascriptSemanticQueryAssessment.ts +++ b/src/domain/javascriptSemanticQueryAssessment.ts @@ -3,40 +3,24 @@ import type { JavaScriptSemanticGraph } from "./javascriptSemanticGraph.js"; import type { JavaScriptSemanticGraphUnknown } from "./javascriptSemanticGraphSchemas.js"; import type { JavaScriptSemanticQueryResult } from "./javascriptSemanticQuerySchemas.js"; -/** One caller-limit frontier retained by deterministic query traversal. */ -export interface JavaScriptSemanticQueryFrontier { - readonly node_id: string; - readonly depth: number; - readonly reason: - | "max-depth" - | "max-edges" - | "max-functions" - | "max-modules" - | "max-nodes" - | "max-seed-matches"; -} - /** Inputs needed to classify one completed semantic traversal. */ export interface JavaScriptSemanticQueryAssessmentInput { readonly graph: JavaScriptSemanticGraph; readonly totalSeeds: number; - readonly retainedSeeds: number; readonly expectedMatches: number; readonly hasExpectation: boolean; - readonly frontier: readonly JavaScriptSemanticQueryFrontier[]; readonly unknowns: readonly JavaScriptSemanticGraphUnknown[]; readonly candidateRelations: number; - readonly unknownsTruncated: boolean; } -/** Status, coverage, limits, and limitations derived from one traversal. */ +/** Status, coverage, and limitations derived from one traversal. */ export interface JavaScriptSemanticQueryAssessment { readonly status: JavaScriptSemanticQueryResult["status"]; readonly coverage: JavaScriptSemanticQueryResult["coverage"]; readonly limitations: string[]; } -/** Classify a bounded semantic traversal without promoting unknowns to absence. */ +/** Classify a complete semantic traversal without promoting unknowns to absence. */ export const assessJavaScriptSemanticQuery = ( input: JavaScriptSemanticQueryAssessmentInput, ): JavaScriptSemanticQueryAssessment => ({ @@ -51,28 +35,26 @@ const queryCoverage = ( status: input.graph.coverage.status === "unavailable" ? "unavailable" - : input.frontier.length > 0 || input.unknownsTruncated - ? "truncated" - : input.unknowns.length > 0 || - input.candidateRelations > 0 || - input.graph.coverage.status !== "complete" - ? "partial" - : "complete", - frontier: [...input.frontier], + : input.unknowns.length > 0 || + input.candidateRelations > 0 || + input.graph.coverage.status !== "complete" || + input.graph.coverage.truncated + ? "partial" + : "complete", }); const queryStatus = ( input: JavaScriptSemanticQueryAssessmentInput, ): JavaScriptSemanticQueryResult["status"] => { if (input.graph.coverage.status === "unavailable") return "unsupported"; - if (input.frontier.length > 0 || input.unknownsTruncated) return "truncated"; if (input.candidateRelations > 0) return "ambiguous"; if ( input.totalSeeds === 0 || (input.hasExpectation && input.expectedMatches === 0) ) return input.unknowns.length > 0 || - input.graph.coverage.status !== "complete" + input.graph.coverage.status !== "complete" || + input.graph.coverage.truncated ? "partial" : "no-match"; return input.totalSeeds > 1 ? "ambiguous" : "found"; @@ -84,26 +66,11 @@ const queryLimitations = ( uniqueSorted([ "Semantic graph reachability is a static inference and does not prove runtime execution.", ...input.graph.limitations, - ...(input.totalSeeds === input.retainedSeeds - ? [] - : [ - "Seed matches exceeded the internal traversal safety ceiling; omitted starts remain unexplored.", - ]), - ...(input.frontier.length === 0 - ? [] - : [ - "Traversal stopped at an internal resource ceiling; frontier facts remain unknown.", - ]), ...(input.unknowns.length === 0 ? [] : [ "Relevant dynamic, unsupported, incomplete, or ambiguous semantics remain unknown.", ]), - ...(input.unknownsTruncated - ? [ - "Relevant unknown frontiers exceeded the internal result safety ceiling; omitted unknowns remain unresolved.", - ] - : []), ...(input.candidateRelations === 0 ? [] : [ diff --git a/src/domain/javascriptSemanticQuerySchemas.ts b/src/domain/javascriptSemanticQuerySchemas.ts index 1797938ebb..d324f77372 100644 --- a/src/domain/javascriptSemanticQuerySchemas.ts +++ b/src/domain/javascriptSemanticQuerySchemas.ts @@ -86,51 +86,28 @@ export const javaScriptSemanticQueryInputSchema = z.strictObject({ source_map_authority: sourceMapAuthoritySchema.default({ authority: "none" }), }); -const queryFrontierSchema = z.strictObject({ - node_id: semanticNodeIdSchema, - depth: z.number().int().min(0).max(65), - reason: z.enum([ - "max-depth", - "max-edges", - "max-functions", - "max-modules", - "max-nodes", - "max-seed-matches", - ]), -}); - /** Deterministic semantic trace result. */ export const javaScriptSemanticQueryResultSchema = z.strictObject({ query_id: z.string().regex(/^jsrq_[a-f0-9]{64}$/u), source_graph_id: z.string().regex(/^jsrg_[a-f0-9]{64}$/u), seed: javaScriptSemanticQuerySeedSchema, direction: javaScriptSemanticQueryInputSchema.shape.direction, - status: z.enum([ - "found", - "no-match", - "ambiguous", - "partial", - "truncated", - "unsupported", - ]), - seed_node_ids: z.array(semanticNodeIdSchema).max(1_000), - nodes: z.array(javaScriptSemanticNodeSchema).max(3_000), - relations: z.array(javaScriptSemanticRelationSchema).max(1_000), - unknowns: z.array(javaScriptSemanticUnknownSchema).max(100_000), - expected_match_node_ids: z.array(semanticNodeIdSchema).max(50_000), + status: z.enum(["found", "no-match", "ambiguous", "partial", "unsupported"]), + seed_node_ids: z.array(semanticNodeIdSchema), + nodes: z.array(javaScriptSemanticNodeSchema), + relations: z.array(javaScriptSemanticRelationSchema), + unknowns: z.array(javaScriptSemanticUnknownSchema), + expected_match_node_ids: z.array(semanticNodeIdSchema), summary: z.strictObject({ total_seed_matches: z.number().int().min(0), - retained_seed_matches: z.number().int().min(0), traversed_nodes: z.number().int().min(0), traversed_relations: z.number().int().min(0), traversed_functions: z.number().int().min(0), traversed_modules: z.number().int().min(0), relevant_unknowns: z.number().int().min(0), - retained_unknowns: z.number().int().min(0).max(10_000), }), coverage: z.strictObject({ - status: z.enum(["complete", "partial", "truncated", "unavailable"]), - frontier: z.array(queryFrontierSchema).max(100_000), + status: z.enum(["complete", "partial", "unavailable"]), }), limitations: z.array(z.string().min(1).max(4_096)).max(1_000), }); diff --git a/src/domain/managedMemberComparison.ts b/src/domain/managedMemberComparison.ts index ebba3e181e..506ea80fd7 100644 --- a/src/domain/managedMemberComparison.ts +++ b/src/domain/managedMemberComparison.ts @@ -28,7 +28,6 @@ export const compareManagedMembersInputSchema = z .strictObject({ left: evidenceSchema, right: evidenceSchema, - unknown_registry_approved: z.literal(true).optional(), }) .superRefine((input, context) => { if (input.left.evidence_id === input.right.evidence_id) diff --git a/src/domain/managedNativeVerificationSchemas.ts b/src/domain/managedNativeVerificationSchemas.ts index c0c2551ec9..1aac5204cc 100644 --- a/src/domain/managedNativeVerificationSchemas.ts +++ b/src/domain/managedNativeVerificationSchemas.ts @@ -91,7 +91,6 @@ export const managedNativeVerificationInputSchema = z .strictObject({ managed_boundaries: evidenceSchema, native_observations: z.array(evidenceSchema).min(1), - unknown_registry_approved: z.literal(true).optional(), }) .superRefine((input, context) => { const ids = new Set(); diff --git a/src/domain/managedReconstruction.ts b/src/domain/managedReconstruction.ts index 9f5867ef54..7017e9f7db 100644 --- a/src/domain/managedReconstruction.ts +++ b/src/domain/managedReconstruction.ts @@ -49,7 +49,6 @@ export const managedReconstructionImportInputSchema = z.strictObject({ decompiler: decompilerSchema, methods: z.array(managedReconstructionMethodInputSchema).min(1).max(50), notes: z.array(z.string().min(1).max(4_096)).max(100).default([]), - unknown_registry_approved: z.literal(true).optional(), }); const importedMethodSchema = z.strictObject({ diff --git a/src/domain/managedRuntimeCorrelation.ts b/src/domain/managedRuntimeCorrelation.ts index 71f4af220d..e2ff3c9fe5 100644 --- a/src/domain/managedRuntimeCorrelation.ts +++ b/src/domain/managedRuntimeCorrelation.ts @@ -53,7 +53,6 @@ export const managedRuntimeCorrelationInputSchema = z.strictObject({ allow_network: false, allow_ui: false, }), - unknown_registry_approved: z.literal(true).optional(), }); export const managedRuntimeCorrelationResultSchema = z.strictObject({ diff --git a/src/domain/sourceToBundleComparison.test.ts b/src/domain/sourceToBundleComparison.test.ts index 6ade37376f..a55054020e 100644 --- a/src/domain/sourceToBundleComparison.test.ts +++ b/src/domain/sourceToBundleComparison.test.ts @@ -6,7 +6,10 @@ import { } from "./javascriptApplicationGraph.js"; import { createHistoricalSourceGraph } from "./referenceSourceGraph.js"; import { compareSourceToBundle } from "./sourceToBundleComparison.js"; -import { sourceToBundleComparisonResultSchema } from "./sourceToBundleComparisonSchemas.js"; +import { + compareSourceToBundleInputSchema, + sourceToBundleComparisonResultSchema, +} from "./sourceToBundleComparisonSchemas.js"; import { artifactEvidence } from "./javascriptApplicationGraph.fixture.js"; const HASH = { @@ -31,7 +34,6 @@ describe("historical source to bundle comparison", () => { rootArtifactSha256: HASH.artifact, graph, }, - limits: limits(), }; const first = compareSourceToBundle(input); @@ -104,7 +106,6 @@ describe("historical source to bundle comparison", () => { rootArtifactSha256: HASH.artifact, graph, }, - limits: limits(), }); expect(result.summary).toMatchObject({ removed: 0, unknown: 1 }); @@ -116,7 +117,7 @@ describe("historical source to bundle comparison", () => { expect(result.coverage.status).toBe("partial"); }); - it("keeps exact mappings unknown when candidate output is truncated", () => { + it("retains every candidate without caller-selected caps", () => { const result = compareSourceToBundle({ reference: historicalGraph("complete"), application: { @@ -124,16 +125,15 @@ describe("historical source to bundle comparison", () => { rootArtifactSha256: HASH.artifact, graph: applicationGraph("complete"), }, - limits: { ...limits(), max_candidate_nodes: 1 }, }); - expect(item(result, "src/duplicated.ts").status).toBe("unknown"); - expect( - item(result, "src/duplicated.ts").omitted_candidates, - ).toBeGreaterThan(0); + expect(item(result, "src/duplicated.ts")).toMatchObject({ + status: "duplicated", + candidates: [{}, {}], + }); }); - it("reports the candidate frontier instead of deciding past a limit", () => { + it("evaluates all indexed candidates for each source file", () => { const result = compareSourceToBundle({ reference: historicalGraph("complete", ["src/split.ts"]), application: { @@ -141,20 +141,18 @@ describe("historical source to bundle comparison", () => { rootArtifactSha256: HASH.artifact, graph: applicationGraph("complete"), }, - limits: { ...limits(), max_candidate_evaluations: 1 }, }); - expect(result.items[0]).toMatchObject({ - status: "unknown", - confidence: "unknown", - omitted_candidates: 1, - }); + expect(result.items[0]).toMatchObject({ status: "split" }); expect(result.coverage).toMatchObject({ - status: "truncated", - candidate_evaluations: 1, - omitted_candidate_evaluations: 1, + status: "complete-within-inputs", + candidate_evaluations: 2, }); }); + + it("does not expose caller-selected comparison limits", () => { + expect(compareSourceToBundleInputSchema.shape).not.toHaveProperty("limits"); + }); }); const historicalGraph = ( @@ -300,13 +298,6 @@ const sourceDigest = (path: string): string => { return HASH.unchanged; }; -const limits = () => ({ - max_source_files: 100, - max_application_nodes: 100, - max_candidate_nodes: 100, - max_candidate_evaluations: 10_000, -}); - const item = ( result: ReturnType, sourcePath: string, diff --git a/src/domain/sourceToBundleComparison.ts b/src/domain/sourceToBundleComparison.ts index 39ff9dbd61..63e7a6a9c3 100644 --- a/src/domain/sourceToBundleComparison.ts +++ b/src/domain/sourceToBundleComparison.ts @@ -7,7 +7,6 @@ import type { HistoricalSourceGraph } from "./referenceSourceGraph.js"; import { SOURCE_TO_BUNDLE_SIGNAL_WEIGHTS, sourceToBundleComparisonResultSchema, - type CompareSourceToBundleInput, type SourceToBundleCandidate, type SourceToBundleComparisonItem, type SourceToBundleComparisonResult, @@ -28,7 +27,6 @@ interface SourceToBundleComparisonInput { readonly rootArtifactSha256: string; readonly graph: JavaScriptApplicationGraph; }; - readonly limits: CompareSourceToBundleInput["limits"]; } interface ClassifiedMapping { @@ -49,8 +47,6 @@ interface ComparisonProjection { interface MappingProjection { readonly items: SourceToBundleComparisonItem[]; readonly candidateEvaluations: number; - readonly omittedCandidateEvaluations: number; - readonly omittedCandidateReferences: number; } /** Compare historical source files to static source-bearing application nodes. */ @@ -61,8 +57,6 @@ export const compareSourceToBundle = ( const mappings = compareMappings(input, projection); const factors = { ...projection.factors, - omittedCandidateEvaluations: mappings.omittedCandidateEvaluations, - omittedCandidateReferences: mappings.omittedCandidateReferences, }; const mappedCurrentIds = new Set( mappings.items.flatMap(({ current_node_ids: nodeIds }) => nodeIds), @@ -99,13 +93,8 @@ const projectComparison = ( ): ComparisonProjection => { const allSources = historicalSourceFiles(input.reference); const allCurrentNodes = sourceBearingNodes(input.application.graph.nodes); - const sources = allSources.slice(0, input.limits.max_source_files); - const currentNodes = allCurrentNodes.slice( - 0, - input.limits.max_application_nodes, - ); - const omittedSourceFiles = allSources.length - sources.length; - const omittedApplicationNodes = allCurrentNodes.length - currentNodes.length; + const sources = allSources; + const currentNodes = allCurrentNodes; const index = buildSourceToBundleCandidateIndex(currentNodes); return { sources, @@ -114,17 +103,9 @@ const projectComparison = ( absenceComplete: input.reference.inventory_state === "complete" && input.application.graph.coverage.status === "complete" && - !input.application.graph.coverage.truncated && - omittedSourceFiles === 0 && - omittedApplicationNodes === 0 && - !index.pathIndexTruncated, + !input.application.graph.coverage.truncated, factors: { input, - omittedSourceFiles, - omittedApplicationNodes, - omittedCandidateEvaluations: 0, - omittedCandidateReferences: 0, - pathIndexTruncated: index.pathIndexTruncated, }, }; }; @@ -133,19 +114,12 @@ const compareMappings = ( input: SourceToBundleComparisonInput, projection: ComparisonProjection, ): MappingProjection => { - let evaluationsRemaining = input.limits.max_candidate_evaluations; let candidateEvaluations = 0; - let omittedCandidateEvaluations = 0; - let omittedCandidateReferences = 0; const provisionalItems: SourceToBundleComparisonItem[] = []; for (const source of projection.sources) { const candidateIds = candidateIdsForSource(source, projection.index); - const retainedIds = candidateIds.slice(0, evaluationsRemaining); - const omittedEvaluations = candidateIds.length - retainedIds.length; - evaluationsRemaining -= retainedIds.length; - candidateEvaluations += retainedIds.length; - omittedCandidateEvaluations += omittedEvaluations; - const scored = retainedIds + candidateEvaluations += candidateIds.length; + const scored = candidateIds .map((nodeId) => scoreSourceToBundleCandidate(source, nodeId, projection.index), ) @@ -153,18 +127,7 @@ const compareMappings = ( (candidate): candidate is SourceToBundleCandidate => candidate !== null, ) .sort(compareCandidates); - const retainedCandidates = scored.slice( - 0, - input.limits.max_candidate_nodes, - ); - const omittedCandidates = - omittedEvaluations + scored.length - retainedCandidates.length; - omittedCandidateReferences += omittedCandidates; - const classification = classifyMapping( - retainedCandidates, - omittedCandidates === 0, - projection.absenceComplete, - ); + const classification = classifyMapping(scored, projection.absenceComplete); const semantic = { source_path: source.path, source_sha256: source.sha256, @@ -172,16 +135,10 @@ const compareMappings = ( status: classification.status, confidence: classification.confidence, current_node_ids: classification.currentNodeIds, - candidates: retainedCandidates, - omitted_candidates: omittedCandidates, + candidates: scored, limitations: uniqueSorted([ ...source.limitations, ...classification.limitations, - ...(omittedCandidates > 0 - ? [ - `${String(omittedCandidates)} candidate references were omitted by comparison limits.`, - ] - : []), ]), }; provisionalItems.push({ @@ -192,8 +149,6 @@ const compareMappings = ( return { items: markMergedMappings(input, provisionalItems), candidateEvaluations, - omittedCandidateEvaluations, - omittedCandidateReferences, }; }; @@ -206,13 +161,8 @@ const comparisonCoverage = ( reference_inventory_state: factors.input.reference.inventory_state, application_graph_status: factors.input.application.graph.coverage.status, retained_source_files: projection.sources.length, - omitted_source_files: factors.omittedSourceFiles, retained_application_nodes: projection.currentNodes.length, - omitted_application_nodes: factors.omittedApplicationNodes, candidate_evaluations: mappings.candidateEvaluations, - omitted_candidate_evaluations: mappings.omittedCandidateEvaluations, - omitted_candidate_references: mappings.omittedCandidateReferences, - omitted_unmapped_current_nodes: 0, }); const scoringModel = (): SourceToBundleComparisonResult["scoring"] => ({ @@ -226,18 +176,8 @@ const scoringModel = (): SourceToBundleComparisonResult["scoring"] => ({ const classifyMapping = ( candidates: readonly SourceToBundleCandidate[], - evaluationComplete: boolean, absenceComplete: boolean, ): ClassifiedMapping => { - if (!evaluationComplete) - return { - status: "unknown", - confidence: "unknown", - currentNodeIds: [], - limitations: [ - "Candidate evaluation stopped at the declared limit; a stronger match may be omitted.", - ], - }; const exact = candidates.filter((candidate) => hasSignal(candidate, "exact-source-digest"), ); @@ -387,24 +327,11 @@ const countStatus = ( interface CoverageFactors { readonly input: SourceToBundleComparisonInput; - readonly omittedSourceFiles: number; - readonly omittedApplicationNodes: number; - readonly omittedCandidateEvaluations: number; - readonly omittedCandidateReferences: number; - readonly pathIndexTruncated: boolean; } const coverageStatus = ( factors: CoverageFactors, ): SourceToBundleComparisonResult["coverage"]["status"] => { - if ( - factors.omittedSourceFiles > 0 || - factors.omittedApplicationNodes > 0 || - factors.omittedCandidateEvaluations > 0 || - factors.omittedCandidateReferences > 0 || - factors.pathIndexTruncated - ) - return "truncated"; return factors.input.reference.inventory_state === "complete" && factors.input.application.graph.coverage.status === "complete" ? "complete-within-inputs" @@ -426,31 +353,6 @@ const comparisonLimitations = (factors: CoverageFactors): string[] => : [ `Application graph coverage is ${factors.input.application.graph.coverage.status}; unmatched files remain unknown.`, ]), - ...(factors.omittedSourceFiles > 0 - ? [ - `${String(factors.omittedSourceFiles)} historical source files were omitted by max_source_files.`, - ] - : []), - ...(factors.omittedApplicationNodes > 0 - ? [ - `${String(factors.omittedApplicationNodes)} source-bearing application nodes were omitted by max_application_nodes.`, - ] - : []), - ...(factors.omittedCandidateEvaluations > 0 - ? [ - `${String(factors.omittedCandidateEvaluations)} candidate evaluations were omitted by max_candidate_evaluations.`, - ] - : []), - ...(factors.omittedCandidateReferences > 0 - ? [ - `${String(factors.omittedCandidateReferences)} candidate references were omitted from item output.`, - ] - : []), - ...(factors.pathIndexTruncated - ? [ - "At least one current path exceeded the 128-segment suffix index bound.", - ] - : []), ]); const hasSignal = ( diff --git a/src/domain/sourceToBundleComparisonSchemas.ts b/src/domain/sourceToBundleComparisonSchemas.ts index b9f7938bf3..041c2295ba 100644 --- a/src/domain/sourceToBundleComparisonSchemas.ts +++ b/src/domain/sourceToBundleComparisonSchemas.ts @@ -27,36 +27,17 @@ const sourceToBundleSignalKindSchema = z.enum([ "language-extension", ]); -const sourceToBundleLimitsSchema = z.strictObject({ - max_source_files: z.number().int().min(1).max(100_000).default(10_000), - max_application_nodes: z.number().int().min(1).max(100_000).default(20_000), - max_candidate_nodes: z.number().int().min(1).max(1_000).default(100), - max_candidate_evaluations: z - .number() - .int() - .min(1) - .max(10_000_000) - .default(1_000_000), -}); - -/** Historical source inventory, authenticated application Evidence, and bounds. */ +/** Historical source inventory and authenticated application Evidence. */ export const compareSourceToBundleInputSchema = z.strictObject({ reference: historicalSourceGraphSchema, application: evidenceSchema, - limits: sourceToBundleLimitsSchema.default({ - max_source_files: 10_000, - max_application_nodes: 20_000, - max_candidate_nodes: 100, - max_candidate_evaluations: 1_000_000, - }), - unknown_registry_approved: z.literal(true).optional(), }); const sourceToBundleSignalSchema = z.strictObject({ kind: sourceToBundleSignalKindSchema, weight: z.number().int().min(1).max(100), source_value: boundedTextSchema, - current_values: z.array(boundedTextSchema).min(1).max(64), + current_values: z.array(boundedTextSchema).min(1), }); const sourceToBundleCandidateSchema = z.strictObject({ @@ -82,9 +63,8 @@ const sourceToBundleItemSchema = z.strictObject({ "unknown", ]), confidence: z.enum(["exact", "high", "medium", "unknown"]), - current_node_ids: z.array(nodeIdSchema).max(1_000), - candidates: z.array(sourceToBundleCandidateSchema).max(1_000), - omitted_candidates: z.number().int().min(0), + current_node_ids: z.array(nodeIdSchema), + candidates: z.array(sourceToBundleCandidateSchema), limitations: z.array(boundedTextSchema).max(100), }); @@ -119,10 +99,10 @@ export const sourceToBundleComparisonResultSchema = z.strictObject({ duplicated: z.number().int().min(0), unknown: z.number().int().min(0), }), - items: z.array(sourceToBundleItemSchema).max(100_000), - unmapped_current_node_ids: z.array(nodeIdSchema).max(100_000), + items: z.array(sourceToBundleItemSchema), + unmapped_current_node_ids: z.array(nodeIdSchema), coverage: z.strictObject({ - status: z.enum(["complete-within-inputs", "partial", "truncated"]), + status: z.enum(["complete-within-inputs", "partial"]), reference_inventory_state: z.enum(["complete", "partial", "unknown"]), application_graph_status: z.enum([ "complete", @@ -131,13 +111,8 @@ export const sourceToBundleComparisonResultSchema = z.strictObject({ "unavailable", ]), retained_source_files: z.number().int().min(0), - omitted_source_files: z.number().int().min(0), retained_application_nodes: z.number().int().min(0), - omitted_application_nodes: z.number().int().min(0), candidate_evaluations: z.number().int().min(0), - omitted_candidate_evaluations: z.number().int().min(0), - omitted_candidate_references: z.number().int().min(0), - omitted_unmapped_current_nodes: z.number().int().min(0), }), evidence_links: z.array(evidenceIdSchema).min(1).max(1), limitations: z.array(boundedTextSchema).max(1_000), diff --git a/src/domain/sourceToBundleSignals.ts b/src/domain/sourceToBundleSignals.ts index 96957ee45b..7df07b90af 100644 --- a/src/domain/sourceToBundleSignals.ts +++ b/src/domain/sourceToBundleSignals.ts @@ -35,7 +35,6 @@ export interface SourceToBundleCandidateIndex { readonly byDigest: ReadonlyMap>; readonly byPathSuffix: ReadonlyMap>; readonly byBasename: ReadonlyMap>; - readonly pathIndexTruncated: boolean; } const RELEVANT_NODE_KINDS = new Set([ @@ -51,7 +50,6 @@ const PATH_PROPERTIES = [ "resolved_path", "declared_path", ] as const; -const MAX_PATH_SUFFIXES = 128; const signalWeight = (kind: SourceToBundleSignal["kind"]): number => { const entry = SOURCE_TO_BUNDLE_SIGNAL_WEIGHTS.find( @@ -81,7 +79,7 @@ export const historicalSourceFiles = ( ) .sort((left, right) => compareText(left.path, right.path)); -/** Build bounded-key indices without assigning fuzzy matches. */ +/** Build deterministic candidate indices without assigning fuzzy matches. */ export const buildSourceToBundleCandidateIndex = ( nodes: readonly ApplicationNode[], ): SourceToBundleCandidateIndex => { @@ -89,14 +87,11 @@ export const buildSourceToBundleCandidateIndex = ( const byDigest = new Map>(); const byPathSuffix = new Map>(); const byBasename = new Map>(); - let pathIndexTruncated = false; for (const projection of projections) { for (const digest of projection.digests) addIndexValue(byDigest, digest, projection.node.node_id); for (const path of projection.paths) { - const suffixes = pathSuffixes(path.value); - pathIndexTruncated ||= suffixes.truncated; - for (const suffix of suffixes.values) + for (const suffix of pathSuffixes(path.value)) addIndexValue(byPathSuffix, suffix, projection.node.node_id); addIndexValue( byBasename, @@ -112,7 +107,6 @@ export const buildSourceToBundleCandidateIndex = ( byDigest, byPathSuffix, byBasename, - pathIndexTruncated, }; }; @@ -238,7 +232,7 @@ const signal = ( kind, weight: signalWeight(kind), source_value: sourceValue, - current_values: [...new Set(currentValues)].sort(compareText).slice(0, 64), + current_values: [...new Set(currentValues)].sort(compareText), }); const candidateConfidence = ( @@ -294,17 +288,9 @@ const uniquePaths = (paths: readonly CurrentPath[]): CurrentPath[] => compareText(`${left.kind}\0${left.value}`, `${right.kind}\0${right.value}`), ); -const pathSuffixes = ( - path: string, -): { readonly values: readonly string[]; readonly truncated: boolean } => { +const pathSuffixes = (path: string): string[] => { const parts = path.split("/"); - const start = Math.max(0, parts.length - MAX_PATH_SUFFIXES); - return { - values: parts - .slice(start) - .map((_, index) => parts.slice(start + index).join("/")), - truncated: start > 0, - }; + return parts.map((_, index) => parts.slice(index).join("/")); }; const addIndexValue = ( diff --git a/src/domain/webBundleAnalysis.ts b/src/domain/webBundleAnalysis.ts index f33ceec880..c87fda84cf 100644 --- a/src/domain/webBundleAnalysis.ts +++ b/src/domain/webBundleAnalysis.ts @@ -103,7 +103,6 @@ const sourceMapSchema = z.union([ ]); const webTextArtifactSummarySchema = z.object({ - uri: z.string().regex(/^rea:\/\/web-content\/sha256\/[a-f0-9]{64}$/u), sha256: z.string().regex(/^[a-f0-9]{64}$/u), bytes: z.number().int().min(0), media_type: z.string().min(1).max(256),