refactor(process): share capture evidence admission
This commit is contained in:
1 parent
b2dc45006d
commit
603a7d022c
3 files changed
+154
-113
No files matched your search
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.`,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user