refactor(process): share capture evidence admission

This commit is contained in:
morluto committed 2026-10-22 07:37:21 +08:00
1 parent b2dc45006d
commit 603a7d022c
3 files changed
+154 -113

No files matched your search

+36 -47
View File
@@ -9,7 +9,7 @@ import {
import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
import type { AnalysisErrorProjection } from "../../domain/analysisErrorProjection.js";
import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
import { createEvidence, parseEvidence } from "../../domain/evidence.js";
import { createEvidence } from "../../domain/evidence.js";
import { describeValidationFailure } from "../../domain/evidenceBundle.js";
import { jsonValueSchema } from "../../domain/jsonValue.js";
import {
@@ -19,16 +19,15 @@ import {
import { processTraceSpecificationSchema } from "../../domain/process/processTraceSpecification.js";
import { processScenarioSchema } from "../../domain/process/processScenario.js";
import { compareProcessCaptures } from "../../domain/process/processComparison.js";
import { parseProcessCapture } from "../../domain/process/processCaptureParsing.js";
import { createProgressReporter } from "../ProgressReporter.js";
import { captureProcessScenario } from "../../process/capture/ProcessHarness.js";
import { ProcessCaptureResourceScope } from "../../process/capture/ProcessCaptureLifecycle.js";
import { ProcessCaptureError } from "../../process/capture/ProcessCaptureError.js";
import { PROCESS_PROVIDER } from "../../domain/process/processEvidenceProvider.js";
import {
PROCESS_PROVIDER,
isProcessEvidenceProvider,
} from "../../domain/process/processEvidenceProvider.js";
import { createProcessCaptureEvidence } from "./ProcessEvidence.js";
createProcessCaptureEvidence,
parseProcessCaptureEvidence,
} from "./ProcessEvidence.js";
import { readJsonFile } from "../JsonFiles.js";
import { RegularFileCleanupFailure } from "../RegularFileRead.js";
@@ -147,8 +146,8 @@ export const compareProcessEvidenceFiles = async (
predicateType: "rea.process-comparison",
operation: "compare_process_captures",
parameters: {
left_evidence_id: left.id,
right_evidence_id: right.id,
left_evidence_id: left.record.evidence_id,
right_evidence_id: right.record.evidence_id,
left_normalization: left.capture.normalization,
right_normalization: right.capture.normalization,
...(traceSpecification === undefined
@@ -159,8 +158,8 @@ export const compareProcessEvidenceFiles = async (
confidence: "derived",
authority: "analyst-inference",
limitations: comparison.limitations,
locations: [...left.locations, ...right.locations],
evidenceLinks: [left.id, right.id],
locations: [...left.record.locations, ...right.record.locations],
evidenceLinks: [left.record.evidence_id, right.record.evidence_id],
});
} catch (cause: unknown) {
if (cause instanceof RegularFileCleanupFailure) throw cause;
@@ -169,45 +168,35 @@ export const compareProcessEvidenceFiles = async (
};
const parseCaptureEvidence = (input: unknown, side: "left" | "right") => {
let evidence;
try {
evidence = parseEvidence(input);
} catch (cause: unknown) {
if (cause instanceof z.ZodError)
throw new AnalysisInputError(
const parsed = parseProcessCaptureEvidence(input);
if (parsed.ok) return parsed.value;
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);
case "wrong-workflow":
throw processInputFailure(
"compare_process_captures",
{ cause },
projectInputIssues(cause.issues, input),
"Capture evidence is not from the current process-capture workflow. Create new capture evidence, then try again.",
[side],
"invalid_value",
);
throw invalidCaptureEvidence(cause, side);
}
if (
evidence.operation !== "capture_process_scenario" ||
evidence.predicate_type !== "rea.process-capture"
) {
throw processInputFailure(
"compare_process_captures",
`Capture evidence is not from the current process-capture workflow. Create new capture evidence, then try again.`,
[side],
"invalid_value",
);
}
if (!isProcessEvidenceProvider(evidence.provider))
throw new AnalysisInputError("compare_process_captures", undefined, [
{
path: [side, "provider"],
reason: "invalid_value",
message: `The ${side} Evidence provider ${evidence.provider.id} v${evidence.provider.version} (${evidence.provider.name}) is not the current or supported legacy ${PROCESS_PROVIDER.id} v${PROCESS_PROVIDER.version} process capture provider.`,
},
]);
try {
return {
id: evidence.evidence_id,
capture: parseProcessCapture(evidence.normalized_result),
locations: evidence.locations,
};
} catch (cause: unknown) {
throw invalidCaptureEvidence(cause, side);
case "unsupported-provider":
throw new AnalysisInputError("compare_process_captures", undefined, [
{
path: [side, "provider"],
reason: "invalid_value",
message: `The ${side} Evidence provider ${failure.provider.id} v${failure.provider.version} (${failure.provider.name}) is not the current or supported legacy ${PROCESS_PROVIDER.id} v${PROCESS_PROVIDER.version} process capture provider.`,
},
]);
case "invalid-capture":
throw invalidCaptureEvidence(failure.cause, side);
}
};
+73 -5
View File
@@ -1,11 +1,79 @@
import { posix, win32 } from "node:path";
import type { JsonValue } from "../../domain/jsonValue.js";
import { createEvidence } from "../../domain/evidence.js";
import { PROCESS_PROVIDER } from "../../domain/process/processEvidenceProvider.js";
import {
createEvidence,
parseEvidence,
type Evidence,
type EvidenceProvider,
} from "../../domain/evidence.js";
import {
PROCESS_PROVIDER,
isProcessEvidenceProvider,
} from "../../domain/process/processEvidenceProvider.js";
import { jsonValueSchema } from "../../domain/jsonValue.js";
import type { ProcessCapture } from "../../domain/process/processCaptureParsing.js";
import {
parseProcessCapture,
type ProcessCapture,
} from "../../domain/process/processCaptureParsing.js";
import type { ProcessScenario } from "../../domain/process/processScenario.js";
import { err, ok, type Result } from "../../domain/result.js";
const PROCESS_CAPTURE_EVIDENCE = {
operation: "capture_process_scenario",
predicate: "rea.process-capture",
} as const;
/** Semantic rejection facts; CLI and MCP retain their own error projections. */
export type ProcessCaptureEvidenceFailure =
| { readonly kind: "invalid-evidence"; readonly cause: unknown }
| {
readonly kind: "wrong-workflow";
readonly expected: typeof PROCESS_CAPTURE_EVIDENCE;
readonly actual: {
readonly operation: string;
readonly predicate: string;
};
}
| {
readonly kind: "unsupported-provider";
readonly provider: EvidenceProvider;
}
| { readonly kind: "invalid-capture"; readonly cause: unknown };
/** Authenticate capture Evidence before admitting its workflow, provider and payload. */
export const parseProcessCaptureEvidence = (
input: unknown,
): Result<
{ readonly record: Evidence; readonly capture: ProcessCapture },
ProcessCaptureEvidenceFailure
> => {
let record: Evidence;
try {
record = parseEvidence(input);
} catch (cause: unknown) {
return err({ kind: "invalid-evidence", cause });
}
if (
record.operation !== PROCESS_CAPTURE_EVIDENCE.operation ||
record.predicate_type !== PROCESS_CAPTURE_EVIDENCE.predicate
)
return err({
kind: "wrong-workflow",
expected: PROCESS_CAPTURE_EVIDENCE,
actual: { operation: record.operation, predicate: record.predicate_type },
});
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 });
}
};
/** Project one process scenario into secret-free Evidence parameters. */
const processEvidenceParameters = (
@@ -28,8 +96,8 @@ export const createProcessCaptureEvidence = (
capture: ProcessCapture,
) => {
return createEvidence(undefined, PROCESS_PROVIDER, {
predicateType: "rea.process-capture",
operation: "capture_process_scenario",
predicateType: PROCESS_CAPTURE_EVIDENCE.predicate,
operation: PROCESS_CAPTURE_EVIDENCE.operation,
parameters: processEvidenceParameters(scenario, capture.manifest.platform),
result: jsonValueSchema.parse(capture),
confidence: "observed",
+45 -61
View File
@@ -8,34 +8,23 @@ import { describeValidationFailure } from "../domain/evidenceBundle.js";
import { EvidenceIntegrityError } from "../domain/evidenceErrors.js";
import {
createEvidence,
parseEvidence,
type Evidence,
type EvidenceLocation,
} from "../domain/evidence.js";
import { jsonValueSchema } from "../domain/jsonValue.js";
import { compareProcessCaptures } from "../domain/process/processComparison.js";
import {
parseProcessCapture,
type ProcessCapture,
} from "../domain/process/processCaptureParsing.js";
import type { ProcessCapture } from "../domain/process/processCaptureParsing.js";
import { parseProcessCaptureEvidence } from "../application/process/ProcessEvidence.js";
import type { RecordUnknownInput } from "../domain/residualUnknown.js";
import { err, ok, type Result } from "../domain/result.js";
import { err, type Result } from "../domain/result.js";
import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
import { recordSessionEvidenceSources } from "./sessionEvidence.js";
import { runDerivedOperation } from "./runDerivedOperation.js";
import {
PROCESS_PROVIDER,
isProcessEvidenceProvider,
} from "../domain/process/processEvidenceProvider.js";
import { PROCESS_PROVIDER } from "../domain/process/processEvidenceProvider.js";
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
import { withAdmittedAnalysis } from "./analysisAdmission.js";
import { runAdmittedToolOperation } from "./admittedToolOperation.js";
const PROCESS_CAPTURE_EVIDENCE = {
operation: "capture_process_scenario",
predicate: "rea.process-capture",
} as const;
const sourceLocations = (
left: readonly EvidenceLocation[] | undefined,
right: readonly EvidenceLocation[] | undefined,
@@ -53,52 +42,47 @@ const parseCaptureSide = (
{ readonly record: Evidence; readonly capture: ProcessCapture },
AnalysisError
> => {
let record: Evidence;
try {
record = parseEvidence(value);
} catch (cause: unknown) {
return err(
new EvidenceIntegrityError(`${side} Evidence failed validation`, {
cause,
userMessage: `The ${side} Evidence failed validation (${describeValidationFailure(cause)}). Supply unmodified capture Evidence from capture_process_scenario.`,
}),
);
}
if (
record.operation !== PROCESS_CAPTURE_EVIDENCE.operation ||
record.predicate_type !== PROCESS_CAPTURE_EVIDENCE.predicate
)
return err(
new AnalysisInputError(operation, undefined, [
{
path: [side],
reason: "invalid_value",
message: `Expected process capture Evidence (operation ${PROCESS_CAPTURE_EVIDENCE.operation}, predicate ${PROCESS_CAPTURE_EVIDENCE.predicate}); the ${side} Evidence has operation ${record.operation} and predicate ${record.predicate_type}.`,
},
]),
);
if (!isProcessEvidenceProvider(record.provider))
return err(
new AnalysisInputError(operation, undefined, [
{
path: [side, "provider"],
reason: "invalid_value",
message: `The ${side} Evidence provider ${record.provider.id} v${record.provider.version} (${record.provider.name}) is not the current or supported legacy ${PROCESS_PROVIDER.id} v${PROCESS_PROVIDER.version} process capture provider.`,
},
]),
);
try {
return ok({
record,
capture: parseProcessCapture(record.normalized_result),
});
} catch (cause: unknown) {
return err(
new EvidenceIntegrityError(`${side} process capture failed validation`, {
cause,
userMessage: `The ${side} Evidence has an invalid process capture result (${describeValidationFailure(cause)}). Recreate the capture with capture_process_scenario.`,
}),
);
const parsed = parseProcessCaptureEvidence(value);
if (parsed.ok) return parsed;
const failure = parsed.error;
switch (failure.kind) {
case "invalid-evidence":
return err(
new EvidenceIntegrityError(`${side} Evidence failed validation`, {
cause: failure.cause,
userMessage: `The ${side} Evidence failed validation (${describeValidationFailure(failure.cause)}). Supply unmodified capture Evidence from capture_process_scenario.`,
}),
);
case "wrong-workflow":
return err(
new AnalysisInputError(operation, undefined, [
{
path: [side],
reason: "invalid_value",
message: `Expected process capture Evidence (operation ${failure.expected.operation}, predicate ${failure.expected.predicate}); the ${side} Evidence has operation ${failure.actual.operation} and predicate ${failure.actual.predicate}.`,
},
]),
);
case "unsupported-provider":
return err(
new AnalysisInputError(operation, undefined, [
{
path: [side, "provider"],
reason: "invalid_value",
message: `The ${side} Evidence provider ${failure.provider.id} v${failure.provider.version} (${failure.provider.name}) is not the current or supported legacy ${PROCESS_PROVIDER.id} v${PROCESS_PROVIDER.version} process capture provider.`,
},
]),
);
case "invalid-capture":
return err(
new EvidenceIntegrityError(
`${side} process capture failed validation`,
{
cause: failure.cause,
userMessage: `The ${side} Evidence has an invalid process capture result (${describeValidationFailure(failure.cause)}). Recreate the capture with capture_process_scenario.`,
},
),
);
}
};