refactor(javascript): own runtime reconciliation failures

Return expected source and semantic failures from the runtime input owner as tagged Results. Bind source schema validation to concrete schemas, preserve nested active-capture paths, and let unexpected interpretation and derivation failures reach the adapter boundary.

Remove broad protocol-error wrappers, duplicate active identity validation, and redundant JSON result parsing. Migrate parsed-fact consumers and retain CLI/MCP contracts. Relevant source and public-boundary tests pass; six injected implementation-fault controls fail on the prior revision. Live Electron behavior was not exercised.
This commit is contained in:
morluto committed 2026-10-23 09:55:02 +08:00
1 parent ed069bb9d4
commit f4b2ba1313
13 files changed
+358 -171

No files matched your search

@@ -14,6 +14,8 @@ import type { JavaScriptRuntimeReconciliationResult } from "./javascriptRuntimeR
import type { ReconcileJavaScriptRuntimeInput } from "./javascriptRuntimeReconciliationSchemas.js";
import { projectRuntimeCaptures } from "./javascriptRuntimeReconciliationRuntime.js";
import { collectStaticRuntimeCandidates } from "./javascriptRuntimeStaticCandidates.js";
import { AnalysisInputError } from "../analysisErrorCore.js";
import { err, ok, type Result } from "../result.js";
/** Verified caller input and the static and runtime facts parsed from it. */
export interface ParsedRuntimeReconciliationInput {
@@ -23,15 +25,22 @@ export interface ParsedRuntimeReconciliationInput {
}
/** Parse caller Evidence once into the owner consumed by reconciliation. */
export const parseRuntimeReconciliationInput = (
export const readRuntimeReconciliationInput = (
input: ReconcileJavaScriptRuntimeInput,
): ParsedRuntimeReconciliationInput => ({
input,
layers: parseStaticLayers(input.static_layers, ["static_layers"]),
captures: parseRuntimeCaptures(input.runtime_observations, [
"runtime_observations",
]),
});
): Result<ParsedRuntimeReconciliationInput, AnalysisInputError> => {
try {
return ok({
input,
layers: parseStaticLayers(input.static_layers, ["static_layers"]),
captures: parseRuntimeCaptures(input.runtime_observations, [
"runtime_observations",
]),
});
} catch (cause: unknown) {
if (cause instanceof AnalysisInputError) return err(cause);
throw cause;
}
};
/** Reconcile static JAG layers with authorized passive CDP Evidence. */
export const reconcileJavaScriptRuntime = (
@@ -4,8 +4,9 @@ import { digestCanonicalValue } from "../canonicalDigest.js";
import { isPathWithinRoot } from "../localPath.js";
import { z } from "zod";
import {
assertEvidenceIdentity,
invalidInput,
parseAtPath,
parseInputAtPath,
} from "./javascriptRuntimeReconciliationInputValidation.js";
import { classifyBrowserCompleteness } from "../browserCompleteness.js";
@@ -19,24 +20,35 @@ import type { ParsedRuntimeCapture } from "./javascriptRuntimeReconciliationCapt
/** Parse active Electron Evidence as a bounded, target-only runtime capture. */
export const parseActiveElectronCapture = (
evidence: Evidence,
path: readonly (string | number)[],
): ParsedRuntimeCapture => {
assertIdentity(evidence);
const result = parseAtPath(
(value) => electronActiveObservationResultSchema.parse(value),
evidence.normalized_result,
["normalized_result"],
assertEvidenceIdentity(
evidence,
{
operation: "capture_electron_scenario",
predicate: "rea.electron-active-scenario",
providerId: "rea-playwright-electron-active",
providerName: "REA Playwright active Electron observation provider",
providerVersion: "1",
authority: "controlled-replay",
confidence: "observed",
},
path,
);
const parameters = parseAtPath(
(value) =>
z
.object({
application_path: absolutePathSchema,
application_root: absolutePathSchema,
})
.passthrough()
.parse(value),
const result = parseInputAtPath(
electronActiveObservationResultSchema,
evidence.normalized_result,
[...path, "normalized_result"],
);
const parameters = parseInputAtPath(
z
.object({
application_path: absolutePathSchema,
application_root: absolutePathSchema,
})
.passthrough(),
evidence.parameters,
["parameters"],
[...path, "parameters"],
);
if (
result.application.application_path !== parameters.application_path ||
@@ -46,7 +58,7 @@ export const parseActiveElectronCapture = (
)
)
throw invalidInput(
["normalized_result", "application", "application_path"],
[...path, "normalized_result", "application", "application_path"],
"Active Electron Evidence application path disagrees with its configured root",
);
return {
@@ -60,22 +72,6 @@ export const parseActiveElectronCapture = (
const absolutePathSchema = z.string().min(1).refine(isAbsolute);
const assertIdentity = (evidence: Evidence): void => {
if (
evidence.operation !== "capture_electron_scenario" ||
evidence.predicate_type !== "rea.electron-active-scenario" ||
evidence.provider.id !== "rea-playwright-electron-active" ||
evidence.provider.name !==
"REA Playwright active Electron observation provider" ||
evidence.provider.version !== "1" ||
evidence.authority !== "controlled-replay" ||
evidence.confidence !== "observed"
)
throw invalidInput(
[],
"Evidence does not match the supported capture_electron_scenario contract",
);
};
const normalizeInspection = (
result: ElectronActiveObservationResult,
): Extract<
@@ -26,12 +26,13 @@ import {
} from "./javascriptRuntimeObservation.js";
import { digestCanonicalValue } from "../canonicalDigest.js";
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
import { parseEvidence, type Evidence } from "../evidence.js";
import type { Evidence } from "../evidence.js";
import { parseActiveElectronCapture } from "./javascriptRuntimeReconciliationActive.js";
import {
assertEvidenceIdentity,
authenticateRuntimeEvidence,
invalidInput,
parseAtPath,
parseInputAtPath,
} from "./javascriptRuntimeReconciliationInputValidation.js";
interface RuntimeCaptureBase {
@@ -142,7 +143,7 @@ const parseRuntimeCapture = (
input: Evidence,
evidencePath: readonly (string | number)[],
): ParsedRuntimeCapture => {
const evidence = parseAtPath(parseEvidence, input, evidencePath);
const evidence = authenticateRuntimeEvidence(input, evidencePath);
if (evidence.operation === "inspect_web_page")
return parseBrowserCapture(evidence, evidencePath);
if (evidence.operation === "inspect_electron_page")
@@ -150,11 +151,7 @@ const parseRuntimeCapture = (
if (evidence.operation === "observe_javascript_runtime")
return parseV8Capture(evidence, evidencePath);
if (evidence.operation === "capture_electron_scenario") {
return parseAtPath(
() => parseActiveElectronCapture(evidence),
evidence,
evidencePath,
);
return parseActiveElectronCapture(evidence, evidencePath);
}
throw invalidInput(
evidencePath,
@@ -179,8 +176,8 @@ const parseBrowserCapture = (
},
path,
);
const inspection = parseAtPath(
(value) => webPageInspectionSchema.parse(value),
const inspection = parseInputAtPath(
webPageInspectionSchema,
evidence.normalized_result,
[...path, "normalized_result"],
);
@@ -222,8 +219,8 @@ const parseElectronCapture = (
},
path,
);
const inspection = parseAtPath(
(value) => electronPageInspectionSchema.parse(value),
const inspection = parseInputAtPath(
electronPageInspectionSchema,
evidence.normalized_result,
[...path, "normalized_result"],
);
@@ -264,8 +261,8 @@ const parseV8Capture = (
},
path,
);
const result = parseAtPath(
(value) => javascriptRuntimeObservationSchema.parse(value),
const result = parseInputAtPath(
javascriptRuntimeObservationSchema,
evidence.normalized_result,
[...path, "normalized_result"],
);
@@ -284,16 +281,14 @@ const assertV8RuntimeParameters = (
result: JavaScriptRuntimeObservation,
path: readonly (string | number)[],
): void => {
const parameters = parseAtPath(
(value) =>
z
.object({
inspector_endpoint: browserEndpointSchema,
target_id: z.string().trim().min(1),
runtime_kind: javascriptRuntimeKindSchema.optional(),
})
.passthrough()
.parse(value),
const parameters = parseInputAtPath(
z
.object({
inspector_endpoint: browserEndpointSchema,
target_id: z.string().trim().min(1),
runtime_kind: javascriptRuntimeKindSchema.optional(),
})
.passthrough(),
evidence.parameters,
[...path, "parameters"],
);
@@ -450,16 +445,14 @@ const assertRuntimeParameters = (
include_script_sources: z.boolean(),
};
if (expected.kind === "browser") {
const parameters = parseAtPath(
(value) =>
z
.object({
...common,
cdp_endpoint: recordedBrowserObservationEndpointSchema,
allowed_origins: browserAllowedOriginsSchema,
})
.passthrough()
.parse(value),
const parameters = parseInputAtPath(
z
.object({
...common,
cdp_endpoint: recordedBrowserObservationEndpointSchema,
allowed_origins: browserAllowedOriginsSchema,
})
.passthrough(),
evidence.parameters,
[...path, "parameters"],
);
@@ -480,12 +473,8 @@ const assertRuntimeParameters = (
);
return;
}
const parameters = parseAtPath(
(value) =>
z
.object({ ...common, cdp_endpoint: browserEndpointSchema })
.passthrough()
.parse(value),
const parameters = parseInputAtPath(
z.object({ ...common, cdp_endpoint: browserEndpointSchema }).passthrough(),
evidence.parameters,
[...path, "parameters"],
);
@@ -1,33 +1,47 @@
import { z } from "zod";
import type { Evidence } from "../evidence.js";
/** Prefix caller paths on a nested Zod validation boundary. */
export const parseAtPath = <Value>(
parse: (input: unknown) => Value,
import { AnalysisInputError } from "../analysisErrorCore.js";
import { projectInputIssues } from "../inputIssueProjection.js";
import { parseEvidence, type Evidence } from "../evidence.js";
const OPERATION = "reconcile_javascript_runtime";
/** Parse one caller-owned source value and prefix failures with its request path. */
export const parseInputAtPath = <Schema extends z.ZodType>(
schema: Schema,
input: unknown,
path: readonly (string | number)[],
): Value => {
): z.output<Schema> => {
const parsed = schema.safeParse(input);
if (!parsed.success)
throw inputErrorAtPath(parsed.error.issues, input, path, parsed.error);
return parsed.data;
};
/** Authenticate Evidence while preserving the reconciliation input contract. */
export const authenticateRuntimeEvidence = (
input: unknown,
path: readonly (string | number)[],
): Evidence => {
try {
return parse(input);
return parseEvidence(input);
} catch (cause: unknown) {
if (cause instanceof z.ZodError)
throw new z.ZodError(
cause.issues.map((issue) => ({
...issue,
path: [...path, ...issue.path],
})),
);
throw inputErrorAtPath(cause.issues, input, path, cause);
throw cause;
}
};
/** Represent one semantic caller constraint as a standard validation issue. */
/** Represent one cross-field caller constraint as a standard input issue. */
export const invalidInput = (
path: readonly (string | number)[],
message: string,
): z.ZodError => new z.ZodError([{ code: "custom", path: [...path], message }]);
): AnalysisInputError =>
new AnalysisInputError(OPERATION, undefined, [
{ path: [...path], reason: "invalid_value", message },
]);
/** Verify an authenticated Evidence record against one runtime workflow. */
/** Verify authenticated Evidence against one supported producer contract. */
export const assertEvidenceIdentity = (
evidence: Evidence,
expected: {
@@ -55,3 +69,18 @@ export const assertEvidenceIdentity = (
`Evidence does not match the supported ${expected.operation} contract`,
);
};
const inputErrorAtPath = (
issues: readonly z.core.$ZodIssue[],
input: unknown,
path: readonly (string | number)[],
cause: unknown,
): AnalysisInputError =>
new AnalysisInputError(
OPERATION,
{ cause },
projectInputIssues(issues, input).map((issue) => ({
...issue,
path: [...path, ...issue.path],
})),
);
@@ -1,6 +1,6 @@
import { digestCanonicalValue } from "../canonicalDigest.js";
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
import { parseEvidence, type Evidence } from "../evidence.js";
import type { Evidence } from "../evidence.js";
import {
javascriptApplicationAnalysisResultSchema,
type JavaScriptApplicationAnalysisResult,
@@ -9,8 +9,9 @@ import type { JavaScriptApplicationGraph } from "./javascriptApplicationGraph.js
import type { ReconcileJavaScriptRuntimeInput } from "./javascriptRuntimeReconciliationSchemas.js";
import {
assertEvidenceIdentity,
authenticateRuntimeEvidence,
invalidInput,
parseAtPath,
parseInputAtPath,
} from "./javascriptRuntimeReconciliationInputValidation.js";
type StaticLayerInput =
@@ -43,9 +44,9 @@ const parseStaticLayer = (
layer: StaticLayerInput,
evidencePath: readonly (string | number)[],
): ParsedStaticLayer => {
const evidence = parseAtPath(parseEvidence, layer.analysis, evidencePath);
const result = parseAtPath(
(value) => javascriptApplicationAnalysisResultSchema.parse(value),
const evidence = authenticateRuntimeEvidence(layer.analysis, evidencePath);
const result = parseInputAtPath(
javascriptApplicationAnalysisResultSchema,
evidence.normalized_result,
[...evidencePath, "normalized_result"],
);