mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 01:41:21 +00:00
* fix(native): retain value trace source evidence * fix(native): build trace error evidence only on failure * test(mcp): expect unowned value trace refusal * test(mcp): avoid freezing trace error wording * test(native): keep value-trace stop coverage with source evidence Main's stop and continuation cases now meet the trace's evidence-owner precondition: direct calls supply a source id, and a completed MCP trace runs in a session that can retain it. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
161 lines
5.4 KiB
TypeScript
161 lines
5.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { AnalysisOperationPort } from "../AnalysisProvider.js";
|
|
import { ok } from "../../domain/result.js";
|
|
import {
|
|
AnalysisCancelledError,
|
|
AnalysisCapabilityUnavailableError,
|
|
} from "../../domain/analysisErrorCore.js";
|
|
import { nativeValueTraceSchema } from "../../domain/native/nativeValueTrace.js";
|
|
import {
|
|
CLEANUP_STOPPED_VALUE_TRACE,
|
|
traceNativeValues,
|
|
} from "./NativeValueTrace.js";
|
|
import {
|
|
failingValueTraceReads,
|
|
valueTraceDiamond,
|
|
valueTraceProvider as provider,
|
|
VALUE_TRACE_DIAMOND_READS,
|
|
type ValueTraceDiamondRead,
|
|
} from "./NativeValueTrace.fixture.js";
|
|
|
|
describe("native value trace child stops", () => {
|
|
const cleanup = {
|
|
cleanup: { reason: "cleanup proof refused", resources: ["owned worker"] },
|
|
};
|
|
const operationOf = (read: ValueTraceDiamondRead) =>
|
|
read.startsWith("analyze_function")
|
|
? "analyze_function"
|
|
: "resolve_native_call_targets";
|
|
const childReads = [
|
|
"resolve_native_call_targets:0x1004",
|
|
"analyze_function:0x2000",
|
|
] as const;
|
|
const readsThrough = (read: ValueTraceDiamondRead) =>
|
|
VALUE_TRACE_DIAMOND_READS.slice(
|
|
0,
|
|
VALUE_TRACE_DIAMOND_READS.indexOf(read) + 1,
|
|
);
|
|
// The trace cites each completed dossier. These cases cover stop behavior,
|
|
// so the fixture only has to satisfy that precondition.
|
|
const withSourceEvidence = (
|
|
port: AnalysisOperationPort,
|
|
): AnalysisOperationPort => ({
|
|
execute: async (operation, parameters, options) => {
|
|
const execution = await port.execute(operation, parameters, options);
|
|
if (!execution.ok || execution.value.evidenceId !== undefined)
|
|
return execution;
|
|
return ok({
|
|
...execution.value,
|
|
evidenceId: `ev_${"ab".repeat(32)}`,
|
|
});
|
|
},
|
|
});
|
|
|
|
it.each(childReads)(
|
|
"returns a cancelled %s read without a partial trace",
|
|
async (read) => {
|
|
const cancelled = new AnalysisCancelledError(operationOf(read));
|
|
const { port, reads } = failingValueTraceReads(valueTraceDiamond().port, {
|
|
[read]: cancelled,
|
|
});
|
|
|
|
const result = await traceNativeValues(withSourceEvidence(port), {
|
|
procedure: "0x1000",
|
|
max_depth: 1,
|
|
});
|
|
|
|
expect(result).toStrictEqual({ ok: false, error: cancelled });
|
|
expect(reads).toEqual(readsThrough(read));
|
|
},
|
|
);
|
|
|
|
it.each(
|
|
childReads.flatMap((read) =>
|
|
(["caller abort", "cancelled read"] as const).map((race) => ({
|
|
read,
|
|
race,
|
|
})),
|
|
),
|
|
)(
|
|
"keeps unconfirmed cleanup primary over a $race at $read",
|
|
async ({ read, race }) => {
|
|
const controller = new AbortController();
|
|
const failure =
|
|
race === "cancelled read"
|
|
? new AnalysisCancelledError(operationOf(read), cleanup)
|
|
: new AnalysisCapabilityUnavailableError(
|
|
provider.id,
|
|
operationOf(read),
|
|
"controlled child failure",
|
|
cleanup,
|
|
);
|
|
const { port, reads } = failingValueTraceReads(valueTraceDiamond().port, {
|
|
[read]: failure,
|
|
});
|
|
// The caller aborts while the failing read settles.
|
|
const aborting: AnalysisOperationPort = {
|
|
execute: async (operation, parameters, options) => {
|
|
const selector = `${operation}:${String(parameters.procedure ?? parameters.address)}`;
|
|
if (selector === read) controller.abort("caller stopped the trace");
|
|
return port.execute(operation, parameters, options);
|
|
},
|
|
};
|
|
|
|
const result = await traceNativeValues(
|
|
withSourceEvidence(aborting),
|
|
{ procedure: "0x1000", max_depth: 1 },
|
|
controller.signal,
|
|
);
|
|
|
|
expect(reads).toEqual(readsThrough(read));
|
|
if (result.ok || !("partialResult" in result))
|
|
throw new Error("Expected a stopped trace with its admitted result");
|
|
expect(result.error).toBe(failure);
|
|
expect(nativeValueTraceSchema.parse(result.partialResult)).toMatchObject({
|
|
procedures: [{ address: "0x1000" }],
|
|
traversal: { status: "truncated", reasons: ["cleanup_incomplete"] },
|
|
truncated: true,
|
|
});
|
|
},
|
|
);
|
|
|
|
it("records the stop as a located unknown in the admitted trace", async () => {
|
|
const failure = new AnalysisCapabilityUnavailableError(
|
|
provider.id,
|
|
"resolve_native_call_targets",
|
|
"controlled child failure",
|
|
cleanup,
|
|
);
|
|
const { port } = failingValueTraceReads(valueTraceDiamond().port, {
|
|
"resolve_native_call_targets:0x1008": failure,
|
|
});
|
|
|
|
const result = await traceNativeValues(withSourceEvidence(port), {
|
|
procedure: "0x1000",
|
|
max_depth: 1,
|
|
});
|
|
|
|
if (result.ok || !("partialResult" in result))
|
|
throw new Error("Expected a stopped trace with its admitted result");
|
|
const admitted = nativeValueTraceSchema.parse(result.partialResult);
|
|
// 0x2000 was queued from 0x1004 but never analyzed; the stop, not a depth
|
|
// limit, accounts for it, and the admitted call edge to it remains.
|
|
expect(admitted.traversal).toEqual({
|
|
status: "truncated",
|
|
reasons: ["cleanup_incomplete"],
|
|
});
|
|
expect(admitted.unknowns.filter(({ address }) => address !== null)).toEqual(
|
|
[
|
|
{
|
|
procedure: "0x1000",
|
|
address: "0x1008",
|
|
reason: CLEANUP_STOPPED_VALUE_TRACE,
|
|
},
|
|
],
|
|
);
|
|
expect(admitted.edges).toContainEqual(
|
|
expect.objectContaining({ kind: "call", target: "0x2000" }),
|
|
);
|
|
});
|
|
});
|