mirror of
https://github.com/morluto/rea.git
synced 2026-10-12 02:19:24 +00:00
Deliver complete Evidence directly through MCP with required nullable analysis profiles and immutable per-server delivery settings. Bind selected environments at construction and require typed cleanup outcomes with retained retry ownership. Remove test-only routing, duplicate session and snapshot metadata, producer-field repairs, and ambiguous provider arguments. Separate artifact byte identity from occurrence classification and retain typed source-map and partial native facts across CLI, MCP, and verifier consumers. BREAKING CHANGE: obsolete Evidence envelopes, session aliases, incomplete snapshots, and missing normalized producer facts are no longer accepted.
182 lines
5.4 KiB
JavaScript
182 lines
5.4 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { requireMcpEvidenceResult } from "./mcp-verifier-results.mjs";
|
|
|
|
export const verifyHopperFunctionBasics = async (
|
|
client,
|
|
options,
|
|
procedure,
|
|
) => {
|
|
const containment = requireMcpEvidenceResult(
|
|
await client.callTool(
|
|
{
|
|
name: "resolve_containing_procedure",
|
|
arguments: { address: procedure },
|
|
},
|
|
options,
|
|
),
|
|
"resolve_containing_procedure",
|
|
);
|
|
if (
|
|
containment?.found !== true ||
|
|
containment.procedure?.address !== procedure
|
|
) {
|
|
throw new Error(
|
|
"resolve_containing_procedure returned the wrong procedure",
|
|
);
|
|
}
|
|
|
|
const references = requireMcpEvidenceResult(
|
|
await client.callTool(
|
|
{
|
|
name: "procedure_references",
|
|
arguments: {
|
|
procedure,
|
|
direction: "outgoing",
|
|
},
|
|
},
|
|
options,
|
|
),
|
|
"procedure_references",
|
|
);
|
|
if (!Array.isArray(references?.references)) {
|
|
throw new Error("procedure_references returned an invalid result");
|
|
}
|
|
|
|
const instructions = requireMcpEvidenceResult(
|
|
await client.callTool(
|
|
{
|
|
name: "read_function_instructions",
|
|
arguments: { procedure },
|
|
},
|
|
options,
|
|
),
|
|
"read_function_instructions",
|
|
);
|
|
if (
|
|
instructions?.procedure?.address !== procedure ||
|
|
!Array.isArray(instructions.instructions) ||
|
|
instructions.instructions.length === 0
|
|
) {
|
|
throw new Error("read_function_instructions returned an invalid result");
|
|
}
|
|
|
|
return {
|
|
outgoingReferenceCount: references.references.length,
|
|
instructionWindowCount: instructions.instructions.length,
|
|
};
|
|
};
|
|
|
|
/** Check native terminal call sites across instruction, reference and dossier projections. */
|
|
export async function verifyHopperTerminalInstructions(call) {
|
|
const procedures = await call("list_procedures");
|
|
const selected = procedures.filter(
|
|
({ value }) =>
|
|
value === "-[REAWidget delegate]" ||
|
|
value.startsWith("imp___stubs__") ||
|
|
value.startsWith("_objc_msgSend$"),
|
|
);
|
|
if (selected.length === 0)
|
|
throw new Error("Objective-C fixture omitted terminal-call procedures");
|
|
let terminalCalls = 0;
|
|
for (const { address } of selected) {
|
|
const references = await call("procedure_references", {
|
|
procedure: address,
|
|
direction: "outgoing",
|
|
});
|
|
const nativeCalls = references.references.filter(
|
|
(edge) => edge.call !== undefined,
|
|
);
|
|
if (nativeCalls.length === 0)
|
|
throw new Error(
|
|
`Native fixture procedure ${address} omitted its terminal call`,
|
|
);
|
|
const dossier = await call("analyze_function", { procedure: address });
|
|
const instructions = await call("read_function_instructions", {
|
|
procedure: address,
|
|
});
|
|
const assembly = await call("procedure_assembly", { procedure: address });
|
|
const info = await call("procedure_info", { procedure: address });
|
|
for (const edge of nativeCalls) {
|
|
const observed = dossier.outgoing_references.find(
|
|
(item) =>
|
|
item.source_address === edge.source_address &&
|
|
item.target_address === edge.target_address,
|
|
);
|
|
assert.ok(
|
|
observed,
|
|
`Dossier discarded native call ${edge.source_address} -> ${edge.target_address}`,
|
|
);
|
|
assert.deepEqual(observed.call, edge.call);
|
|
assert.ok(
|
|
instructions.instructions.some((line) =>
|
|
line.startsWith(`${edge.source_address}:`),
|
|
),
|
|
`Instruction inventory discarded native call site ${edge.source_address}`,
|
|
);
|
|
assert.ok(
|
|
dossier.basic_blocks.some(
|
|
({ start, end }) =>
|
|
BigInt(start) <= BigInt(edge.source_address) &&
|
|
BigInt(edge.source_address) < BigInt(end),
|
|
),
|
|
`Normalized block excluded native call site ${edge.source_address}`,
|
|
);
|
|
terminalCalls++;
|
|
}
|
|
assert.equal(assembly, instructions.instructions.join("\n"));
|
|
assert.deepEqual(dossier.assembly, instructions.instructions);
|
|
if (address === selected[0].address)
|
|
await verifyHopperTerminalComment(
|
|
call,
|
|
address,
|
|
nativeCalls.at(-1).source_address,
|
|
);
|
|
const expectedLength = dossier.basic_blocks.reduce(
|
|
(total, { start, end }) => total + Number(BigInt(end) - BigInt(start)),
|
|
0,
|
|
);
|
|
assert.equal(
|
|
info.length,
|
|
expectedLength,
|
|
"Procedure length disagrees with normalized native blocks",
|
|
);
|
|
}
|
|
if (terminalCalls === 0)
|
|
throw new Error("Objective-C fixture omitted native terminal calls");
|
|
return {
|
|
procedures: selected.length,
|
|
terminalCalls,
|
|
normalizedBlockEndpoints: true,
|
|
terminalCommentPreserved: true,
|
|
};
|
|
}
|
|
|
|
async function verifyHopperTerminalComment(call, address, terminal) {
|
|
const original = await call("comment", { address: terminal });
|
|
try {
|
|
await call("set_comment", {
|
|
address: terminal,
|
|
comment: "REA terminal instruction comment",
|
|
});
|
|
const annotated = await call("analyze_function", {
|
|
procedure: address,
|
|
});
|
|
if (
|
|
!annotated.comments.some(
|
|
(item) =>
|
|
item.address === terminal &&
|
|
item.kind === "comment" &&
|
|
item.text === "REA terminal instruction comment",
|
|
)
|
|
)
|
|
throw new Error(
|
|
"Dossier discarded a comment on the native terminal instruction",
|
|
);
|
|
} finally {
|
|
await call("set_comment", {
|
|
address: terminal,
|
|
comment: original ?? "",
|
|
});
|
|
}
|
|
}
|