mirror of
https://github.com/morluto/rea.git
synced 2026-10-22 22:02:27 -05:00
Separate source corruption and selected-role errors from unexpected derivation failures. Normalize coordinates before body membership and conflict checks, reuse parsed requests, and account for outer observation arrays before validation copies.
524 lines
17 KiB
TypeScript
524 lines
17 KiB
TypeScript
import { readFile, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect } from "vitest";
|
|
import { cliTest, type TestCli } from "../../support/cli/cliFixture.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
|
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
|
|
import {
|
|
nativeMatchingInventory,
|
|
nativeMatchingDetails,
|
|
nativeMatchingTarget,
|
|
} from "../../fixtures/nativeFunctionMatching.js";
|
|
import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js";
|
|
import { nativeMatchingProvider } from "../../fixtures/nativeFunctionMatching.js";
|
|
import { procedureInfoSchema } from "../../../src/domain/native/procedureInfo.js";
|
|
import {
|
|
createEvidenceBundle,
|
|
parseEvidenceBundle,
|
|
} from "../../../src/domain/evidenceBundle.js";
|
|
import { nativeFunctionMatchingSchema } from "../../../src/domain/native/functionMatching.js";
|
|
|
|
const createMatchingFixture = async () => {
|
|
const root = await createTestTempDirectory("rea-native-matching-");
|
|
const ancestor = createEvidence(undefined, nativeMatchingProvider, {
|
|
operation: "fixture_provenance",
|
|
parameters: {},
|
|
result: { source: "Synthetic ancestor" },
|
|
});
|
|
const unrelated = createEvidence(undefined, nativeMatchingProvider, {
|
|
operation: "fixture_unrelated",
|
|
parameters: {},
|
|
result: { source: "Unselected synthetic record" },
|
|
});
|
|
const dependency = createEvidence(undefined, nativeMatchingProvider, {
|
|
operation: "fixture_dependency",
|
|
parameters: {},
|
|
result: { source: "Synthetic dependency" },
|
|
evidenceLinks: [ancestor.evidence_id],
|
|
});
|
|
const left = nativeMatchingInventory(
|
|
"1",
|
|
[
|
|
{ address: "0x10", value: "main" },
|
|
{ address: "0x20", value: "old_name" },
|
|
],
|
|
[dependency.evidence_id],
|
|
);
|
|
const right = nativeMatchingInventory("2", [
|
|
{ address: "0x30", value: "main" },
|
|
{ address: "0x40", value: "new_name" },
|
|
]);
|
|
const leftDetails = [
|
|
...nativeMatchingDetails("1", "0x10", "9000", true),
|
|
...nativeMatchingDetails("1", "0x20", "c3", true),
|
|
];
|
|
const rightDetails = [
|
|
...nativeMatchingDetails("2", "0x30", "9001", true),
|
|
...nativeMatchingDetails("2", "0x40", "c3", true),
|
|
];
|
|
const ancestorsPath = join(root, "ancestors.json");
|
|
await writeFile(
|
|
ancestorsPath,
|
|
JSON.stringify(createEvidenceBundle([ancestor, unrelated])),
|
|
);
|
|
const importedPath = join(root, "dependencies.json");
|
|
await writeFile(
|
|
importedPath,
|
|
JSON.stringify(createEvidenceBundle([ancestor, dependency])),
|
|
);
|
|
return {
|
|
root,
|
|
ancestor,
|
|
dependency,
|
|
unrelated,
|
|
left,
|
|
right,
|
|
leftDetails,
|
|
rightDetails,
|
|
ancestorsPath,
|
|
importedPath,
|
|
};
|
|
};
|
|
const verifyPortableCli = async (
|
|
cli: TestCli,
|
|
fixture: Awaited<ReturnType<typeof createMatchingFixture>>,
|
|
input: Record<string, unknown>,
|
|
evidenceId: string,
|
|
) => {
|
|
const {
|
|
root,
|
|
ancestor,
|
|
dependency,
|
|
unrelated,
|
|
left,
|
|
right,
|
|
leftDetails,
|
|
rightDetails,
|
|
ancestorsPath,
|
|
} = fixture;
|
|
const bundlePath = join(root, "sources.json"),
|
|
inputPath = join(root, "request.json"),
|
|
outputPath = join(root, "result.json");
|
|
await writeFile(
|
|
bundlePath,
|
|
JSON.stringify(
|
|
createEvidenceBundle([
|
|
left,
|
|
right,
|
|
...leftDetails,
|
|
...rightDetails,
|
|
dependency,
|
|
]),
|
|
),
|
|
);
|
|
const original = await readFile(bundlePath);
|
|
await writeFile(inputPath, JSON.stringify({ ...input, detail: "complete" }));
|
|
const cliFull = await cli.run({
|
|
arguments: [
|
|
"match-native-functions",
|
|
inputPath,
|
|
"--source_bundle",
|
|
ancestorsPath,
|
|
"--source_bundle",
|
|
bundlePath,
|
|
"--json",
|
|
],
|
|
});
|
|
expect(cliFull.exitCode, cliFull.stderr).toBe(0);
|
|
expect(parseEvidence(cliFull.json).evidence_id).toBe(evidenceId);
|
|
await writeFile(inputPath, JSON.stringify(input));
|
|
const cliSummary = await cli.run({
|
|
arguments: [
|
|
"match-native-functions",
|
|
inputPath,
|
|
"--source_bundle",
|
|
ancestorsPath,
|
|
"--source_bundle",
|
|
bundlePath,
|
|
"--evidence_output",
|
|
outputPath,
|
|
"--json",
|
|
],
|
|
});
|
|
expect(cliSummary.exitCode, cliSummary.stderr).toBe(0);
|
|
expect(cliSummary.json).toMatchObject({
|
|
summary: {
|
|
normalized_result: { parent_evidence_id: evidenceId },
|
|
},
|
|
evidence_bundle: { path: outputPath },
|
|
});
|
|
const bundle = parseEvidenceBundle(
|
|
JSON.parse(await readFile(outputPath, "utf8")),
|
|
);
|
|
const ids = new Set(bundle.records.map((x) => x.evidence_id));
|
|
for (const record of bundle.records)
|
|
for (const id of record.evidence_links)
|
|
expect(
|
|
ids.has(id),
|
|
`Missing dependency ${id} of ${record.evidence_id}`,
|
|
).toBe(true);
|
|
expect(ids.has(ancestor.evidence_id)).toBe(true);
|
|
expect(ids.has(dependency.evidence_id)).toBe(true);
|
|
expect(ids.has(unrelated.evidence_id)).toBe(false);
|
|
expect(ids.has(evidenceId)).toBe(true);
|
|
expect(await readFile(bundlePath)).toEqual(original);
|
|
const incomplete = await cli.run({
|
|
arguments: [
|
|
"match-native-functions",
|
|
inputPath,
|
|
"--source_bundle",
|
|
bundlePath,
|
|
"--evidence_output",
|
|
join(root, "incomplete.json"),
|
|
"--json",
|
|
],
|
|
});
|
|
expect(incomplete.json).toMatchObject({
|
|
code: "evidence_integrity_mismatch",
|
|
});
|
|
};
|
|
|
|
cliTest.for([{ profile: "full" }, { profile: "compact" }] as const)(
|
|
"shared public CLI/MCP matching retains sources, pages exact facts, and feeds selected dossier pairs directly to compare_functions ($profile)",
|
|
{ timeout: 60000 },
|
|
async ({ profile }, { cli }) => {
|
|
const fixture = await createMatchingFixture();
|
|
const { left, right, leftDetails, rightDetails, importedPath } = fixture;
|
|
const { call } = await connectLocalToolsMcp({
|
|
REA_MCP_INPUT_SCHEMA_PROFILE: profile,
|
|
});
|
|
const imported = await call("import_evidence_bundle", {
|
|
path: importedPath,
|
|
});
|
|
expect(imported.isError, JSON.stringify(imported)).not.toBe(true);
|
|
const full = await call("match_native_functions", {
|
|
left,
|
|
right,
|
|
left_observations: leftDetails,
|
|
right_observations: rightDetails,
|
|
detail: "complete",
|
|
});
|
|
expect(full.isError, JSON.stringify(full)).not.toBe(true);
|
|
const evidence = parseEvidence(full.structuredContent),
|
|
report = nativeFunctionMatchingSchema.parse(evidence.normalized_result);
|
|
expect(report.summary).toEqual({
|
|
matched: 2,
|
|
only_left: 0,
|
|
only_right: 0,
|
|
unresolved: 0,
|
|
});
|
|
const reference = (evidence_id: string) => ({
|
|
kind: "retained-evidence",
|
|
evidence_id,
|
|
});
|
|
const input = {
|
|
left: reference(left.evidence_id),
|
|
right: reference(right.evidence_id),
|
|
left_observations: leftDetails.map((x) => reference(x.evidence_id)),
|
|
right_observations: rightDetails.map((x) => reference(x.evidence_id)),
|
|
};
|
|
const summary = await call("match_native_functions", input);
|
|
expect(summary.isError, JSON.stringify(summary)).not.toBe(true);
|
|
expect(
|
|
parseEvidence(summary.structuredContent).normalized_result,
|
|
).toMatchObject({
|
|
kind: "summary",
|
|
artifact: null,
|
|
parent_evidence_id: evidence.evidence_id,
|
|
summary: { summary: report.summary },
|
|
});
|
|
expect(parseEvidence(summary.structuredContent).locations).not.toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ kind: "artifact-path" }),
|
|
]),
|
|
);
|
|
const page = await call("inspect_analysis_view", {
|
|
source: reference(evidence.evidence_id),
|
|
view: {
|
|
kind: "function-matches",
|
|
collection: "functions",
|
|
sides: ["left"],
|
|
fields: ["name", "body_sha256"],
|
|
offset: 1,
|
|
limit: 1,
|
|
},
|
|
});
|
|
expect(page.isError, JSON.stringify(page)).not.toBe(true);
|
|
expect(
|
|
parseEvidence(page.structuredContent).normalized_result,
|
|
).toMatchObject({
|
|
artifact: null,
|
|
coverage: { total: 2, next_offset: null },
|
|
items: [
|
|
{
|
|
name: "old_name",
|
|
function_id: `${left.evidence_id}/1`,
|
|
address: "0x20",
|
|
},
|
|
],
|
|
});
|
|
expect(parseEvidence(page.structuredContent).locations).not.toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ kind: "artifact-path" }),
|
|
]),
|
|
);
|
|
const compared = await call(
|
|
"compare_functions",
|
|
report.matches[0]!.compare_functions_input!,
|
|
);
|
|
expect(compared.isError, JSON.stringify(compared)).not.toBe(true);
|
|
expect(parseEvidence(compared.structuredContent).operation).toBe(
|
|
"compare_functions",
|
|
);
|
|
await verifyPortableCli(cli, fixture, input, evidence.evidence_id);
|
|
const missing = await call("match_native_functions", {
|
|
left: reference(`ev_${"a".repeat(64)}`),
|
|
right: reference(right.evidence_id),
|
|
});
|
|
expect(missing.isError).toBe(true);
|
|
},
|
|
);
|
|
|
|
cliTest(
|
|
"native matching rejects corrupt and misselected MCP sources without retaining them",
|
|
async () => {
|
|
const fixture = await createMatchingFixture();
|
|
const { left, right, leftDetails, root, importedPath } = fixture;
|
|
const { call } = await connectLocalToolsMcp();
|
|
const importedSources = await call("import_evidence_bundle", {
|
|
path: importedPath,
|
|
});
|
|
expect(importedSources.isError, JSON.stringify(importedSources)).not.toBe(
|
|
true,
|
|
);
|
|
const before = await call("get_evidence_bundle", {});
|
|
const corruptEnvelope = { ...left, normalized_result: [] };
|
|
const badCoordinate = nativeMatchingInventory("1", [
|
|
{ address: "not-an-address", value: "main" },
|
|
]);
|
|
const observationResult = procedureInfoSchema.parse(
|
|
nativeMatchingDetails("1", "0x10", "c3")[0]!.normalized_result,
|
|
);
|
|
const corruptObservation = createEvidence(
|
|
nativeMatchingTarget("1"),
|
|
nativeMatchingProvider,
|
|
{
|
|
operation: "procedure_info",
|
|
parameters: { procedure: "0x10" },
|
|
result: {
|
|
...observationResult,
|
|
entrypoint: "not-an-address",
|
|
},
|
|
},
|
|
);
|
|
for (const { request, expected } of [
|
|
{
|
|
request: { left: corruptEnvelope, right },
|
|
expected: { code: "evidence_integrity_mismatch" },
|
|
},
|
|
{
|
|
request: { left: badCoordinate, right },
|
|
expected: { code: "evidence_integrity_mismatch" },
|
|
},
|
|
{
|
|
request: { left, right, left_observations: [corruptObservation] },
|
|
expected: { code: "evidence_integrity_mismatch" },
|
|
},
|
|
{
|
|
request: { left: leftDetails[0], right },
|
|
expected: {
|
|
code: "invalid_request",
|
|
details: {
|
|
issues: [expect.objectContaining({ path: ["left"] })],
|
|
},
|
|
},
|
|
},
|
|
]) {
|
|
const rejected = await call("match_native_functions", {
|
|
...request,
|
|
detail: "complete",
|
|
});
|
|
expect(parseMcpToolError(rejected).error).toMatchObject(expected);
|
|
expect((await call("get_evidence_bundle", {})).structuredContent).toEqual(
|
|
before.structuredContent,
|
|
);
|
|
}
|
|
|
|
const corruptBundlePath = join(root, "corrupt-source.json");
|
|
await writeFile(
|
|
corruptBundlePath,
|
|
JSON.stringify(createEvidenceBundle([badCoordinate, right])),
|
|
);
|
|
const importedCorruptPayload = await call("import_evidence_bundle", {
|
|
path: corruptBundlePath,
|
|
});
|
|
expect(
|
|
importedCorruptPayload.isError,
|
|
JSON.stringify(importedCorruptPayload),
|
|
).not.toBe(true);
|
|
const retainedBefore = await call("get_evidence_bundle", {});
|
|
const retainedFailure = await call("match_native_functions", {
|
|
left: {
|
|
kind: "retained-evidence",
|
|
evidence_id: badCoordinate.evidence_id,
|
|
},
|
|
right: { kind: "retained-evidence", evidence_id: right.evidence_id },
|
|
detail: "complete",
|
|
});
|
|
expect(parseMcpToolError(retainedFailure).error).toMatchObject({
|
|
code: "evidence_integrity_mismatch",
|
|
details: {
|
|
reason: expect.stringContaining("Evidence validation failed"),
|
|
},
|
|
});
|
|
expect((await call("get_evidence_bundle", {})).structuredContent).toEqual(
|
|
retainedBefore.structuredContent,
|
|
);
|
|
|
|
const malformedOuterRequest = await call("match_native_functions", {
|
|
right,
|
|
detail: "complete",
|
|
});
|
|
expect(parseMcpToolError(malformedOuterRequest).error.code).toBe(
|
|
"invalid_request",
|
|
);
|
|
expect((await call("get_evidence_bundle", {})).structuredContent).toEqual(
|
|
retainedBefore.structuredContent,
|
|
);
|
|
},
|
|
60000,
|
|
);
|
|
|
|
cliTest(
|
|
"native matching CLI inline and portable sources report the same validation errors",
|
|
async ({ cli }) => {
|
|
const fixture = await createMatchingFixture();
|
|
const { left, right, leftDetails, root } = fixture;
|
|
const badCoordinate = nativeMatchingInventory("1", [
|
|
{ address: "not-an-address", value: "main" },
|
|
]);
|
|
const corruptBundlePath = join(root, "corrupt-source.json");
|
|
await writeFile(
|
|
corruptBundlePath,
|
|
JSON.stringify(createEvidenceBundle([badCoordinate, right])),
|
|
);
|
|
const requestPath = join(root, "wrong-role-request.json");
|
|
const portablePath = join(root, "wrong-role-sources.json");
|
|
const wrongRole = leftDetails[0]!;
|
|
const inlineRequest = { left: wrongRole, right, detail: "complete" };
|
|
const portableRequest = {
|
|
left: { kind: "retained-evidence", evidence_id: wrongRole.evidence_id },
|
|
right: { kind: "retained-evidence", evidence_id: right.evidence_id },
|
|
detail: "complete",
|
|
};
|
|
await writeFile(requestPath, JSON.stringify(inlineRequest));
|
|
const inline = await cli.run({
|
|
arguments: ["match-native-functions", requestPath, "--json"],
|
|
});
|
|
await writeFile(
|
|
portablePath,
|
|
JSON.stringify(createEvidenceBundle([wrongRole, right])),
|
|
);
|
|
await writeFile(requestPath, JSON.stringify(portableRequest));
|
|
const portable = await cli.run({
|
|
arguments: [
|
|
"match-native-functions",
|
|
requestPath,
|
|
"--source_bundle",
|
|
portablePath,
|
|
"--json",
|
|
],
|
|
});
|
|
expect(inline.exitCode).not.toBe(0);
|
|
expect(portable.exitCode).toBe(inline.exitCode);
|
|
const wrongRoleError = {
|
|
code: "invalid_request",
|
|
details: {
|
|
issues: [expect.objectContaining({ path: ["left"] })],
|
|
},
|
|
};
|
|
expect(inline.json).toMatchObject(wrongRoleError);
|
|
expect(portable.json).toMatchObject(wrongRoleError);
|
|
|
|
const corruptRequest = { left: badCoordinate, right, detail: "complete" };
|
|
await writeFile(requestPath, JSON.stringify(corruptRequest));
|
|
const corruptInline = await cli.run({
|
|
arguments: ["match-native-functions", requestPath, "--json"],
|
|
});
|
|
const corruptPortableRequest = {
|
|
left: {
|
|
kind: "retained-evidence",
|
|
evidence_id: badCoordinate.evidence_id,
|
|
},
|
|
right: { kind: "retained-evidence", evidence_id: right.evidence_id },
|
|
detail: "complete",
|
|
};
|
|
await writeFile(requestPath, JSON.stringify(corruptPortableRequest));
|
|
const corruptPortable = await cli.run({
|
|
arguments: [
|
|
"match-native-functions",
|
|
requestPath,
|
|
"--source_bundle",
|
|
corruptBundlePath,
|
|
"--json",
|
|
],
|
|
});
|
|
expect(corruptInline.json).toMatchObject({
|
|
code: "evidence_integrity_mismatch",
|
|
});
|
|
expect(corruptPortable.json).toMatchObject({
|
|
code: "evidence_integrity_mismatch",
|
|
});
|
|
|
|
const malformedBundlePath = join(root, "malformed-outer-frame.json");
|
|
await writeFile(malformedBundlePath, JSON.stringify({ records: [left] }));
|
|
const malformedBundle = await cli.run({
|
|
arguments: [
|
|
"match-native-functions",
|
|
requestPath,
|
|
"--source_bundle",
|
|
malformedBundlePath,
|
|
"--json",
|
|
],
|
|
});
|
|
expect(malformedBundle.exitCode).not.toBe(0);
|
|
expect(malformedBundle.json).toMatchObject({
|
|
code: "evidence_integrity_mismatch",
|
|
});
|
|
},
|
|
60000,
|
|
);
|
|
|
|
cliTest(
|
|
"native matching admits the request representation before authenticating observation carriers",
|
|
async () => {
|
|
const { call } = await connectLocalToolsMcp();
|
|
const left = nativeMatchingInventory("1", [
|
|
{ address: "0x10", value: "main" },
|
|
]);
|
|
const right = nativeMatchingInventory("2", [
|
|
{ address: "0x20", value: "main" },
|
|
]);
|
|
const before = await call("get_evidence_bundle", {});
|
|
const observations = Array.from({ length: 524288 }, () => ({}));
|
|
const response = await call("match_native_functions", {
|
|
left,
|
|
right,
|
|
left_observations: observations,
|
|
detail: "complete",
|
|
});
|
|
expect(parseMcpToolError(response).error).toMatchObject({
|
|
code: "resource_constraint",
|
|
details: {
|
|
operation: "match_native_functions",
|
|
resource: "capacity",
|
|
},
|
|
});
|
|
expect((await call("get_evidence_bundle", {})).structuredContent).toEqual(
|
|
before.structuredContent,
|
|
);
|
|
},
|
|
60000,
|
|
);
|