refactor(verify): give the javascript verifiers' roots to VerifierFixture (#11068)

* refactor(verify): give the javascript verifiers' roots to VerifierFixture

VerifierFixture.retain(reason) keeps a root that an owned command whose
cleanup is incomplete may still use, replacing each verifier's local
retain flag and cleanup closure.

* refactor(verify): give the Ghidra COM, DOS, 6502, NativeAOT and raw verifiers' workspaces to VerifierFixture (#11070)

* refactor(verify): give the Ghidra COM, DOS, 6502, NativeAOT and raw verifiers' workspaces to VerifierFixture

* refactor(verify): keep the Ghidra decompiler-failure, decompile-timeout and no-return workspaces through VerifierFixture until owned cleanup is verified (#11077)
This commit is contained in:
morluto authored and GitHub committed 2026-11-15 19:52:38 +08:00
1 parent ecab89f3e6
commit 47d14d0fb8
17 files changed
+329 -295

No files matched your search

+10 -18
View File
@@ -11,7 +11,7 @@ import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { artifactCliEvidence } from "../../lib/artifact-e2e.mjs";
import { withVerifierCleanup } from "../../lib/verifier-cleanup.mjs";
import { PrivateRuntimeRoot } from "../../../dist/process/PrivateRuntimeRoot.js";
import { VerifierFixture } from "../../lib/verifier-fixture.mjs";
import {
OwnedCommandFailure,
runOwnedCommand,
@@ -82,7 +82,6 @@ const normalize = (rows) =>
),
}));
let temporary;
let retainTemporary = false;
await withVerifierCleanup(async () => {
await client.connect(transport);
transport.stderr?.resume();
@@ -266,7 +265,7 @@ await withVerifierCleanup(async () => {
}
let cliParity = null;
if (values["cli-parity"]) {
temporary = await PrivateRuntimeRoot.create({
temporary = await VerifierFixture.open({
prefix: "rea-module-reference-verifier-",
});
const bundlePath = join(temporary.path, "bundle.json");
@@ -312,7 +311,7 @@ await withVerifierCleanup(async () => {
}
let tokenCosts = null;
if (values["tokenizer-python"]) {
temporary ??= await PrivateRuntimeRoot.create({
temporary ??= await VerifierFixture.open({
prefix: "rea-module-reference-verifier-",
});
const input = join(temporary.path, "token-input.json");
@@ -357,9 +356,13 @@ print(json.dumps(dict(tokenizer='cl100k_base',tiktoken_version=tiktoken.__versio
// An incomplete cleanup carries its supervisor and identity resources in
// the error. Preserve its private files for that owner instead of erasing
// recovery data while a command may still be using it.
retainTemporary =
if (
cause instanceof OwnedCommandFailure &&
cause.cleanupOwner !== undefined;
cause.cleanupOwner !== undefined
)
temporary.retain(
`Command cleanup is incomplete; retaining its private input/Evidence files at ${temporary.path}.`,
);
throw new Error(
"Token accounting failed; inspect the owned-command outcome and cleanup status.",
{ cause },
@@ -392,15 +395,4 @@ print(json.dumps(dict(tokenizer='cl100k_base',tiktoken_version=tiktoken.__versio
"First query is after shared artifact analysis; subsequent queries share one MCP session. Legacy item reads are serial; a concurrent legacy baseline is unmeasured. Filesystem/build warmness uncontrolled. Token costs count one post-change catalog, preparation request/summary and each request/structured reply once; client rendering, replay, reasoning, final answer and model latency are unmeasured. Preparation and parity/tokenization overhead are outside per-query elapsed times; external family-resource receipts include the complete verifier and children.",
})}\n`,
);
}, [
() => client.close(),
() => transport.close(),
async () => {
if (temporary === undefined) return;
if (retainTemporary)
throw new Error(
`Command cleanup is incomplete; retaining its private input/Evidence files at ${temporary.path}.`,
);
await temporary.close();
},
]);
}, [() => client.close(), () => transport.close(), () => temporary?.release()]);
+11 -17
View File
@@ -11,7 +11,7 @@ import { parseArgs } from "node:util";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { createEvidence } from "../../../dist/domain/evidence.js";
import { PrivateRuntimeRoot } from "../../../dist/process/PrivateRuntimeRoot.js";
import { VerifierFixture } from "../../lib/verifier-fixture.mjs";
import {
OwnedCommandFailure,
runOwnedCommand,
@@ -124,7 +124,6 @@ const simulatedNativeEvidence = (path, sha256) =>
},
);
let temporary;
let retainTemporary = false;
let report;
const ownedCommand = async (command, arguments_, timeoutMs = 60_000) => {
try {
@@ -141,8 +140,13 @@ const ownedCommand = async (command, arguments_, timeoutMs = 60_000) => {
{ timeoutMs, diagnosticBytes: 16 * 1024 * 1024 },
);
} catch (cause) {
retainTemporary ||=
cause instanceof OwnedCommandFailure && cause.cleanupOwner !== undefined;
if (
cause instanceof OwnedCommandFailure &&
cause.cleanupOwner !== undefined
)
temporary.retain(
`Command cleanup is incomplete; retaining its private inputs at ${temporary.path}.`,
);
throw cause;
}
};
@@ -314,7 +318,7 @@ await withVerifierCleanup(async () => {
}
let cliParity = null;
if (values["cli-parity"]) {
temporary = await PrivateRuntimeRoot.create({
temporary = await VerifierFixture.open({
prefix: "rea-native-handoff-verifier-",
});
const bundlePath = join(temporary.path, "bundle.json");
@@ -381,7 +385,7 @@ await withVerifierCleanup(async () => {
);
let tokenCosts = null;
if (values["tokenizer-python"]) {
temporary ??= await PrivateRuntimeRoot.create({
temporary ??= await VerifierFixture.open({
prefix: "rea-native-handoff-verifier-",
});
const input = join(temporary.path, "token-input.json");
@@ -461,15 +465,5 @@ print(json.dumps(dict(tokenizer='cl100k_base',tiktoken_version=tiktoken.__versio
assert.ok(safeIdentity, "Native identity/linkage oracle failed");
assert.ok(safeApplicability, "File analysis applicability oracle failed");
}
}, [
() => client.close(),
() => transport.close(),
async () => {
if (retainTemporary)
throw new Error(
`Command cleanup incomplete; retaining owned inputs at ${temporary?.path}.`,
);
await temporary?.close();
},
]);
}, [() => client.close(), () => transport.close(), () => temporary?.release()]);
console.log(serialize(report));
+21 -12
View File
@@ -5,7 +5,7 @@ import { once } from "node:events";
import { createInterface } from "node:readline";
import { Writable } from "node:stream";
import { createReadStream, createWriteStream } from "node:fs";
import { mkdtemp, rm, stat, statfs } from "node:fs/promises";
import { stat, statfs } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { finished } from "node:stream/promises";
@@ -16,6 +16,10 @@ import { estimateTokenCount } from "tokenx";
import { parseCliOutputArguments } from "../../../dist/cliOutput.js";
import { createCliResultOutput } from "../../../dist/cli/cliResultOutput.js";
import { createEvidence } from "../../../dist/domain/evidence.js";
import {
VerifierFixture,
withVerifierFixture,
} from "../../lib/verifier-fixture.mjs";
// This opt-in format-boundary check writes one temporary file larger than the
// actual engine string limit. Shared leaves keep the analysis input small.
@@ -29,7 +33,10 @@ assert.ok(
volume.bavail * volume.bsize > count * encodedLeaf.length + 1024 ** 3,
"Large-output verification needs room for one output file and a 1-GiB reserve",
);
const workspace = await mkdtemp(join(tmpdir(), "rea-javascript-output-"));
const fixture = await VerifierFixture.open({
prefix: "rea-javascript-output-",
});
const workspace = fixture.path;
const path = join(workspace, "result.json");
let destination;
let peakRss = process.memoryUsage().rss;
@@ -40,7 +47,17 @@ const sampler = setInterval(sampleRss, 25);
sampler.unref();
let finalReport;
try {
// The output stream writes under the root: it closes before the root is
// removed.
fixture.releaseAfter(async () => {
clearInterval(sampler);
if (destination !== undefined && !destination.closed) {
const closed = once(destination, "close");
destination.destroy();
await closed;
}
});
await withVerifierFixture(fixture, async () => {
const evidence = createEvidence(
undefined,
{ id: "large-output-fixture", name: "Large output fixture", version: "1" },
@@ -189,13 +206,5 @@ try {
evidence_id: evidence.evidence_id,
rss: { method: "node-memoryUsage-rss-sampled", peak_bytes: peakRss },
};
} finally {
clearInterval(sampler);
if (destination !== undefined && !destination.closed) {
const closed = once(destination, "close");
destination.destroy();
await closed;
}
await rm(workspace, { recursive: true, force: true });
}
});
process.stdout.write(`${JSON.stringify(finalReport)}\n`);
+14 -16
View File
@@ -1,13 +1,12 @@
#!/usr/bin/env node
import {
closeVerifierResources,
withVerifierCleanup,
} from "../../lib/verifier-cleanup.mjs";
VerifierFixture,
withVerifierFixture,
} from "../../lib/verifier-fixture.mjs";
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { readFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
@@ -26,7 +25,10 @@ if (!process.env.REA_WAKARU_COMMAND)
);
const entrypoint =
process.argv[2] ?? fileURLToPath(new URL("../../rea.mjs", import.meta.url));
const root = await mkdtemp(join(tmpdir(), "rea-real-recovery-"));
const rootFixture = await VerifierFixture.open({
prefix: "rea-real-recovery-",
});
const root = rootFixture.path;
const environment = { ...process.env, REA_LOG_LEVEL: "silent" };
const transport = new StdioClientTransport({
command: process.execPath,
@@ -55,7 +57,11 @@ const call = async (name, args) => {
const fingerprint = (bytes) => createHash("sha256").update(bytes).digest("hex");
const counts = [];
let finalReport;
await withVerifierCleanup(async () => {
// The MCP server reads the built fixtures and writes recovered output under
// the root: the client and its transport close before the root is removed.
rootFixture.releaseAfter(() => client.close());
rootFixture.releaseAfter(() => transport.close());
await withVerifierFixture(rootFixture, async () => {
const fixtures = await buildRecoveryFixtures(
root,
process.env.REA_JAVASCRIPT_FIXTURE_TOOLS,
@@ -169,15 +175,7 @@ await withVerifierCleanup(async () => {
results: counts,
verified: true,
};
}, [
async () => {
await closeVerifierResources([
() => client.close(),
() => transport.close(),
]);
await rm(root, { recursive: true, force: true });
},
]);
});
console.log(JSON.stringify(finalReport));
async function assertRecoveredArtifacts(evidence, result, fixture) {
+10 -16
View File
@@ -10,7 +10,7 @@ import { fileURLToPath } from "node:url";
import { parseArgs } from "node:util";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { PrivateRuntimeRoot } from "../../../dist/process/PrivateRuntimeRoot.js";
import { VerifierFixture } from "../../lib/verifier-fixture.mjs";
import {
OwnedCommandFailure,
runOwnedCommand,
@@ -56,7 +56,6 @@ const client = new Client({
version: "1",
});
let temporary;
let retainTemporary = false;
let report;
const serialize = (value) => JSON.stringify(value);
const digest = (value) =>
@@ -76,8 +75,13 @@ const ownedCommand = async (command, arguments_) => {
{ timeoutMs: 120_000, diagnosticBytes: 16 * 1024 * 1024 },
);
} catch (cause) {
retainTemporary ||=
cause instanceof OwnedCommandFailure && cause.cleanupOwner !== undefined;
if (
cause instanceof OwnedCommandFailure &&
cause.cleanupOwner !== undefined
)
temporary.retain(
`Command cleanup is incomplete; retaining its private inputs at ${temporary.path}.`,
);
throw cause;
}
};
@@ -111,7 +115,7 @@ await withVerifierCleanup(async () => {
for await (const chunk of createReadStream(values.artifact))
artifactHash.update(chunk);
const artifactSha256 = artifactHash.digest("hex");
temporary = await PrivateRuntimeRoot.create({
temporary = await VerifierFixture.open({
prefix: "rea-retained-facts-verifier-",
});
await client.connect(transport);
@@ -343,15 +347,5 @@ await withVerifierCleanup(async () => {
conditions:
"Fresh MCP and input hashing (file cache warmed); build/OS caches uncontrolled. First export sample includes export plus caller projection code, later samples reuse that immutable export. View samples precede diagnostic export. Task token projections include preparation, first answer, selected/full name/description/inputSchema catalog and the exact baseline program; exec tool schema, host rendering, output schemas, reasoning and billing unmeasured. At-answer cgroup counters include observer/startup overhead, before repeats/export diagnostics/parity/tokenization; final external observer measures all validation work. No application or native provider executes.",
};
}, [
() => client.close(),
() => transport.close(),
async () => {
if (retainTemporary)
throw new Error(
`Command cleanup incomplete; retaining owned inputs at ${temporary?.path}.`,
);
await temporary?.close();
},
]);
}, [() => client.close(), () => transport.close(), () => temporary?.release()]);
console.log(serialize(report));