mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
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:
1 parent
ed069bb9d4
commit
f4b2ba1313
13 files changed
+358
-171
No files matched your search
@@ -7,7 +7,6 @@ import type {
|
||||
JavaScriptRuntimeReconciliationResult,
|
||||
ReconcileJavaScriptRuntimeInput,
|
||||
} from "../../domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
import { JAVASCRIPT_RUNTIME_RECONCILIATION_PROVIDER } from "../InvestigationProviders.js";
|
||||
|
||||
/** Create derived Evidence for one exact set of static/runtime inputs. */
|
||||
@@ -19,7 +18,7 @@ export const createJavaScriptRuntimeReconciliationEvidence = (
|
||||
predicateType: "rea.javascript-runtime-reconciliation",
|
||||
operation: "reconcile_javascript_runtime",
|
||||
parameters: parameters(input),
|
||||
result: jsonValueSchema.parse(result),
|
||||
result,
|
||||
rawResult: null,
|
||||
confidence: "inferred",
|
||||
authority: "analyst-inference",
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { resolve } from "node:path";
|
||||
import { z } from "zod";
|
||||
|
||||
import { AnalysisProtocolError } from "../../domain/analysisErrorCore.js";
|
||||
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import type { Evidence } from "../../domain/evidence.js";
|
||||
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
||||
import {
|
||||
parseRuntimeReconciliationInput,
|
||||
readRuntimeReconciliationInput,
|
||||
reconcileJavaScriptRuntime,
|
||||
type ParsedRuntimeReconciliationInput,
|
||||
} from "../../domain/javascript/javascriptRuntimeReconciliation.js";
|
||||
import { reconcileJavaScriptRuntimeInputSchema } from "../../domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { err, ok, type Result } from "../../domain/result.js";
|
||||
@@ -32,37 +30,14 @@ export const reconcileJavaScriptRuntimeEvidence = (
|
||||
export const reconcileJavaScriptRuntimeEvidenceValidated = (
|
||||
parsedInput: z.output<typeof reconcileJavaScriptRuntimeInputSchema>,
|
||||
): Result<Evidence, AnalysisError> => {
|
||||
let parsed: ParsedRuntimeReconciliationInput;
|
||||
try {
|
||||
parsed = parseRuntimeReconciliationInput(
|
||||
normalizeRuntimeMappingRoots(parsedInput),
|
||||
);
|
||||
} catch (cause: unknown) {
|
||||
if (cause instanceof z.ZodError)
|
||||
return err(
|
||||
analysisInputErrorFromIssues(OPERATION, cause.issues, parsedInput),
|
||||
);
|
||||
return err(
|
||||
new AnalysisProtocolError(
|
||||
"JavaScript runtime reconciliation input parsing failed",
|
||||
{ cause },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = reconcileJavaScriptRuntime(parsed);
|
||||
return ok(
|
||||
createJavaScriptRuntimeReconciliationEvidence(parsed.input, result),
|
||||
);
|
||||
} catch (cause: unknown) {
|
||||
return err(
|
||||
new AnalysisProtocolError(
|
||||
"JavaScript runtime reconciliation produced an invalid result",
|
||||
{ cause },
|
||||
),
|
||||
);
|
||||
}
|
||||
const parsed = readRuntimeReconciliationInput(
|
||||
normalizeRuntimeMappingRoots(parsedInput),
|
||||
);
|
||||
if (!parsed.ok) return parsed;
|
||||
const result = reconcileJavaScriptRuntime(parsed.value);
|
||||
return ok(
|
||||
createJavaScriptRuntimeReconciliationEvidence(parsed.value.input, result),
|
||||
);
|
||||
};
|
||||
|
||||
const normalizeRuntimeMappingRoots = (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createEvidence } from "../../domain/evidence.js";
|
||||
import {
|
||||
parseRuntimeReconciliationInput,
|
||||
readRuntimeReconciliationInput,
|
||||
reconcileJavaScriptRuntime,
|
||||
} from "../../domain/javascript/javascriptRuntimeReconciliation.js";
|
||||
import {
|
||||
@@ -10,13 +10,17 @@ import {
|
||||
import { createHistoricalSourceGraph } from "../../domain/referenceSourceGraph.js";
|
||||
import { reconcileJavaScriptRuntimeInputSchema } from "../../domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
|
||||
const reconciliation = reconcileJavaScriptRuntime(
|
||||
parseRuntimeReconciliationInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse(
|
||||
JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE,
|
||||
),
|
||||
const reconciliationInput = readRuntimeReconciliationInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse(
|
||||
JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE,
|
||||
),
|
||||
);
|
||||
if (!reconciliationInput.ok)
|
||||
throw new Error(
|
||||
"The JavaScript runtime reconciliation contract example is invalid",
|
||||
{ cause: reconciliationInput.error },
|
||||
);
|
||||
const reconciliation = reconcileJavaScriptRuntime(reconciliationInput.value);
|
||||
const reconciliationEvidence = createEvidence(
|
||||
undefined,
|
||||
{
|
||||
|
||||
@@ -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"],
|
||||
);
|
||||
|
||||
+14
-6
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
parseRuntimeReconciliationInput,
|
||||
readRuntimeReconciliationInput,
|
||||
reconcileJavaScriptRuntime,
|
||||
} from "../../../../src/domain/javascript/javascriptRuntimeReconciliation.js";
|
||||
import { reconcileRuntimeEntities } from "../../../../src/domain/javascript/javascriptRuntimeReconciliationMatching.js";
|
||||
@@ -22,6 +22,14 @@ import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js
|
||||
|
||||
const CAPTURED = "export const observed = 'different';\n";
|
||||
|
||||
const readInput = (
|
||||
input: Parameters<typeof readRuntimeReconciliationInput>[0],
|
||||
) => {
|
||||
const result = readRuntimeReconciliationInput(input);
|
||||
if (!result.ok) throw result.error;
|
||||
return result.value;
|
||||
};
|
||||
|
||||
const collisionInput = async (
|
||||
alternatives: number,
|
||||
explicitMapping: boolean,
|
||||
@@ -33,7 +41,7 @@ const collisionInput = async (
|
||||
: root;
|
||||
const extra = await createTestTempDirectory("rea-runtime-content-extra-");
|
||||
await writeFile(join(extra, "app.js"), CAPTURED);
|
||||
return parseRuntimeReconciliationInput(
|
||||
return readInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse({
|
||||
static_layers: [
|
||||
{
|
||||
@@ -106,7 +114,7 @@ it("restricts content-and-location ambiguity to mapped candidates", async () =>
|
||||
const live = await createTestTempDirectory("rea-runtime-content-mapped-");
|
||||
const analysis = await analyzeFixture(application);
|
||||
const mapping = [{ kind: "file-root", root: live, artifact_prefix: "" }];
|
||||
const parsed = parseRuntimeReconciliationInput(
|
||||
const parsed = readInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse({
|
||||
static_layers: [
|
||||
{ role: "application", analysis, runtime_mappings: mapping },
|
||||
@@ -148,7 +156,7 @@ it.each([false, true])(
|
||||
"rea-runtime-content-unlocated-",
|
||||
);
|
||||
const result = reconcileJavaScriptRuntime(
|
||||
parseRuntimeReconciliationInput(
|
||||
readInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse({
|
||||
static_layers: [
|
||||
{
|
||||
@@ -245,7 +253,7 @@ const reconcileCapturedScript = async (input: {
|
||||
readonly includeWorker?: boolean;
|
||||
}) => {
|
||||
const result = reconcileJavaScriptRuntime(
|
||||
parseRuntimeReconciliationInput(
|
||||
readInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse({
|
||||
static_layers: await Promise.all(
|
||||
input.layers.map(async ({ root, mapping }, index) => ({
|
||||
@@ -385,7 +393,7 @@ it.each([
|
||||
"applies URL-prefix mapping presence to captured script %s",
|
||||
async (url, status) => {
|
||||
const root = await applicationFixture();
|
||||
const parsed = parseRuntimeReconciliationInput(
|
||||
const parsed = readInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse({
|
||||
static_layers: [
|
||||
{
|
||||
|
||||
+11
-7
@@ -13,7 +13,7 @@ import { mappedRuntimePaths } from "../../../../src/domain/javascript/javascript
|
||||
import { projectRuntimeCaptures } from "../../../../src/domain/javascript/javascriptRuntimeReconciliationRuntime.js";
|
||||
import { electronPageInspectionSchema } from "../../../../src/domain/javascript/electronObservation.js";
|
||||
import {
|
||||
parseRuntimeReconciliationInput,
|
||||
readRuntimeReconciliationInput,
|
||||
reconcileJavaScriptRuntime,
|
||||
} from "../../../../src/domain/javascript/javascriptRuntimeReconciliation.js";
|
||||
import { reconcileJavaScriptRuntimeInputSchema } from "../../../../src/domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
@@ -28,12 +28,16 @@ import {
|
||||
electronRuntimeEvidence,
|
||||
} from "../../../fixtures/javascriptRuntimeReconciliation.js";
|
||||
|
||||
const reconcileInput = (input: unknown) =>
|
||||
reconcileJavaScriptRuntime(
|
||||
parseRuntimeReconciliationInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse(input),
|
||||
),
|
||||
const readInput = (input: unknown) => {
|
||||
const result = readRuntimeReconciliationInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse(input),
|
||||
);
|
||||
if (!result.ok) throw result.error;
|
||||
return result.value;
|
||||
};
|
||||
|
||||
const reconcileInput = (input: unknown) =>
|
||||
reconcileJavaScriptRuntime(readInput(input));
|
||||
|
||||
it("preserves source-capture input paths for runtime Evidence", async () => {
|
||||
const root = await applicationFixture();
|
||||
@@ -504,7 +508,7 @@ it.skipIf(process.platform === "win32")(
|
||||
"export const marker = 42;\n",
|
||||
);
|
||||
const analysis = await analyzeFixture(root);
|
||||
const parsed = parseRuntimeReconciliationInput(
|
||||
const parsed = readInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse({
|
||||
static_layers: [
|
||||
{
|
||||
|
||||
@@ -8,19 +8,20 @@ import { analyzeJavaScriptApplication } from "../../support/javascriptApplicatio
|
||||
import { createJavaScriptRuntimeObservationEvidence } from "../../../src/application/javascript/JavaScriptRuntimeObservationEvidence.js";
|
||||
import { V8InspectorProvider } from "../../../src/inspector/V8InspectorProvider.js";
|
||||
import {
|
||||
parseRuntimeReconciliationInput,
|
||||
readRuntimeReconciliationInput,
|
||||
reconcileJavaScriptRuntime,
|
||||
} from "../../../src/domain/javascript/javascriptRuntimeReconciliation.js";
|
||||
import { reconcileJavaScriptRuntimeInputSchema } from "../../../src/domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { startFakeV8Inspector } from "../../fixtures/inspector/fakeV8Inspector.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
const reconcileInput = (input: unknown) =>
|
||||
reconcileJavaScriptRuntime(
|
||||
parseRuntimeReconciliationInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse(input),
|
||||
),
|
||||
const reconcileInput = (input: unknown) => {
|
||||
const result = readRuntimeReconciliationInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse(input),
|
||||
);
|
||||
if (!result.ok) throw result.error;
|
||||
return reconcileJavaScriptRuntime(result.value);
|
||||
};
|
||||
|
||||
test("keeps unresolved target identity unknown while matching verified loaded scripts", async () => {
|
||||
const root = await createTestTempDirectory("rea-unresolved-runtime-");
|
||||
|
||||
@@ -8,7 +8,7 @@ import { afterEach, expect, it } from "vitest";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
import {
|
||||
parseRuntimeReconciliationInput,
|
||||
readRuntimeReconciliationInput,
|
||||
reconcileJavaScriptRuntime,
|
||||
} from "../../../src/domain/javascript/javascriptRuntimeReconciliation.js";
|
||||
import { reconcileJavaScriptRuntimeInputSchema } from "../../../src/domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
@@ -20,12 +20,13 @@ import {
|
||||
electronRuntimeEvidence,
|
||||
} from "../../fixtures/javascriptRuntimeReconciliation.js";
|
||||
|
||||
const reconcileInput = (input: unknown) =>
|
||||
reconcileJavaScriptRuntime(
|
||||
parseRuntimeReconciliationInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse(input),
|
||||
),
|
||||
const reconcileInput = (input: unknown) => {
|
||||
const result = readRuntimeReconciliationInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse(input),
|
||||
);
|
||||
if (!result.ok) throw result.error;
|
||||
return reconcileJavaScriptRuntime(result.value);
|
||||
};
|
||||
|
||||
const execute = promisify(execFile);
|
||||
|
||||
@@ -133,12 +134,32 @@ it("rejects runtime source bytes whose Evidence omits source-capture approval",
|
||||
sourceIncluded: false,
|
||||
});
|
||||
|
||||
expect(() =>
|
||||
reconcileInput({
|
||||
const result = readRuntimeReconciliationInput(
|
||||
reconcileJavaScriptRuntimeInputSchema.parse({
|
||||
static_layers: [{ role: "application", analysis: staticEvidence }],
|
||||
runtime_observations: [contradictoryRuntime],
|
||||
}),
|
||||
).toThrow(/source-capture selection/u);
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: {
|
||||
_tag: "AnalysisInputError",
|
||||
operation: "reconcile_javascript_runtime",
|
||||
issues: [
|
||||
{
|
||||
path: [
|
||||
"runtime_observations",
|
||||
0,
|
||||
"parameters",
|
||||
"include_script_sources",
|
||||
],
|
||||
reason: "invalid_value",
|
||||
message:
|
||||
"Runtime Evidence contains source without source-capture selection",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps graph omission counts unknown when a runtime section is unavailable", async () => {
|
||||
|
||||
@@ -49,6 +49,12 @@ import { applicationFeatureTraceResultSchema } from "../../src/domain/javascript
|
||||
import { ownedApplicationVersionComparisonResultSchema } from "../../src/domain/javascript/javascriptApplicationVersionComparisonSchemas.js";
|
||||
import { javaScriptExportShapeComparisonResultSchema } from "../../src/domain/javascript/javascriptExportShapeComparisonSchemas.js";
|
||||
import { sourceToBundleComparisonResultSchema } from "../../src/domain/javascript/sourceToBundleComparisonSchemas.js";
|
||||
import { JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE } from "../../src/contracts/javascript/javascriptRuntimeReconciliationExample.js";
|
||||
import { javascriptRuntimeReconciliationResultSchema } from "../../src/domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { browserCompletenessSchema } from "../../src/domain/browserCompleteness.js";
|
||||
import { createElectronActiveEvidence } from "../../src/application/javascript/ElectronActiveEvidence.js";
|
||||
import { electronActiveObservationInputSchema } from "../../src/domain/javascript/electronActiveObservation.js";
|
||||
import { createElectronActiveObservationFixtureResult } from "../../src/domain/javascript/electronActiveObservation.fixture.js";
|
||||
|
||||
import { nativeFunctionMatchingSchema } from "../../src/domain/native/functionMatching.js";
|
||||
import { FunctionByteCoverage } from "../../src/domain/native/functionByteCoverage.js";
|
||||
@@ -207,6 +213,151 @@ const withSdk = async <Value>(
|
||||
}
|
||||
};
|
||||
|
||||
const activeRuntimeFixture = () => {
|
||||
const root = join(process.cwd(), "active-runtime-fixture");
|
||||
const applicationPath = join(root, "main.js");
|
||||
const result = createElectronActiveObservationFixtureResult(applicationPath);
|
||||
const evidence = createElectronActiveEvidence(
|
||||
{
|
||||
...electronActiveObservationInputSchema.parse({
|
||||
executable_path: process.execPath,
|
||||
application_path: applicationPath,
|
||||
application_root: root,
|
||||
}),
|
||||
application_root: root,
|
||||
},
|
||||
result,
|
||||
{
|
||||
id: "rea-playwright-electron-active",
|
||||
name: "REA Playwright active Electron observation provider",
|
||||
version: "1",
|
||||
},
|
||||
);
|
||||
return { evidence, result };
|
||||
};
|
||||
|
||||
describe("runtime reconciliation failure ownership through the public SDK", () => {
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
it.each(unexpectedResultFailures)(
|
||||
"preserves an unexpected %s from generated reconciliation output",
|
||||
async (_label, createFailure) => {
|
||||
await withSdk(async ({ client, session }) => {
|
||||
const failure = createFailure();
|
||||
vi.spyOn(
|
||||
javascriptRuntimeReconciliationResultSchema,
|
||||
"parse",
|
||||
).mockImplementation(() => {
|
||||
throw failure;
|
||||
});
|
||||
const response = await client.callTool({
|
||||
name: "reconcile_javascript_runtime",
|
||||
arguments: JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE,
|
||||
});
|
||||
expectUnexpectedToolFailure(response, failure);
|
||||
expect(session.exportEvidenceBundle()).toMatchObject({
|
||||
records: [],
|
||||
unknowns: [],
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it.each(unexpectedResultFailures)(
|
||||
"preserves an unexpected %s while interpreting an authenticated active capture",
|
||||
async (_label, createFailure) => {
|
||||
await withSdk(async ({ client, session }) => {
|
||||
const { evidence: runtime } = activeRuntimeFixture();
|
||||
const failure = createFailure();
|
||||
vi.spyOn(browserCompletenessSchema, "parse").mockImplementation(() => {
|
||||
throw failure;
|
||||
});
|
||||
const response = await client.callTool({
|
||||
name: "reconcile_javascript_runtime",
|
||||
arguments: {
|
||||
static_layers:
|
||||
JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE.static_layers,
|
||||
runtime_observations: [runtime],
|
||||
},
|
||||
});
|
||||
expectUnexpectedToolFailure(response, failure);
|
||||
expect(session.exportEvidenceBundle()).toMatchObject({
|
||||
records: [],
|
||||
unknowns: [],
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
{
|
||||
field: "payload",
|
||||
path: ["normalized_result", "application", "application_path"],
|
||||
reason: "invalid_type",
|
||||
},
|
||||
{
|
||||
field: "parameters",
|
||||
path: ["parameters", "application_root"],
|
||||
reason: "invalid_type",
|
||||
},
|
||||
{
|
||||
field: "scope",
|
||||
path: ["normalized_result", "application", "application_path"],
|
||||
reason: "invalid_value",
|
||||
},
|
||||
])(
|
||||
"keeps an invalid active $field at its caller path",
|
||||
async ({ field, path, reason }) => {
|
||||
await withSdk(async ({ client, session }) => {
|
||||
const { evidence, result } = activeRuntimeFixture();
|
||||
const runtime = createEvidence(undefined, evidence.provider, {
|
||||
operation: evidence.operation,
|
||||
predicateType: evidence.predicate_type,
|
||||
authority: evidence.authority,
|
||||
confidence: evidence.confidence,
|
||||
parameters:
|
||||
field === "parameters"
|
||||
? { ...evidence.parameters, application_root: 3 }
|
||||
: evidence.parameters,
|
||||
result:
|
||||
field === "parameters"
|
||||
? result
|
||||
: {
|
||||
...result,
|
||||
application: {
|
||||
...result.application,
|
||||
application_path:
|
||||
field === "payload"
|
||||
? 3
|
||||
: join(process.cwd(), "another-application", "main.js"),
|
||||
},
|
||||
},
|
||||
});
|
||||
const response = await client.callTool({
|
||||
name: "reconcile_javascript_runtime",
|
||||
arguments: {
|
||||
static_layers:
|
||||
JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE.static_layers,
|
||||
runtime_observations: [runtime],
|
||||
},
|
||||
});
|
||||
expect(response.isError).toBe(true);
|
||||
expect(parseMcpToolError(response).error).toMatchObject({
|
||||
code: "invalid_request",
|
||||
details: {
|
||||
operation: "reconcile_javascript_runtime",
|
||||
issues: [{ path: ["runtime_observations", 0, ...path], reason }],
|
||||
},
|
||||
});
|
||||
expect(session.exportEvidenceBundle()).toMatchObject({
|
||||
records: [],
|
||||
unknowns: [],
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("derived operation failures through the public SDK", () => {
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
|
||||
Reference in new issue
Block a user