mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
refactor(evidence): unify retained inputs across domains (#8299)
Declare logical Evidence sources once and resolve exact retained inputs through shared application workflows across 20 composition tools. Preserve domain-owned authentication, source closure, caller choices, coverage and resource admission. Validation includes public CLI/MCP regressions, local Codex terra/high trials and fresh-import real-artifact workflows. Measurements, failed attempts and coverage limits are recorded in docs/verification/evidence-input-composition-2026-10-31.md. Fixes #8293. Fixes #8281. Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
This commit is contained in:
1 parent
23de10bb28
commit
a989c0e458
114 files changed
+8532
-1383
No files matched your search
@@ -1,39 +1,39 @@
|
||||
import type { ExecutionOptions } from "../AnalysisProvider.js";
|
||||
import {
|
||||
resolveEvidenceInput,
|
||||
resolveEvidenceRequest,
|
||||
type EvidenceLookup,
|
||||
} from "../EvidenceInputResolver.js";
|
||||
import { retainedEvidenceReferenceSchema } from "../../domain/evidenceReference.js";
|
||||
import { parseAnalysisInput } from "../AnalysisInput.js";
|
||||
import {
|
||||
compareApplicationVersionsRequestSchema,
|
||||
compareJavaScriptExportShapesRequestSchema,
|
||||
compareSourceToBundleCliInputSchema,
|
||||
compareSourceToBundleRequestSchema,
|
||||
traceApplicationFeatureRequestSchema,
|
||||
} from "../../contracts/javascript/applicationWorkflowInputContracts.js";
|
||||
import type { EvidenceWorkflowExecution } from "../investigation/EvidenceWorkflowExecution.js";
|
||||
import type { ArtifactResourceScope } from "../../artifacts/ArtifactResourceScope.js";
|
||||
import type { ResolvedSourceToBundleReference } from "./SourceToBundleReference.js";
|
||||
import { z } from "zod";
|
||||
|
||||
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import type { Evidence } from "../../domain/evidence.js";
|
||||
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
||||
import { compareJavaScriptApplicationVersions } from "../../domain/javascript/javascriptApplicationVersionComparison.js";
|
||||
import {
|
||||
compareApplicationVersionsInputSchema,
|
||||
applicationVersionIdentitiesSchema,
|
||||
type ApplicationVersionComparisonResult,
|
||||
} from "../../domain/javascript/javascriptApplicationVersionComparisonSchemas.js";
|
||||
import { compareJavaScriptExportShapes } from "../../domain/javascript/javascriptExportShapeComparison.js";
|
||||
import {
|
||||
compareJavaScriptExportShapesInputSchema,
|
||||
javaScriptExportShapeIdentitiesSchema,
|
||||
type JavaScriptExportShapeComparisonResult,
|
||||
} from "../../domain/javascript/javascriptExportShapeComparisonSchemas.js";
|
||||
import { traceApplicationFeature } from "../../domain/javascript/javascriptFeatureTrace.js";
|
||||
import {
|
||||
traceApplicationFeatureInputSchema,
|
||||
applicationTraceIdentitiesSchema,
|
||||
type ApplicationFeatureTraceResult,
|
||||
} from "../../domain/javascript/javascriptFeatureTraceSchemas.js";
|
||||
import { err, ok, type Result } from "../../domain/result.js";
|
||||
import { compareSourceToBundle } from "../../domain/javascript/sourceToBundleComparison.js";
|
||||
import {
|
||||
compareSourceToBundleInputSchema,
|
||||
type SourceToBundleComparisonResult,
|
||||
} from "../../domain/javascript/sourceToBundleComparisonSchemas.js";
|
||||
import type { SourceToBundleComparisonResult } from "../../domain/javascript/sourceToBundleComparisonSchemas.js";
|
||||
import {
|
||||
applicationGraphEvidenceInputError,
|
||||
parseApplicationGraphEvidence,
|
||||
@@ -48,68 +48,32 @@ import {
|
||||
} from "./JavaScriptApplicationWorkflowEvidence.js";
|
||||
|
||||
/** Completed workflow facts and authenticated sources retained for adapter delivery. */
|
||||
export interface ApplicationWorkflowExecution<Projection> {
|
||||
readonly evidence: Evidence;
|
||||
export interface ApplicationWorkflowExecution<
|
||||
Projection,
|
||||
> extends EvidenceWorkflowExecution {
|
||||
readonly result: Projection;
|
||||
readonly sources: readonly Evidence[];
|
||||
}
|
||||
|
||||
// Validate the request frame without cloning source payloads before their owner reads them.
|
||||
const featureTraceFrameSchema = z.strictObject({
|
||||
...traceApplicationFeatureInputSchema.shape,
|
||||
application: z.unknown().nonoptional(),
|
||||
native_observations: z.array(z.unknown()).default([]),
|
||||
});
|
||||
const versionComparisonFrameSchema = z.strictObject({
|
||||
...compareApplicationVersionsInputSchema.shape,
|
||||
left: z.unknown().nonoptional(),
|
||||
right: z.unknown().nonoptional(),
|
||||
left_native_observations: z.array(z.unknown()).default([]),
|
||||
right_native_observations: z.array(z.unknown()).default([]),
|
||||
});
|
||||
const exportShapeFrameSchema = z.strictObject({
|
||||
...compareJavaScriptExportShapesInputSchema.shape,
|
||||
left: z.unknown().nonoptional(),
|
||||
right: z.unknown().nonoptional(),
|
||||
});
|
||||
|
||||
const resolveWorkflowEvidence = (
|
||||
raw: unknown,
|
||||
lookup: EvidenceLookup | undefined,
|
||||
): Result<unknown, AnalysisError> => {
|
||||
const reference = retainedEvidenceReferenceSchema.safeParse(raw);
|
||||
return reference.success
|
||||
? resolveEvidenceInput(reference.data, lookup)
|
||||
: ok(raw);
|
||||
};
|
||||
|
||||
/** Resolve both selections before authenticating either graph, retaining diagnostic inputs. */
|
||||
const resolveApplicationGraphPair = (
|
||||
/** Authenticate the resolved graph pair at the owning domain boundary. */
|
||||
const parseApplicationGraphPair = (
|
||||
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"]);
|
||||
const left = parseApplicationGraphEvidence(input.left, ["left"]);
|
||||
if (!left.ok)
|
||||
return err(applicationGraphEvidenceInputError(operation, left.error));
|
||||
const right = parseApplicationGraphEvidence(selectedRight.value, ["right"]);
|
||||
const right = parseApplicationGraphEvidence(input.right, ["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 },
|
||||
});
|
||||
};
|
||||
|
||||
@@ -117,20 +81,21 @@ const resolveApplicationGraphPair = (
|
||||
export const traceApplicationFeatureEvidence = (
|
||||
rawInput: unknown,
|
||||
lookup?: EvidenceLookup,
|
||||
options: Pick<ExecutionOptions, "signal"> = {},
|
||||
): Result<
|
||||
ApplicationWorkflowExecution<ApplicationFeatureTraceResult>,
|
||||
AnalysisError
|
||||
> => {
|
||||
const operation = "trace_application_feature";
|
||||
const request = parseAnalysisInput(
|
||||
const request = resolveEvidenceRequest(
|
||||
operation,
|
||||
rawInput,
|
||||
featureTraceFrameSchema,
|
||||
traceApplicationFeatureRequestSchema,
|
||||
lookup,
|
||||
options,
|
||||
);
|
||||
if (!request.ok) return request;
|
||||
const selected = resolveWorkflowEvidence(request.value.application, lookup);
|
||||
if (!selected.ok) return selected;
|
||||
const input = { ...request.value, application: selected.value };
|
||||
const input = request.value.input;
|
||||
const sourceResult = parseApplicationGraphEvidence(input.application, [
|
||||
"application",
|
||||
]);
|
||||
@@ -189,21 +154,24 @@ export const traceApplicationFeatureEvidence = (
|
||||
export const compareApplicationVersionsEvidence = (
|
||||
rawInput: unknown,
|
||||
lookup?: EvidenceLookup,
|
||||
options: Pick<ExecutionOptions, "signal"> = {},
|
||||
): Result<
|
||||
ApplicationWorkflowExecution<ApplicationVersionComparisonResult>,
|
||||
AnalysisError
|
||||
> => {
|
||||
const operation = "compare_application_versions";
|
||||
const request = parseAnalysisInput(
|
||||
const request = resolveEvidenceRequest(
|
||||
operation,
|
||||
rawInput,
|
||||
versionComparisonFrameSchema,
|
||||
compareApplicationVersionsRequestSchema,
|
||||
lookup,
|
||||
options,
|
||||
);
|
||||
if (!request.ok) return request;
|
||||
const pair = resolveApplicationGraphPair(operation, request.value, lookup);
|
||||
const input = request.value.input;
|
||||
const pair = parseApplicationGraphPair(operation, input);
|
||||
if (!pair.ok) return pair;
|
||||
const { left, right } = pair.value;
|
||||
const input = { ...request.value, ...pair.value.selected };
|
||||
const leftNativeResult = parseNativeApplicationEvidence(
|
||||
input.left_native_observations,
|
||||
["left_native_observations"],
|
||||
@@ -262,10 +230,47 @@ export const compareApplicationVersionsEvidence = (
|
||||
});
|
||||
};
|
||||
|
||||
/** Compare source and bundle inputs already parsed by a trusted adapter. */
|
||||
export const compareSourceToBundleEvidence = (
|
||||
input: ResolvedSourceToBundleReference &
|
||||
Pick<z.output<typeof compareSourceToBundleInputSchema>, "application">,
|
||||
/** Select the application record and historical graph through their shared owners. */
|
||||
export const executeSourceToBundleComparison = async (
|
||||
rawInput: unknown,
|
||||
schema:
|
||||
| typeof compareSourceToBundleCliInputSchema
|
||||
| typeof compareSourceToBundleRequestSchema,
|
||||
lookup: EvidenceLookup | undefined,
|
||||
resources: ArtifactResourceScope,
|
||||
options: Pick<ExecutionOptions, "signal"> = {},
|
||||
): Promise<
|
||||
Result<
|
||||
ApplicationWorkflowExecution<SourceToBundleComparisonResult>,
|
||||
AnalysisError
|
||||
>
|
||||
> => {
|
||||
const selected = resolveEvidenceRequest(
|
||||
"compare_source_to_bundle",
|
||||
rawInput,
|
||||
schema,
|
||||
lookup,
|
||||
options,
|
||||
);
|
||||
if (!selected.ok) return selected;
|
||||
const { resolveSourceToBundleReference } =
|
||||
await import("./SourceToBundleReference.js");
|
||||
const reference = await resolveSourceToBundleReference(
|
||||
selected.value.input.reference,
|
||||
options.signal,
|
||||
resources,
|
||||
);
|
||||
return reference.ok
|
||||
? compareSourceToBundleEvidence({
|
||||
...reference.value,
|
||||
application: selected.value.input.application,
|
||||
})
|
||||
: reference;
|
||||
};
|
||||
|
||||
/** Compare an owner-parsed historical graph with an unadmitted application source. */
|
||||
const compareSourceToBundleEvidence = (
|
||||
input: ResolvedSourceToBundleReference & { readonly application: unknown },
|
||||
): Result<
|
||||
ApplicationWorkflowExecution<SourceToBundleComparisonResult>,
|
||||
AnalysisError
|
||||
@@ -307,21 +312,24 @@ export const compareSourceToBundleEvidence = (
|
||||
export const compareJavaScriptExportShapesEvidence = (
|
||||
rawInput: unknown,
|
||||
lookup?: EvidenceLookup,
|
||||
options: Pick<ExecutionOptions, "signal"> = {},
|
||||
): Result<
|
||||
ApplicationWorkflowExecution<JavaScriptExportShapeComparisonResult>,
|
||||
AnalysisError
|
||||
> => {
|
||||
const operation = "compare_javascript_export_shapes";
|
||||
const request = parseAnalysisInput(
|
||||
const request = resolveEvidenceRequest(
|
||||
operation,
|
||||
rawInput,
|
||||
exportShapeFrameSchema,
|
||||
compareJavaScriptExportShapesRequestSchema,
|
||||
lookup,
|
||||
options,
|
||||
);
|
||||
if (!request.ok) return request;
|
||||
const pair = resolveApplicationGraphPair(operation, request.value, lookup);
|
||||
const input = request.value.input;
|
||||
const pair = parseApplicationGraphPair(operation, input);
|
||||
if (!pair.ok) return pair;
|
||||
const { left, right } = pair.value;
|
||||
const input = { ...request.value, ...pair.value.selected };
|
||||
const identities = javaScriptExportShapeIdentitiesSchema.safeParse({
|
||||
left: left.evidence,
|
||||
right: right.evidence,
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
import { expect, it, vi } from "vitest";
|
||||
|
||||
import { createEvidence, type Evidence } from "../../domain/evidence.js";
|
||||
import {
|
||||
JAVASCRIPT_APPLICATION_VERSION_COMPARISON_EXAMPLE,
|
||||
JAVASCRIPT_FEATURE_TRACE_EXAMPLE,
|
||||
} from "../../contracts/javascript/javascriptApplicationWorkflowExamples.js";
|
||||
import { JAVASCRIPT_EXPORT_SHAPE_COMPARISON_EXAMPLE } from "../../contracts/javascript/javascriptExportShapeComparisonExample.js";
|
||||
import { JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE } from "../../contracts/javascript/javascriptRuntimeReconciliationExample.js";
|
||||
import {
|
||||
compareApplicationVersionsEvidence,
|
||||
compareJavaScriptExportShapesEvidence,
|
||||
traceApplicationFeatureEvidence,
|
||||
} from "./JavaScriptApplicationWorkflowService.js";
|
||||
import { reconcileJavaScriptRuntimeEvidence } from "./JavaScriptRuntimeReconciliationService.js";
|
||||
import { traceJavaScriptSemanticsEvidence } from "./JavaScriptSemanticTraceService.js";
|
||||
|
||||
const reference = ({ evidence_id }: Evidence) => ({
|
||||
kind: "retained-evidence" as const,
|
||||
evidence_id,
|
||||
});
|
||||
|
||||
const native = createEvidence(
|
||||
{ path: "/example/addon.node", sha256: "e".repeat(64), format: "elf" },
|
||||
{ id: "fixture", name: "Native fixture", version: "1" },
|
||||
{ operation: "procedure_info", parameters: {}, result: {} },
|
||||
);
|
||||
|
||||
it("resolves feature native arrays without changing source identity or unknowns", () => {
|
||||
const input = {
|
||||
...JAVASCRIPT_FEATURE_TRACE_EXAMPLE,
|
||||
native_observations: [native],
|
||||
};
|
||||
const inline = traceApplicationFeatureEvidence(input);
|
||||
const records = new Map(
|
||||
[input.application, native].map((record) => [record.evidence_id, record]),
|
||||
);
|
||||
const lookup = vi.fn((id: string) => records.get(id));
|
||||
const retained = traceApplicationFeatureEvidence(
|
||||
{
|
||||
...input,
|
||||
application: reference(input.application),
|
||||
native_observations: [reference(native)],
|
||||
},
|
||||
lookup,
|
||||
);
|
||||
if (!inline.ok) throw inline.error;
|
||||
if (!retained.ok) throw retained.error;
|
||||
expect(retained.value.evidence.evidence_id).toBe(
|
||||
inline.value.evidence.evidence_id,
|
||||
);
|
||||
expect(retained.value.result).toEqual(inline.value.result);
|
||||
expect(retained.value.sources.map(({ evidence_id }) => evidence_id)).toEqual([
|
||||
input.application.evidence_id,
|
||||
native.evidence_id,
|
||||
]);
|
||||
expect(lookup).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("resolves per-side native observations with one lookup per repeated exact ID", () => {
|
||||
const input = {
|
||||
...JAVASCRIPT_APPLICATION_VERSION_COMPARISON_EXAMPLE,
|
||||
left_native_observations: [native],
|
||||
right_native_observations: [native],
|
||||
};
|
||||
const inline = compareApplicationVersionsEvidence(input);
|
||||
const records = new Map(
|
||||
[input.left, input.right, native].map((record) => [
|
||||
record.evidence_id,
|
||||
record,
|
||||
]),
|
||||
);
|
||||
const lookup = vi.fn((id: string) => records.get(id));
|
||||
const retained = compareApplicationVersionsEvidence(
|
||||
{
|
||||
left: reference(input.left),
|
||||
right: reference(input.right),
|
||||
left_native_observations: [reference(native)],
|
||||
right_native_observations: [reference(native)],
|
||||
},
|
||||
lookup,
|
||||
);
|
||||
if (!inline.ok) throw inline.error;
|
||||
if (!retained.ok) throw retained.error;
|
||||
expect(retained.value.evidence.evidence_id).toBe(
|
||||
inline.value.evidence.evidence_id,
|
||||
);
|
||||
expect(retained.value.result).toEqual(inline.value.result);
|
||||
expect(lookup).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it("uses retained graphs for exact export-shape comparison", () => {
|
||||
const input = JAVASCRIPT_EXPORT_SHAPE_COMPARISON_EXAMPLE;
|
||||
const inline = compareJavaScriptExportShapesEvidence(input);
|
||||
const records = new Map(
|
||||
[input.left, input.right].map((record) => [record.evidence_id, record]),
|
||||
);
|
||||
const retained = compareJavaScriptExportShapesEvidence(
|
||||
{
|
||||
...input,
|
||||
left: reference(input.left),
|
||||
right: reference(input.right),
|
||||
},
|
||||
(id) => records.get(id),
|
||||
);
|
||||
if (!inline.ok) throw inline.error;
|
||||
if (!retained.ok) throw retained.error;
|
||||
expect(retained.value.evidence.evidence_id).toBe(
|
||||
inline.value.evidence.evidence_id,
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves nested static layers and runtime arrays before source-owned reconciliation", () => {
|
||||
const input = JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE;
|
||||
const records = new Map(
|
||||
[
|
||||
...input.static_layers.map(({ analysis }) => analysis),
|
||||
...input.runtime_observations,
|
||||
].map((record) => [record.evidence_id, record]),
|
||||
);
|
||||
const inline = reconcileJavaScriptRuntimeEvidence(input);
|
||||
const retained = reconcileJavaScriptRuntimeEvidence(
|
||||
{
|
||||
...input,
|
||||
static_layers: input.static_layers.map((layer) => ({
|
||||
...layer,
|
||||
analysis: reference(layer.analysis),
|
||||
})),
|
||||
runtime_observations: input.runtime_observations.map(reference),
|
||||
},
|
||||
(id) => records.get(id),
|
||||
);
|
||||
if (!inline.ok) throw inline.error;
|
||||
if (!retained.ok) throw retained.error;
|
||||
expect(retained.value.evidence.evidence_id).toBe(
|
||||
inline.value.evidence.evidence_id,
|
||||
);
|
||||
expect(
|
||||
retained.value.sources.map(({ evidence_id }) => evidence_id).sort(),
|
||||
).toEqual([...records.keys()].sort());
|
||||
});
|
||||
|
||||
it("preserves semantic-trace unknown coverage through retained sources", () => {
|
||||
const application = JAVASCRIPT_FEATURE_TRACE_EXAMPLE.application;
|
||||
const input = {
|
||||
application,
|
||||
query: {
|
||||
seed: { kind: "property", name: "unobserved" },
|
||||
direction: "ownership",
|
||||
},
|
||||
};
|
||||
const inline = traceJavaScriptSemanticsEvidence(input);
|
||||
const retained = traceJavaScriptSemanticsEvidence(
|
||||
{ ...input, application: reference(application) },
|
||||
(id) => (id === application.evidence_id ? application : undefined),
|
||||
);
|
||||
if (!inline.ok) throw inline.error;
|
||||
if (!retained.ok) throw retained.error;
|
||||
expect(retained.value.result).toEqual(inline.value.result);
|
||||
expect(retained.value.sources).toEqual([application]);
|
||||
});
|
||||
|
||||
it("reports missing nested references before reconciliation without guessing a source", () => {
|
||||
const layer = JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE.static_layers[0];
|
||||
if (layer === undefined) throw new Error("Expected an application layer");
|
||||
const result = reconcileJavaScriptRuntimeEvidence({
|
||||
...JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE,
|
||||
static_layers: [{ ...layer, analysis: reference(layer.analysis) }],
|
||||
});
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: { _tag: "EvidenceIntegrityError" },
|
||||
});
|
||||
});
|
||||
|
||||
it("honors cancellation before reading any retained graph", () => {
|
||||
const controller = new AbortController();
|
||||
const reason = { request: "cancel before source resolution" };
|
||||
controller.abort(reason);
|
||||
const application = JAVASCRIPT_FEATURE_TRACE_EXAMPLE.application;
|
||||
const lookup = vi.fn(() => application);
|
||||
const result = traceApplicationFeatureEvidence(
|
||||
{
|
||||
...JAVASCRIPT_FEATURE_TRACE_EXAMPLE,
|
||||
application: reference(application),
|
||||
},
|
||||
lookup,
|
||||
{ signal: controller.signal },
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: { _tag: "AnalysisCancelledError", cause: reason },
|
||||
});
|
||||
expect(lookup).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -1,14 +1,21 @@
|
||||
import type { ExecutionOptions } from "../AnalysisProvider.js";
|
||||
import { resolve } from "node:path";
|
||||
import { z } from "zod";
|
||||
import type { z } from "zod";
|
||||
import {
|
||||
resolveEvidenceRequest,
|
||||
type EvidenceLookup,
|
||||
} from "../EvidenceInputResolver.js";
|
||||
import type { EvidenceWorkflowExecution } from "../investigation/EvidenceWorkflowExecution.js";
|
||||
import type { EvidenceRequestFrameInput } from "../../domain/evidenceInputContract.js";
|
||||
import { reconcileJavaScriptRuntimeRequestSchema } from "../../contracts/javascript/applicationWorkflowInputContracts.js";
|
||||
|
||||
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import type { Evidence } from "../../domain/evidence.js";
|
||||
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
||||
import {
|
||||
readRuntimeReconciliationInput,
|
||||
reconcileJavaScriptRuntime,
|
||||
} from "../../domain/javascript/javascriptRuntimeReconciliation.js";
|
||||
import { reconcileJavaScriptRuntimeInputSchema } from "../../domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { runtimeReconciliationIdentitiesSchema } from "../../domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { err, ok, type Result } from "../../domain/result.js";
|
||||
import { createJavaScriptRuntimeReconciliationEvidence } from "./JavaScriptRuntimeReconciliationEvidence.js";
|
||||
|
||||
@@ -17,32 +24,56 @@ const OPERATION = "reconcile_javascript_runtime" as const;
|
||||
/** Derive a combined JAG from verified static and passive-runtime Evidence. */
|
||||
export const reconcileJavaScriptRuntimeEvidence = (
|
||||
rawInput: unknown,
|
||||
): Result<Evidence, AnalysisError> => {
|
||||
const parsed = reconcileJavaScriptRuntimeInputSchema.safeParse(rawInput);
|
||||
if (!parsed.success)
|
||||
return err(
|
||||
analysisInputErrorFromIssues(OPERATION, parsed.error.issues, rawInput),
|
||||
);
|
||||
return reconcileJavaScriptRuntimeEvidenceValidated(parsed.data);
|
||||
};
|
||||
|
||||
/** Reconcile input already parsed by a trusted adapter boundary. */
|
||||
export const reconcileJavaScriptRuntimeEvidenceValidated = (
|
||||
parsedInput: z.output<typeof reconcileJavaScriptRuntimeInputSchema>,
|
||||
): Result<Evidence, AnalysisError> => {
|
||||
lookup?: EvidenceLookup,
|
||||
options: Pick<ExecutionOptions, "signal"> = {},
|
||||
): Result<EvidenceWorkflowExecution, AnalysisError> => {
|
||||
const selected = resolveEvidenceRequest(
|
||||
OPERATION,
|
||||
rawInput,
|
||||
reconcileJavaScriptRuntimeRequestSchema,
|
||||
lookup,
|
||||
options,
|
||||
);
|
||||
if (!selected.ok) return selected;
|
||||
const parsed = readRuntimeReconciliationInput(
|
||||
normalizeRuntimeMappingRoots(parsedInput),
|
||||
normalizeRuntimeMappingRoots(selected.value.input),
|
||||
);
|
||||
if (!parsed.ok) return parsed;
|
||||
const identities = runtimeReconciliationIdentitiesSchema.safeParse({
|
||||
static_layers: parsed.value.layers.map(({ role, evidence }) => ({
|
||||
role,
|
||||
analysis: evidence,
|
||||
})),
|
||||
runtime_observations: parsed.value.captures.map(({ evidence }) => evidence),
|
||||
});
|
||||
if (!identities.success)
|
||||
return err(
|
||||
analysisInputErrorFromIssues(
|
||||
OPERATION,
|
||||
identities.error.issues,
|
||||
rawInput,
|
||||
),
|
||||
);
|
||||
const result = reconcileJavaScriptRuntime(parsed.value);
|
||||
return ok(
|
||||
createJavaScriptRuntimeReconciliationEvidence(parsed.value.input, result),
|
||||
);
|
||||
return ok({
|
||||
evidence: createJavaScriptRuntimeReconciliationEvidence(
|
||||
parsed.value.input,
|
||||
result,
|
||||
),
|
||||
sources: [
|
||||
...parsed.value.layers.map(({ evidence }) => evidence),
|
||||
...parsed.value.captures.map(({ evidence }) => evidence),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
type RuntimeReconciliationFrame = EvidenceRequestFrameInput<
|
||||
z.output<typeof reconcileJavaScriptRuntimeRequestSchema>
|
||||
>;
|
||||
|
||||
const normalizeRuntimeMappingRoots = (
|
||||
input: z.output<typeof reconcileJavaScriptRuntimeInputSchema>,
|
||||
): z.output<typeof reconcileJavaScriptRuntimeInputSchema> => ({
|
||||
input: RuntimeReconciliationFrame,
|
||||
): RuntimeReconciliationFrame => ({
|
||||
...input,
|
||||
static_layers: input.static_layers.map((layer) => ({
|
||||
...layer,
|
||||
|
||||
@@ -1,26 +1,41 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import type { ExecutionOptions } from "../AnalysisProvider.js";
|
||||
import {
|
||||
resolveEvidenceRequest,
|
||||
type EvidenceLookup,
|
||||
} from "../EvidenceInputResolver.js";
|
||||
import { traceJavaScriptSemanticsRequestSchema } from "../../contracts/javascript/applicationWorkflowInputContracts.js";
|
||||
import { AnalysisInputError } from "../../domain/analysisErrorCore.js";
|
||||
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import {
|
||||
applicationGraphEvidenceInputError,
|
||||
parseApplicationGraphEvidence,
|
||||
} from "./JavaScriptApplicationEvidenceGraph.js";
|
||||
import type { Evidence } from "../../domain/evidence.js";
|
||||
import { queryJavaScriptSemanticGraph } from "../../domain/javascript/javascriptSemanticQuery.js";
|
||||
import {
|
||||
traceJavaScriptSemanticsInputSchema,
|
||||
type JavaScriptSemanticTraceResult,
|
||||
} from "../../domain/javascript/javascriptSemanticTraceSchemas.js";
|
||||
import type { JavaScriptSemanticTraceResult } from "../../domain/javascript/javascriptSemanticTraceSchemas.js";
|
||||
import { err, ok, type Result } from "../../domain/result.js";
|
||||
import { createJavaScriptSemanticTraceEvidence } from "./JavaScriptApplicationWorkflowEvidence.js";
|
||||
import type { ApplicationWorkflowExecution } from "./JavaScriptApplicationWorkflowService.js";
|
||||
|
||||
const OPERATION = "trace_javascript_semantics" as const;
|
||||
|
||||
/** Trace semantic relations from input already parsed by a trusted adapter. */
|
||||
export const traceJavaScriptSemanticsEvidenceValidated = (
|
||||
input: z.output<typeof traceJavaScriptSemanticsInputSchema>,
|
||||
): Result<Evidence, AnalysisError> => {
|
||||
/** Resolve and authenticate one application source before tracing semantic relations. */
|
||||
export const traceJavaScriptSemanticsEvidence = (
|
||||
rawInput: unknown,
|
||||
lookup?: EvidenceLookup,
|
||||
options: Pick<ExecutionOptions, "signal"> = {},
|
||||
): Result<
|
||||
ApplicationWorkflowExecution<JavaScriptSemanticTraceResult>,
|
||||
AnalysisError
|
||||
> => {
|
||||
const request = resolveEvidenceRequest(
|
||||
OPERATION,
|
||||
rawInput,
|
||||
traceJavaScriptSemanticsRequestSchema,
|
||||
lookup,
|
||||
options,
|
||||
);
|
||||
if (!request.ok) return request;
|
||||
const input = request.value.input;
|
||||
const sourceResult = parseApplicationGraphEvidence(input.application, [
|
||||
"application",
|
||||
]);
|
||||
@@ -36,7 +51,7 @@ export const traceJavaScriptSemanticsEvidenceValidated = (
|
||||
path: ["application"],
|
||||
reason: "invalid_value",
|
||||
expected:
|
||||
"inline Evidence from analyze_javascript_application with a semantic graph",
|
||||
"Evidence from analyze_javascript_application with a semantic graph",
|
||||
},
|
||||
]),
|
||||
);
|
||||
@@ -47,16 +62,15 @@ export const traceJavaScriptSemanticsEvidenceValidated = (
|
||||
evidence_links: [source.evidence.evidence_id],
|
||||
};
|
||||
const { allowed_relations, ...queryParameters } = input.query;
|
||||
return ok(
|
||||
createJavaScriptSemanticTraceEvidence(
|
||||
{
|
||||
application_evidence_id: source.evidence.evidence_id,
|
||||
query:
|
||||
allowed_relations === undefined
|
||||
? queryParameters
|
||||
: { ...queryParameters, allowed_relations },
|
||||
},
|
||||
result,
|
||||
),
|
||||
const evidence = createJavaScriptSemanticTraceEvidence(
|
||||
{
|
||||
application_evidence_id: source.evidence.evidence_id,
|
||||
query:
|
||||
allowed_relations === undefined
|
||||
? queryParameters
|
||||
: { ...queryParameters, allowed_relations },
|
||||
},
|
||||
result,
|
||||
);
|
||||
return ok({ evidence, result, sources: [source.evidence] });
|
||||
};
|
||||
Reference in new issue
Block a user