From f276bcbf6ce2cdf05ca382678868ac2cd42f426c Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 24 Oct 2026 01:11:57 +0800 Subject: [PATCH] refactor(javascript): own paired graph evidence resolution --- .../JavaScriptApplicationWorkflowService.ts | 77 ++++++++++--------- .../cli/workflowInputIdentities.test.ts | 29 +++++++ .../javascriptApplicationWorkflows.test.ts | 2 + .../javascriptExportShapeComparison.test.ts | 13 ++++ 4 files changed, 83 insertions(+), 38 deletions(-) diff --git a/src/application/javascript/JavaScriptApplicationWorkflowService.ts b/src/application/javascript/JavaScriptApplicationWorkflowService.ts index 41e0576d1a..fe4a4b2880 100644 --- a/src/application/javascript/JavaScriptApplicationWorkflowService.ts +++ b/src/application/javascript/JavaScriptApplicationWorkflowService.ts @@ -37,6 +37,7 @@ import { applicationGraphEvidenceInputError, parseApplicationGraphEvidence, parseNativeApplicationEvidence, + type ApplicationGraphEvidenceSource, } from "./JavaScriptApplicationEvidenceGraph.js"; import { createApplicationFeatureTraceEvidence, @@ -81,6 +82,36 @@ const resolveWorkflowEvidence = ( : ok(raw); }; +/** Resolve both selections before authenticating either graph, retaining diagnostic inputs. */ +const resolveApplicationGraphPair = ( + operation: string, + input: { readonly left: unknown; readonly right: unknown }, + lookup: EvidenceLookup | undefined, +): Result< + { + readonly left: ApplicationGraphEvidenceSource; + readonly right: ApplicationGraphEvidenceSource; + readonly selected: { readonly left: unknown; readonly right: unknown }; + }, + AnalysisError +> => { + const selectedLeft = resolveWorkflowEvidence(input.left, lookup); + if (!selectedLeft.ok) return selectedLeft; + const selectedRight = resolveWorkflowEvidence(input.right, lookup); + if (!selectedRight.ok) return selectedRight; + const left = parseApplicationGraphEvidence(selectedLeft.value, ["left"]); + if (!left.ok) + return err(applicationGraphEvidenceInputError(operation, left.error)); + const right = parseApplicationGraphEvidence(selectedRight.value, ["right"]); + if (!right.ok) + return err(applicationGraphEvidenceInputError(operation, right.error)); + return ok({ + left: left.value, + right: right.value, + selected: { left: selectedLeft.value, right: selectedRight.value }, + }); +}; + /** Authenticate Evidence and derive one bounded cross-layer feature trace. */ export const traceApplicationFeatureEvidence = ( rawInput: unknown, @@ -163,23 +194,10 @@ export const compareApplicationVersionsEvidence = ( cause: request.error, }), ); - const selectedLeft = resolveWorkflowEvidence(request.data.left, lookup); - if (!selectedLeft.ok) return selectedLeft; - const selectedRight = resolveWorkflowEvidence(request.data.right, lookup); - if (!selectedRight.ok) return selectedRight; - const input = { - ...request.data, - left: selectedLeft.value, - right: selectedRight.value, - }; - const leftResult = parseApplicationGraphEvidence(input.left, ["left"]); - if (!leftResult.ok) - return err(applicationGraphEvidenceInputError(operation, leftResult.error)); - const rightResult = parseApplicationGraphEvidence(input.right, ["right"]); - if (!rightResult.ok) - return err( - applicationGraphEvidenceInputError(operation, rightResult.error), - ); + const pair = resolveApplicationGraphPair(operation, request.data, lookup); + if (!pair.ok) return pair; + const { left, right } = pair.value; + const input = { ...request.data, ...pair.value.selected }; const leftNativeResult = parseNativeApplicationEvidence( input.left_native_observations, ["left_native_observations"], @@ -196,8 +214,6 @@ export const compareApplicationVersionsEvidence = ( return err( applicationGraphEvidenceInputError(operation, rightNativeResult.error), ); - const left = leftResult.value; - const right = rightResult.value; const leftNative = leftNativeResult.value; const rightNative = rightNativeResult.value; const identities = applicationVersionIdentitiesSchema.safeParse({ @@ -297,25 +313,10 @@ export const compareJavaScriptExportShapesEvidence = ( cause: request.error, }), ); - const selectedLeft = resolveWorkflowEvidence(request.data.left, lookup); - if (!selectedLeft.ok) return selectedLeft; - const selectedRight = resolveWorkflowEvidence(request.data.right, lookup); - if (!selectedRight.ok) return selectedRight; - const input = { - ...request.data, - left: selectedLeft.value, - right: selectedRight.value, - }; - const leftResult = parseApplicationGraphEvidence(input.left, ["left"]); - if (!leftResult.ok) - return err(applicationGraphEvidenceInputError(operation, leftResult.error)); - const rightResult = parseApplicationGraphEvidence(input.right, ["right"]); - if (!rightResult.ok) - return err( - applicationGraphEvidenceInputError(operation, rightResult.error), - ); - const left = leftResult.value; - const right = rightResult.value; + const pair = resolveApplicationGraphPair(operation, request.data, lookup); + if (!pair.ok) return pair; + const { left, right } = pair.value; + const input = { ...request.data, ...pair.value.selected }; const identities = javaScriptExportShapeIdentitiesSchema.safeParse({ left: left.evidence, right: right.evidence, diff --git a/tests/boundary/cli/workflowInputIdentities.test.ts b/tests/boundary/cli/workflowInputIdentities.test.ts index 125e32a069..c59849d639 100644 --- a/tests/boundary/cli/workflowInputIdentities.test.ts +++ b/tests/boundary/cli/workflowInputIdentities.test.ts @@ -239,6 +239,35 @@ const checkMalformedEvidenceBeforeIdentities = async ({ details: { issues: [{ path, reason: "invalid_type" }] }, }); } + const missingLeft = { + kind: "retained-evidence", + evidence_id: `ev_${"a".repeat(64)}`, + }; + const missingRight = { + kind: "retained-evidence", + evidence_id: `ev_${"b".repeat(64)}`, + }; + for (const [command, extra] of [ + ["compare-application-versions", {}], + ["compare-javascript-export-shapes", selectors], + ] as const) { + // Lookup of both selected sources precedes graph authentication. + for (const [left, right, evidenceId] of [ + [malformed, missingRight, missingRight.evidence_id], + [missingLeft, missingRight, missingLeft.evidence_id], + ] as const) { + const rejected = await runJson(cli, root, command, { + left, + right, + ...extra, + }); + expect(rejected.exitCode).toBe(1); + expect(rejected.json).toMatchObject({ + code: "evidence_integrity_mismatch", + details: { evidence_id: evidenceId, reason: "missing" }, + }); + } + } }; cliTest( diff --git a/tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts b/tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts index e0d8430690..d02db4b809 100644 --- a/tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts +++ b/tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts @@ -198,6 +198,8 @@ describe("complete comparison projections", () => { ]); const compared = compareApplicationVersionsEvidence({ left, right }); if (!compared.ok) throw compared.error; + expect(compared.value.sources[0]).toBe(left); + expect(compared.value.sources[1]).toBe(right); const result = compared.value.result; expectFullComparisonResultSchema(result); const modules = result.items.filter( diff --git a/tests/boundary/filesystem/javascriptExportShapeComparison.test.ts b/tests/boundary/filesystem/javascriptExportShapeComparison.test.ts index 705fb7b187..d1f5321a0a 100644 --- a/tests/boundary/filesystem/javascriptExportShapeComparison.test.ts +++ b/tests/boundary/filesystem/javascriptExportShapeComparison.test.ts @@ -7,6 +7,7 @@ import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { analyzeJavaScriptApplication } from "../../support/javascriptApplicationScope.js"; import { parseApplicationGraphEvidence } from "../../../src/application/javascript/JavaScriptApplicationEvidenceGraph.js"; +import { compareJavaScriptExportShapesEvidence } from "../../../src/application/javascript/JavaScriptApplicationWorkflowService.js"; import { compareJavaScriptExportShapes } from "../../../src/domain/javascript/javascriptExportShapeComparison.js"; import { javaScriptExportShapeComparisonResultSchema, @@ -40,6 +41,18 @@ describe("JavaScript export return-shape comparison", () => { const first = compare(left, right); const second = compare(left, right); + const execution = compareJavaScriptExportShapesEvidence({ + left: left.evidence, + right: right.evidence, + left_module_path: "parser.mjs", + left_export_name: "default", + right_module_path: "parser.mjs", + right_export_name: "default", + }); + if (!execution.ok) throw execution.error; + expect(execution.value.result).toEqual(first); + expect(execution.value.sources[0]).toBe(left.evidence); + expect(execution.value.sources[1]).toBe(right.evidence); expect(first.comparison_id).toBe(second.comparison_id); expect(() =>