mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
fix(mcp): return complete inline Evidence records (#585)
Return complete canonical Evidence records inline, including normalized_result, and preserve direct comparison and retained bundle identity. Verified full deterministic gate, real Linux Ghidra CLI/MCP and independently installed artifact, generic package acceptance, private local samples, and targeted updates after syncing main. Closes #551 Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
This commit is contained in:
@@ -9,7 +9,7 @@ import { parseEvidence } from "../../dist/domain/evidence.js";
|
||||
import { analysisProfileSchema } from "../../dist/domain/analysisProfile.js";
|
||||
import { nativeValueTraceSchema } from "../../dist/domain/nativeValueTrace.js";
|
||||
|
||||
const entrypoint = fileURLToPath(new URL("../rea.mjs", import.meta.url));
|
||||
const defaultEntrypoint = fileURLToPath(new URL("../rea.mjs", import.meta.url));
|
||||
const environment = () => ({
|
||||
PATH: process.env.PATH ?? "/usr/bin:/bin",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
@@ -67,7 +67,12 @@ const verifyGraph = (input, target, procedure, globalAddress) => {
|
||||
};
|
||||
|
||||
/** Verify real Ghidra dependency composition through production CLI and stdio MCP. */
|
||||
export async function verifyNativeValueE2e(target, procedure, globalAddress) {
|
||||
export async function verifyNativeValueE2e(
|
||||
target,
|
||||
procedure,
|
||||
globalAddress,
|
||||
{ entrypoint = defaultEntrypoint } = {},
|
||||
) {
|
||||
const { stdout } = await promisify(execFile)(
|
||||
process.execPath,
|
||||
[
|
||||
@@ -142,10 +147,7 @@ export async function verifyNativeValueE2e(target, procedure, globalAddress) {
|
||||
{ timeout: 180000 },
|
||||
);
|
||||
assert.notEqual(result.isError, true, JSON.stringify(result));
|
||||
const mcpEvidence = parseEvidence({
|
||||
...result.structuredContent?.evidence,
|
||||
normalized_result: result.structuredContent?.result,
|
||||
});
|
||||
const mcpEvidence = parseEvidence(result.structuredContent?.evidence);
|
||||
assert.equal(mcpEvidence.operation, "trace_native_values");
|
||||
assert.equal(mcpEvidence.provider.id, "rea-workflow");
|
||||
assert.deepEqual(mcpEvidence.analysis_profile, workflowProfile);
|
||||
@@ -157,6 +159,66 @@ export async function verifyNativeValueE2e(target, procedure, globalAddress) {
|
||||
);
|
||||
assert.deepEqual(mcpEvidence.normalized_result, mcpGraph);
|
||||
assert.deepEqual(mcpGraph, cliGraph);
|
||||
const analyzed = await client.callTool(
|
||||
{ name: "analyze_function", arguments: { procedure } },
|
||||
{ timeout: 180000 },
|
||||
);
|
||||
assert.notEqual(analyzed.isError, true, JSON.stringify(analyzed));
|
||||
const functionEvidence = parseEvidence(
|
||||
analyzed.structuredContent?.evidence,
|
||||
);
|
||||
assert.equal(functionEvidence.operation, "analyze_function");
|
||||
assert.equal(functionEvidence.provider.id, "rea-workflow");
|
||||
const functionProfile = analysisProfileSchema.parse(
|
||||
functionEvidence.analysis_profile,
|
||||
);
|
||||
assert.deepEqual(
|
||||
functionProfile.parameters.upstream_analysis_profile,
|
||||
upstreamProfile,
|
||||
);
|
||||
assert.deepEqual(
|
||||
functionEvidence.normalized_result,
|
||||
analyzed.structuredContent?.result,
|
||||
);
|
||||
const comparison = await client.callTool(
|
||||
{
|
||||
name: "compare_functions",
|
||||
arguments: {
|
||||
left: analyzed.structuredContent.evidence,
|
||||
right: analyzed.structuredContent.evidence,
|
||||
},
|
||||
},
|
||||
{ timeout: 180000 },
|
||||
);
|
||||
assert.notEqual(comparison.isError, true, JSON.stringify(comparison));
|
||||
const comparisonEvidence = parseEvidence(
|
||||
comparison.structuredContent?.evidence,
|
||||
);
|
||||
assert.equal(comparisonEvidence.normalized_result.status, "unchanged");
|
||||
assert.deepEqual(
|
||||
comparisonEvidence.normalized_result,
|
||||
comparison.structuredContent?.result,
|
||||
);
|
||||
assert.ok(
|
||||
comparisonEvidence.evidence_links.includes(functionEvidence.evidence_id),
|
||||
);
|
||||
const bundle = await client.callTool({
|
||||
name: "get_evidence_bundle",
|
||||
arguments: {},
|
||||
});
|
||||
assert.notEqual(bundle.isError, true, JSON.stringify(bundle));
|
||||
for (const evidence of [
|
||||
mcpEvidence,
|
||||
functionEvidence,
|
||||
comparisonEvidence,
|
||||
]) {
|
||||
assert.deepEqual(
|
||||
bundle.structuredContent?.result?.records.find(
|
||||
(record) => record.evidence_id === evidence.evidence_id,
|
||||
),
|
||||
evidence,
|
||||
);
|
||||
}
|
||||
const closed = await client.callTool({
|
||||
name: "close_binary",
|
||||
arguments: {},
|
||||
@@ -175,6 +237,8 @@ export async function verifyNativeValueE2e(target, procedure, globalAddress) {
|
||||
mocked: false,
|
||||
cli: true,
|
||||
stdio_mcp: true,
|
||||
inline_evidence: true,
|
||||
direct_function_comparison: true,
|
||||
nodes: cliGraph.total_nodes,
|
||||
edges: cliGraph.total_edges,
|
||||
decompilations: cliGraph.decompilations,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { join } from "node:path";
|
||||
import { execFile } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
@@ -5,6 +6,7 @@ import { promisify } from "node:util";
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { MCP_STARTUP_POLICY } from "../dist/mcpStartupPolicy.js";
|
||||
import { parseEvidence } from "../dist/domain/evidence.js";
|
||||
import * as prompts from "./verify-package-prompts.mjs";
|
||||
import { json, verifyCompleteToolCatalog } from "./lib/verify-package-core.mjs";
|
||||
|
||||
@@ -155,8 +157,77 @@ const verifyMcpEvidenceBundle = async (client, mcpOptions, evidenceRoot) => {
|
||||
throw new Error("packaged MCP evidence import failed");
|
||||
};
|
||||
|
||||
/** Connect to the packaged MCP server and exercise the target-free catalog. */
|
||||
export async function verifyPackageMcp({ cli, environment, evidenceRoot }) {
|
||||
const verifyMcpInlineArtifactEvidence = async (
|
||||
client,
|
||||
mcpOptions,
|
||||
artifactArchive,
|
||||
) => {
|
||||
const opened = await client.callTool(
|
||||
{ name: "open_binary", arguments: { path: artifactArchive } },
|
||||
mcpOptions,
|
||||
);
|
||||
assert.notEqual(opened.isError, true, JSON.stringify(opened));
|
||||
try {
|
||||
const inspected = await client.callTool(
|
||||
{ name: "inspect_artifact", arguments: {} },
|
||||
mcpOptions,
|
||||
);
|
||||
assert.notEqual(inspected.isError, true, JSON.stringify(inspected));
|
||||
const source = parseEvidence(inspected.structuredContent?.evidence);
|
||||
assert.deepEqual(
|
||||
source.normalized_result,
|
||||
inspected.structuredContent?.result,
|
||||
);
|
||||
assert.deepEqual(
|
||||
json(prompts.mcpText(inspected)),
|
||||
inspected.structuredContent,
|
||||
);
|
||||
const compared = await client.callTool(
|
||||
{
|
||||
name: "compare_artifacts",
|
||||
arguments: {
|
||||
left: inspected.structuredContent.evidence,
|
||||
right: inspected.structuredContent.evidence,
|
||||
},
|
||||
},
|
||||
mcpOptions,
|
||||
);
|
||||
assert.notEqual(compared.isError, true, JSON.stringify(compared));
|
||||
const comparison = parseEvidence(compared.structuredContent?.evidence);
|
||||
assert.deepEqual(
|
||||
comparison.normalized_result,
|
||||
compared.structuredContent?.result,
|
||||
);
|
||||
assert.ok(comparison.evidence_links.includes(source.evidence_id));
|
||||
const bundle = await client.callTool(
|
||||
{ name: "get_evidence_bundle", arguments: {} },
|
||||
mcpOptions,
|
||||
);
|
||||
assert.notEqual(bundle.isError, true, JSON.stringify(bundle));
|
||||
for (const evidence of [source, comparison]) {
|
||||
assert.deepEqual(
|
||||
bundle.structuredContent?.result?.records.find(
|
||||
(record) => record.evidence_id === evidence.evidence_id,
|
||||
),
|
||||
evidence,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
const closed = await client.callTool(
|
||||
{ name: "close_binary", arguments: {} },
|
||||
mcpOptions,
|
||||
);
|
||||
assert.notEqual(closed.isError, true, JSON.stringify(closed));
|
||||
}
|
||||
};
|
||||
|
||||
/** Connect to the packaged MCP server and verify catalog and Evidence composition. */
|
||||
export async function verifyPackageMcp({
|
||||
cli,
|
||||
environment,
|
||||
evidenceRoot,
|
||||
artifactArchive,
|
||||
}) {
|
||||
const diagnosed = json(
|
||||
(
|
||||
await execute(cli, ["mcp", "doctor", "--json"], {
|
||||
@@ -191,6 +262,7 @@ export async function verifyPackageMcp({ cli, environment, evidenceRoot }) {
|
||||
await verifyMcpTargetFree(client, mcpOptions);
|
||||
await verifyMcpUnknownProvider(client, mcpOptions);
|
||||
await verifyMcpBinaryLifecycle(client, mcpOptions);
|
||||
await verifyMcpInlineArtifactEvidence(client, mcpOptions, artifactArchive);
|
||||
await verifyMcpEvidenceBundle(client, mcpOptions, evidenceRoot);
|
||||
} catch (cause) {
|
||||
throw new Error(`packaged MCP smoke failed: ${mcpStderr}`, { cause });
|
||||
|
||||
@@ -84,7 +84,7 @@ try {
|
||||
cli,
|
||||
environment: environmentData.environment,
|
||||
});
|
||||
await verifyPackageArtifactAndElectron({
|
||||
const { artifactArchive } = await verifyPackageArtifactAndElectron({
|
||||
cli,
|
||||
workspace,
|
||||
environment: environmentData.environment,
|
||||
@@ -131,6 +131,7 @@ try {
|
||||
cli,
|
||||
environment: environmentData.environment,
|
||||
evidenceRoot,
|
||||
artifactArchive,
|
||||
});
|
||||
process.stdout.write(
|
||||
`${JSON.stringify({ verifier_run: await completeVerifierRun(verifierRun), cli: true, analysisCli: true, artifactCli: true, managedCli: true, managedReconstructionCli: true, managedNativeVerificationCli: true, managedApplicationGraphCli: true, evidenceCli: true, incurMcpCommand: PRODUCT_IDENTITY.mcpCommand, lifecycleScriptsRequired: false, doctor: "platform-appropriate", setup: supportedSetupHost ? "planned-then-idempotent" : "unsupported-host-rejected", setupPlanReadOnly: supportedSetupHost, existingHopperPreserved: supportedSetupHost, clients: supportedSetupHost ? 4 : 0, backupReadback: supportedSetupHost, failureRecovery: supportedSetupHost, configSymlinkLifecycle: supportedSetupHost, skill: supportedSetupHost, skillReferences: supportedSetupHost, mcpTools: TOOL_CONTRACTS.length, mcpPrompts: prompts.names.length, promptCompletion: true, promptCompletionLifecycle: true, evidenceMcp: true, targetFree: true, targetLifecycle: true, boundedRegexBridge: true, update, mcpStartup, mcpModuleLoading })}\n`,
|
||||
|
||||
Reference in New Issue
Block a user