Files
rea/tests/acceptance/derivedEvidenceReplay.test.ts
T
morluto 10e9448d89 fix(evidence): anchor deterministic replay to unknown creation
Match repeated derived Evidence against the immutable creation revision while retaining analyst updates, conflict rejection and imported history.

Refs: #3925, #4159

Co-authored-by: rudrakshkarpe <rudraksh.karpe@gmail.com>
2026-10-16 04:41:39 +08:00

160 lines
5.9 KiB
TypeScript

import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it } from "vitest";
import { z } from "zod";
import { FUNCTION_COMPARISON_EXAMPLE } from "../../src/contracts/functionComparisonExample.js";
import { createEvidence, parseEvidence } from "../../src/domain/evidence.js";
import { parseEvidenceBundle } from "../../src/domain/evidenceBundle.js";
import { EMPTY_PROCESS_CAPTURE_EXAMPLE } from "../../src/domain/process/processCaptureExample.js";
import { PROCESS_PROVIDER } from "../../src/domain/process/processEvidenceProvider.js";
import {
residualUnknownSchema,
type ResidualUnknown,
type UpdateUnknownInput,
} from "../../src/domain/residualUnknown.js";
import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js";
import { parseMcpToolError } from "../fixtures/mcpToolError.js";
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
const resultEnvelope = z.object({ result: z.unknown() });
const capture = (changed: boolean) =>
createEvidence(undefined, PROCESS_PROVIDER, {
predicateType: "rea.process-capture",
operation: "capture_process_scenario",
parameters: {},
result: {
...EMPTY_PROCESS_CAPTURE_EXAMPLE,
interaction_events: changed
? [
{
sequence: 0,
scheduled_at_ms: 0,
dispatched_at_ms: 0,
type: "input",
data: "fixture",
outcome: "dispatched",
},
]
: [],
},
confidence: "observed",
authority: "controlled-replay",
});
const updateInput = (
unknown: ResidualUnknown,
status: "investigating" | "resolved",
): UpdateUnknownInput => ({
unknown_id: unknown.unknown_id,
expected_revision: unknown.revision,
status,
severity: "low",
supporting_evidence_ids: unknown.supporting_evidence_ids,
contradicting_evidence_ids: unknown.contradicting_evidence_ids,
required_authority: unknown.required_authority,
required_confidence: unknown.required_confidence,
required_environment: unknown.required_environment,
recommended_probes: [],
relationships: unknown.relationships,
resolution:
status === "resolved"
? {
disposition: "withdrawn",
rationale: "Analyst withdrew this question.",
evidence_ids: [],
}
: null,
});
it.each([
{
operation: "compare_process_captures",
left: capture(false),
right: capture(true),
},
{ operation: "compare_functions", ...FUNCTION_COMPARISON_EXAMPLE },
])(
"replays $operation without reverting analyst revisions, including after handoff",
async ({ operation, left, right }) => {
const { call } = await connectLocalToolsMcp();
const args = { left, right };
const compared = await call(operation, args);
expect(compared.isError, JSON.stringify(compared)).not.toBe(true);
const evidence = parseEvidence(compared.structuredContent);
expect(evidence.normalized_result).toMatchObject({ status: "changed" });
const bundle = async () => {
const response = await call("get_evidence_bundle", {});
expect(response.isError, JSON.stringify(response)).not.toBe(true);
return parseEvidenceBundle(
resultEnvelope.parse(response.structuredContent).result,
);
};
const initial = await bundle();
expect(initial.unknowns).toHaveLength(1);
let head = residualUnknownSchema.parse(initial.unknowns[0]);
const repeat = await call(operation, args);
expect(repeat.isError, JSON.stringify(repeat)).not.toBe(true);
expect(parseEvidence(repeat.structuredContent)).toEqual(evidence);
expect(await bundle()).toEqual(initial);
for (const status of ["investigating", "resolved"] as const) {
const command = updateInput(head, status);
const updated = await call("update_unknown", command);
expect(updated.isError, JSON.stringify(updated)).not.toBe(true);
head = residualUnknownSchema.parse(
resultEnvelope.parse(updated.structuredContent).result,
);
expect(head).toMatchObject({
status,
revision: command.expected_revision + 1,
});
const beforeReplay = await bundle();
const replayed = await call(operation, args);
expect(replayed.isError, JSON.stringify(replayed)).not.toBe(true);
expect(parseEvidence(replayed.structuredContent)).toEqual(evidence);
expect(await bundle()).toEqual(beforeReplay);
const stale = await call("update_unknown", command);
expect(stale.isError).toBe(true);
expect(parseMcpToolError(stale)).toMatchObject({
error: {
code: "revision_conflict",
details: { reason: "revision-conflict" },
},
});
expect(await bundle()).toEqual(beforeReplay);
}
const retained = await bundle();
expect(retained.unknowns.map(({ revision }) => revision)).toEqual([
1, 2, 3,
]);
const root = await createTestTempDirectory("rea-derived-replay-");
const path = join(root, "handoff.json");
await writeFile(path, JSON.stringify(retained));
const restored = await connectLocalToolsMcp();
const imported = await restored.call("import_evidence_bundle", { path });
expect(imported.isError, JSON.stringify(imported)).not.toBe(true);
const replayed = await restored.call(operation, args);
expect(replayed.isError, JSON.stringify(replayed)).not.toBe(true);
expect(parseEvidence(replayed.structuredContent)).toEqual(evidence);
const listed = await restored.call("list_unknowns", {});
expect(listed.structuredContent).toMatchObject({
result: { items: [head] },
});
const invalid = await restored.call(operation, {
left: { ...left, normalized_result: {} },
right,
});
expect(invalid.isError).toBe(true);
const afterReplay = await restored.call("get_evidence_bundle", {});
expect(
parseEvidenceBundle(
resultEnvelope.parse(afterReplay.structuredContent).result,
),
).toEqual(retained);
},
);