fix(process): centralize capture evidence admission
This commit is contained in:
1 parent
71a024c789
commit
9eb829aca3
11 files changed
+240
-100
No files matched your search
@@ -17,7 +17,7 @@ import {
|
||||
AnalysisCancelledError,
|
||||
type AnalysisResourceConstraintError,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { parseProcessCapture } from "../domain/process/processCaptureParsing.js";
|
||||
import { safeParseProcessCapture } from "../domain/process/processCaptureParsing.js";
|
||||
import {
|
||||
bufferedJsonParts,
|
||||
canonicalJsonParts,
|
||||
@@ -103,13 +103,12 @@ const readBundle = async (
|
||||
}
|
||||
for (const record of bundle.records) {
|
||||
if (record.predicate_type !== "rea.process-capture") continue;
|
||||
try {
|
||||
parseProcessCapture(record.normalized_result);
|
||||
} catch (cause: unknown) {
|
||||
const parsedCapture = safeParseProcessCapture(record.normalized_result);
|
||||
if (!parsedCapture.success) {
|
||||
return err(
|
||||
new EvidenceIntegrityError("Evidence bundle validation failed", {
|
||||
cause,
|
||||
userMessage: `Process capture record ${record.evidence_id} has an invalid normalized_result (${describeValidationFailure(cause)}). Recreate or re-export the bundle, then try again.`,
|
||||
cause: parsedCapture.error,
|
||||
userMessage: `Process capture record ${record.evidence_id} has an invalid normalized_result (${describeValidationFailure(parsedCapture.error)}). Recreate or re-export the bundle, then try again.`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,16 +3,12 @@ import { digestCanonicalValue } from "../domain/canonicalDigest.js";
|
||||
|
||||
import type { Evidence } from "../domain/evidence.js";
|
||||
import { describeValidationFailure } from "../domain/validationFailure.js";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
parseProcessCapture,
|
||||
type ProcessCapture,
|
||||
} from "../domain/process/processCaptureParsing.js";
|
||||
import type {
|
||||
ReconstructionObligation,
|
||||
ReviewedReconstructionObligation,
|
||||
} from "../domain/reconstructionObligationLedgerSchemas.js";
|
||||
import { parseApplicationGraphEvidence } from "./javascript/JavaScriptApplicationEvidenceGraph.js";
|
||||
import { parseProcessCaptureEvidence } from "./process/ProcessEvidence.js";
|
||||
import {
|
||||
applicationObligationPolicy,
|
||||
semanticObligationPolicy,
|
||||
@@ -181,25 +177,29 @@ const deriveApplicationCandidates = (
|
||||
};
|
||||
|
||||
const deriveProcessCandidate = (
|
||||
evidence: Evidence,
|
||||
input: Evidence,
|
||||
candidates: Map<string, ReconstructionObligationCandidate>,
|
||||
limitations: Set<string>,
|
||||
): void => {
|
||||
if (
|
||||
evidence.operation !== "capture_process_scenario" ||
|
||||
evidence.predicate_type !== "rea.process-capture"
|
||||
)
|
||||
return;
|
||||
let capture: ProcessCapture;
|
||||
try {
|
||||
capture = parseProcessCapture(evidence.normalized_result);
|
||||
} catch (cause: unknown) {
|
||||
if (!(cause instanceof z.ZodError)) throw cause;
|
||||
limitations.add(
|
||||
`Process Evidence ${evidence.evidence_id} could not generate candidates: ${describeValidationFailure(cause)}`,
|
||||
);
|
||||
const parsed = parseProcessCaptureEvidence(input);
|
||||
if (!parsed.ok) {
|
||||
switch (parsed.error.kind) {
|
||||
case "wrong-workflow":
|
||||
break;
|
||||
case "unsupported-provider":
|
||||
limitations.add(
|
||||
`Process Evidence ${input.evidence_id} has an unsupported provider; process lifecycle candidates remain unknown.`,
|
||||
);
|
||||
break;
|
||||
case "invalid-evidence":
|
||||
case "invalid-capture":
|
||||
limitations.add(
|
||||
`Process Evidence ${input.evidence_id} could not generate candidates: ${describeValidationFailure(parsed.error.cause)}`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const { record: evidence, capture } = parsed.value;
|
||||
const observedCases: ReconstructionObligation["observed_cases"] = [
|
||||
{
|
||||
kind: capture.exit.code === 0 ? "positive" : "negative",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import { analysisErrorWithCleanupFailure } from "../../domain/analysisErrorCleanup.js";
|
||||
import {
|
||||
@@ -173,13 +171,11 @@ const parseCaptureEvidence = (input: unknown, side: "left" | "right") => {
|
||||
const failure = parsed.error;
|
||||
switch (failure.kind) {
|
||||
case "invalid-evidence":
|
||||
if (failure.cause instanceof z.ZodError)
|
||||
throw new AnalysisInputError(
|
||||
"compare_process_captures",
|
||||
{ cause: failure.cause },
|
||||
projectInputIssues(failure.cause.issues, input),
|
||||
);
|
||||
throw invalidCaptureEvidence(failure.cause, side);
|
||||
throw new AnalysisInputError(
|
||||
"compare_process_captures",
|
||||
{ cause: failure.cause },
|
||||
projectInputIssues(failure.cause.issues, input),
|
||||
);
|
||||
case "wrong-workflow":
|
||||
throw processInputFailure(
|
||||
"compare_process_captures",
|
||||
@@ -273,7 +269,7 @@ export const PROCESS_UNEXPECTED_FAILURE_REMEDIATION =
|
||||
"Do not repeat this command. Tell the user the process command could not complete and ask them to check the input files, then run `rea doctor`.";
|
||||
|
||||
/** Preserve typed failures and local diagnostics at the process CLI boundary. */
|
||||
export const projectProcessCliError = (
|
||||
const projectProcessCliError = (
|
||||
cause: unknown,
|
||||
operation: "capture_process_scenario" | "compare_process_captures",
|
||||
): ProcessCliErrorOutput => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { z } from "zod";
|
||||
import { posix, win32 } from "node:path";
|
||||
|
||||
import type { JsonValue } from "../../domain/jsonValue.js";
|
||||
@@ -13,7 +14,7 @@ import {
|
||||
} from "../../domain/process/processEvidenceProvider.js";
|
||||
import { jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
import {
|
||||
parseProcessCapture,
|
||||
safeParseProcessCapture,
|
||||
type ProcessCapture,
|
||||
} from "../../domain/process/processCaptureParsing.js";
|
||||
import type { ProcessScenario } from "../../domain/process/processScenario.js";
|
||||
@@ -26,7 +27,7 @@ const PROCESS_CAPTURE_EVIDENCE = {
|
||||
|
||||
/** Semantic rejection facts; CLI and MCP retain their own error projections. */
|
||||
export type ProcessCaptureEvidenceFailure =
|
||||
| { readonly kind: "invalid-evidence"; readonly cause: unknown }
|
||||
| { readonly kind: "invalid-evidence"; readonly cause: z.ZodError }
|
||||
| {
|
||||
readonly kind: "wrong-workflow";
|
||||
readonly expected: typeof PROCESS_CAPTURE_EVIDENCE;
|
||||
@@ -39,7 +40,7 @@ export type ProcessCaptureEvidenceFailure =
|
||||
readonly kind: "unsupported-provider";
|
||||
readonly provider: EvidenceProvider;
|
||||
}
|
||||
| { readonly kind: "invalid-capture"; readonly cause: unknown };
|
||||
| { readonly kind: "invalid-capture"; readonly cause: z.ZodError };
|
||||
|
||||
/** Authenticate capture Evidence before admitting its workflow, provider and payload. */
|
||||
export const parseProcessCaptureEvidence = (
|
||||
@@ -52,7 +53,9 @@ export const parseProcessCaptureEvidence = (
|
||||
try {
|
||||
record = parseEvidence(input);
|
||||
} catch (cause: unknown) {
|
||||
return err({ kind: "invalid-evidence", cause });
|
||||
if (cause instanceof z.ZodError)
|
||||
return err({ kind: "invalid-evidence", cause });
|
||||
throw cause;
|
||||
}
|
||||
if (
|
||||
record.operation !== PROCESS_CAPTURE_EVIDENCE.operation ||
|
||||
@@ -65,14 +68,10 @@ export const parseProcessCaptureEvidence = (
|
||||
});
|
||||
if (!isProcessEvidenceProvider(record.provider))
|
||||
return err({ kind: "unsupported-provider", provider: record.provider });
|
||||
try {
|
||||
return ok({
|
||||
record,
|
||||
capture: parseProcessCapture(record.normalized_result),
|
||||
});
|
||||
} catch (cause: unknown) {
|
||||
return err({ kind: "invalid-capture", cause });
|
||||
}
|
||||
const parsedCapture = safeParseProcessCapture(record.normalized_result);
|
||||
return parsedCapture.success
|
||||
? ok({ record, capture: parsedCapture.data })
|
||||
: err({ kind: "invalid-capture", cause: parsedCapture.error });
|
||||
};
|
||||
|
||||
/** Project one process scenario into secret-free Evidence parameters. */
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { expect, it } from "vitest";
|
||||
import { expect, expectTypeOf, it } from "vitest";
|
||||
import { z } from "zod";
|
||||
|
||||
import { digestProcessCommitment } from "./processScenario.js";
|
||||
import { parseProcessCapture } from "./processCaptureParsing.js";
|
||||
import {
|
||||
parseProcessCapture,
|
||||
type ProcessCapture,
|
||||
} from "./processCaptureParsing.js";
|
||||
import type { UnverifiedProcessCapture } from "./processCapture.js";
|
||||
import { partialProcessCaptureObservationSchema } from "./processCapture.js";
|
||||
import { FINALIZED_PROCESS_CAPTURE_EXAMPLE } from "./processCaptureExample.js";
|
||||
import { finalizationConsistencyIssue } from "./processCaptureValidation.js";
|
||||
@@ -13,6 +17,8 @@ import {
|
||||
processCaptureIssues,
|
||||
} from "./processCapture.fixture.js";
|
||||
|
||||
expectTypeOf<UnverifiedProcessCapture>().not.toExtend<ProcessCapture>();
|
||||
|
||||
/** Name every rejected field reported by the production parser. */
|
||||
const rejectedPaths = (input: unknown): readonly string[] => {
|
||||
try {
|
||||
|
||||
@@ -1,30 +1,21 @@
|
||||
import {
|
||||
processCaptureSchema,
|
||||
type UnverifiedProcessCapture,
|
||||
} from "./processCapture.js";
|
||||
import { z } from "zod";
|
||||
|
||||
declare class ProcessCaptureProof {
|
||||
private readonly verified: never;
|
||||
}
|
||||
import { processCaptureSchema } from "./processCapture.js";
|
||||
|
||||
declare const processCaptureProof: unique symbol;
|
||||
const verifiedProcessCaptureSchema =
|
||||
processCaptureSchema.brand<typeof processCaptureProof>();
|
||||
|
||||
/** Semantically verified process capture value. */
|
||||
export type ProcessCapture = UnverifiedProcessCapture & ProcessCaptureProof;
|
||||
export type ProcessCapture = z.output<typeof verifiedProcessCaptureSchema>;
|
||||
|
||||
const verifiedCaptures = new WeakSet<UnverifiedProcessCapture>();
|
||||
|
||||
const isVerifiedCapture = (
|
||||
capture: UnverifiedProcessCapture,
|
||||
): capture is ProcessCapture => verifiedCaptures.has(capture);
|
||||
|
||||
const attachProof = (capture: UnverifiedProcessCapture): ProcessCapture => {
|
||||
verifiedCaptures.add(capture);
|
||||
if (isVerifiedCapture(capture)) return capture;
|
||||
throw new TypeError("Process Capture proof ownership failed");
|
||||
};
|
||||
/** Parse a boundary value while retaining Zod's expected validation failure. */
|
||||
export const safeParseProcessCapture = (input: unknown) =>
|
||||
verifiedProcessCaptureSchema.safeParse(input);
|
||||
|
||||
/**
|
||||
* Parse unknown input and reject invalid commitments or semantics. Failures
|
||||
* throw the schema's ZodError so structural and semantic issues stay together.
|
||||
*/
|
||||
export const parseProcessCapture = (input: unknown): ProcessCapture =>
|
||||
attachProof(processCaptureSchema.parse(input));
|
||||
verifiedProcessCaptureSchema.parse(input);
|
||||
@@ -216,8 +216,7 @@ const isProcessCaptureEvidence = (evidence: Evidence): boolean => {
|
||||
return false;
|
||||
case "invalid-evidence":
|
||||
case "invalid-capture":
|
||||
if (parsed.error.cause instanceof z.ZodError) return false;
|
||||
throw parsed.error.cause;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { createTestBinarySession } from "../../fixtures/binarySession.js";
|
||||
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
|
||||
import { createEvidenceBundle } from "../../../src/domain/evidenceBundle.js";
|
||||
import { createEvidence } from "../../../src/domain/evidence.js";
|
||||
import { PROCESS_PROVIDER } from "../../../src/domain/process/processEvidenceProvider.js";
|
||||
import {
|
||||
createResidualUnknown,
|
||||
recordUnknownInputSchema,
|
||||
@@ -15,6 +16,7 @@ import { createServer } from "../../../src/server/createServer.js";
|
||||
import {
|
||||
MIXED_AUTHORITY_SUMMARY,
|
||||
mixedAuthorityRequest,
|
||||
processEvidence,
|
||||
reviewedObligation,
|
||||
} from "../../../src/application/ReconstructionObligationLedger.fixture.js";
|
||||
import { observed } from "../../fixtures/analysisExecution.js";
|
||||
@@ -169,6 +171,66 @@ describe("reconstruction obligation ledger residual graph input", () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe("reconstruction obligation ledger process provider admission", () => {
|
||||
it.each([
|
||||
["current provider", PROCESS_PROVIDER, true],
|
||||
[
|
||||
"supported legacy name",
|
||||
{ ...PROCESS_PROVIDER, name: "REA deterministic process harness" },
|
||||
true,
|
||||
],
|
||||
["foreign id", { ...PROCESS_PROVIDER, id: "foreign-process" }, false],
|
||||
["unsupported version", { ...PROCESS_PROVIDER, version: "4" }, false],
|
||||
[
|
||||
"unsupported name",
|
||||
{ ...PROCESS_PROVIDER, name: "Other provider" },
|
||||
false,
|
||||
],
|
||||
] as const)(
|
||||
"preserves process provider admission for %s",
|
||||
async (_, provider, admitted) => {
|
||||
const source = processEvidence();
|
||||
const record = createEvidence(undefined, provider, {
|
||||
predicateType: source.predicate_type,
|
||||
operation: source.operation,
|
||||
parameters: source.parameters,
|
||||
result: source.normalized_result,
|
||||
confidence: source.confidence,
|
||||
authority: source.authority,
|
||||
});
|
||||
await withLedgerClient(async (request) => {
|
||||
const response = await request(createEvidenceBundle([record]));
|
||||
expect(response.isError).not.toBe(true);
|
||||
expect(response.structuredContent).toMatchObject({
|
||||
normalized_result: {
|
||||
status: admitted ? "open" : "unknown",
|
||||
summary: { total: admitted ? 1 : 0 },
|
||||
obligations: admitted
|
||||
? [
|
||||
{
|
||||
family: "packaged-process-lifecycle",
|
||||
target: { artifact_sha256: "0".repeat(64) },
|
||||
authority_references: [
|
||||
{
|
||||
evidence_id: record.evidence_id,
|
||||
authority: record.authority,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [],
|
||||
limitations: admitted
|
||||
? expect.any(Array)
|
||||
: expect.arrayContaining([
|
||||
`Process Evidence ${record.evidence_id} has an unsupported provider; process lifecycle candidates remain unknown.`,
|
||||
]),
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("reconstruction obligation ledger MCP parity", () => {
|
||||
it("rejects noncanonical source bundles before ledger generation", async () => {
|
||||
const records = ["first", "second"].map((label) =>
|
||||
|
||||
@@ -13,10 +13,8 @@ import { cliTest } from "../../support/cli/cliFixture.js";
|
||||
import {
|
||||
captureProcessScenarioFile,
|
||||
compareProcessEvidenceFiles,
|
||||
projectProcessCliError,
|
||||
} from "../../../src/application/process/ProcessCli.js";
|
||||
import { createEvidence } from "../../../src/domain/evidence.js";
|
||||
import { ProcessCaptureError } from "../../../src/process/capture/ProcessCaptureError.js";
|
||||
import { PROCESS_PROVIDER } from "../../../src/domain/process/processEvidenceProvider.js";
|
||||
import { INVESTIGATION_EXAMPLES } from "../../../src/contracts/investigationExamples.js";
|
||||
import {
|
||||
@@ -221,31 +219,6 @@ describe("process CLI environment key diagnostics", () => {
|
||||
});
|
||||
|
||||
describe("process CLI evidence validation", () => {
|
||||
it("preserves sanitized unknown-process IDs and reasons through CLI projection", () => {
|
||||
const reason =
|
||||
"process ownership token could not be read for 1 live process(es): environment_unavailable=1; live candidates 900=environment_unavailable";
|
||||
const projected = projectProcessCliError(
|
||||
new ProcessCaptureError("cleanup incomplete", {
|
||||
reason: "cleanup_incomplete",
|
||||
cleanup: { reason, resources: ["owned_process_group"] },
|
||||
cleanupReport: {
|
||||
owned_process_group: { state: "unverified", reason },
|
||||
terminal_renderer: { state: "cleaned", reason: null },
|
||||
temporary_root: { state: "cleaned", reason: null },
|
||||
},
|
||||
}),
|
||||
"compare_process_captures",
|
||||
);
|
||||
expect(projected).toMatchObject({
|
||||
code: "cleanup_incomplete",
|
||||
details: {
|
||||
cleanup_report: {
|
||||
owned_process_group: { reason },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("captures the minimal executable-and-arguments scenario", async () => {
|
||||
const root = await createTestTempDirectory("rea-process-cli-");
|
||||
const scenario = join(root, "scenario.json");
|
||||
|
||||
@@ -7,12 +7,44 @@ import { z } from "zod";
|
||||
import { createCli } from "../../src/cli.js";
|
||||
import { compareEvidenceBundlesCommand } from "../../src/application/EvidenceBundleCommands.js";
|
||||
import { bundleComparisonResultSchema } from "../../src/domain/bundleComparison.js";
|
||||
import { processEvidence } from "../../src/application/ReconstructionObligationLedger.fixture.js";
|
||||
import { processCaptureSchema } from "../../src/domain/process/processCapture.js";
|
||||
import {
|
||||
createEvidenceBundle,
|
||||
serializeEvidenceBundle,
|
||||
} from "../../src/domain/evidenceBundle.js";
|
||||
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
|
||||
|
||||
it.each([
|
||||
new TypeError("capture validation implementation failed"),
|
||||
new Error("capture validator failed"),
|
||||
new z.ZodError([
|
||||
{ code: "custom", path: [], message: "capture refinement failed" },
|
||||
]),
|
||||
])(
|
||||
"preserves unexpected process validation failures while reading a bundle: $name",
|
||||
async (failure) => {
|
||||
const directory = await createTestTempDirectory(
|
||||
"rea-bundle-capture-failure-",
|
||||
);
|
||||
const path = join(directory, "bundle.json");
|
||||
await writeFile(
|
||||
path,
|
||||
serializeEvidenceBundle(createEvidenceBundle([processEvidence()])),
|
||||
);
|
||||
const validator = vi
|
||||
.spyOn(processCaptureSchema, "safeParse")
|
||||
.mockImplementation(() => {
|
||||
throw failure;
|
||||
});
|
||||
onTestFinished(() => validator.mockRestore());
|
||||
|
||||
await expect(
|
||||
compareEvidenceBundlesCommand({ leftPath: path, rightPath: path }),
|
||||
).rejects.toBe(failure);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
new TypeError("comparison output invariant failed"),
|
||||
new RangeError("comparison computation failed"),
|
||||
|
||||
@@ -26,9 +26,11 @@ import { managedMemberComparisonResultSchema } from "../../src/domain/managed/ma
|
||||
import { managedNativeVerificationResultSchema } from "../../src/domain/managed/managedNativeVerificationSchemas.js";
|
||||
import { managedReconstructionImportResultSchema } from "../../src/domain/managed/managedReconstruction.js";
|
||||
|
||||
import { captureProcessScenarioFile } from "../../src/application/process/ProcessCli.js";
|
||||
import { compareManagedMemberPaths } from "../../src/application/managed/ManagedMemberComparisonService.js";
|
||||
import { buildManagedPeFixture } from "../../src/dotnet/ManagedPe.fixture.js";
|
||||
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
|
||||
import { ProcessCaptureError } from "../../src/process/capture/ProcessCaptureError.js";
|
||||
|
||||
import { reconstructionObligationLedgerSchema } from "../../src/domain/reconstructionObligationLedgerSchemas.js";
|
||||
import {
|
||||
@@ -664,13 +666,13 @@ describe("ledger generation failure ownership through the public SDK", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it.each(unexpectedResultFailures.filter(([kind]) => kind !== "ZodError"))(
|
||||
it.each(unexpectedResultFailures)(
|
||||
"preserves an unexpected %s while deriving process candidates",
|
||||
async (_label, createFailure) => {
|
||||
await withSdk(async ({ client, session }) => {
|
||||
const input = ledgerRequest([processEvidence()]);
|
||||
const failure = createFailure();
|
||||
vi.spyOn(processCaptureSchema, "parse").mockImplementation(() => {
|
||||
vi.spyOn(processCaptureSchema, "safeParse").mockImplementation(() => {
|
||||
throw failure;
|
||||
});
|
||||
const response = await client.callTool({
|
||||
@@ -684,6 +686,87 @@ describe("ledger generation failure ownership through the public SDK", () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe("process capture CLI cleanup failure projection", () => {
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
it("preserves sanitized unknown-process IDs and reasons through the capture CLI boundary", async () => {
|
||||
const root = await createTestTempDirectory("rea-process-cli-cleanup-");
|
||||
const scenario = join(root, "scenario.json");
|
||||
await writeFile(
|
||||
scenario,
|
||||
JSON.stringify({ executable: process.execPath, arguments: [] }),
|
||||
);
|
||||
const reason =
|
||||
"process ownership token could not be read for 1 live process(es): environment_unavailable=1; live candidates 900=environment_unavailable";
|
||||
const failure = new ProcessCaptureError("cleanup incomplete", {
|
||||
reason: "cleanup_incomplete",
|
||||
cleanup: { reason, resources: ["owned_process_group"] },
|
||||
cleanupReport: {
|
||||
owned_process_group: {
|
||||
state: "unverified",
|
||||
reason,
|
||||
unverified_processes: [
|
||||
{ pid: 900, reason: "environment_unavailable" },
|
||||
],
|
||||
},
|
||||
terminal_renderer: { state: "cleaned", reason: null },
|
||||
temporary_root: { state: "cleaned", reason: null },
|
||||
},
|
||||
});
|
||||
const processHarness =
|
||||
await import("../../src/process/capture/ProcessHarness.js");
|
||||
vi.spyOn(processHarness, "captureProcessScenario").mockResolvedValue({
|
||||
ok: false,
|
||||
error: failure,
|
||||
});
|
||||
|
||||
const projected = await captureProcessScenarioFile(scenario);
|
||||
|
||||
expect(projected).toMatchObject({
|
||||
error: "Process command failed",
|
||||
code: "cleanup_incomplete",
|
||||
details: {
|
||||
cleanup_report: {
|
||||
owned_process_group: {
|
||||
reason,
|
||||
unverified_processes: [
|
||||
{ pid: 900, reason: "environment_unavailable" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("process comparison Evidence authentication failures through the public SDK", () => {
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
it.each(unexpectedResultFailures.filter(([kind]) => kind !== "ZodError"))(
|
||||
"preserves an unexpected %s without retaining Evidence",
|
||||
async (_label, createFailure) => {
|
||||
await withSdk(async ({ client, session }) => {
|
||||
const input = {
|
||||
left: processEvidence(),
|
||||
right: processEvidence(),
|
||||
};
|
||||
const failure = createFailure();
|
||||
const evidenceModule = await import("../../src/domain/evidence.js");
|
||||
vi.spyOn(evidenceModule, "parseEvidence").mockImplementation(() => {
|
||||
throw failure;
|
||||
});
|
||||
const response = await client.callTool({
|
||||
name: "compare_process_captures",
|
||||
arguments: input,
|
||||
});
|
||||
expectUnexpectedToolFailure(response, failure);
|
||||
expect(session.exportEvidenceBundle().records).toEqual([]);
|
||||
expect(session.exportEvidenceBundle().unknowns).toEqual([]);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("semantic query output failures through the public SDK", () => {
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
|
||||
Reference in new issue
Block a user