Merge remote-tracking branch 'origin/main' into eb

The environment-coverage sentence joins main's BEAM additions to the view description.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Edgar ArakelyanandClaude Opus 5.5 committed 2026-10-23 22:41:50 -07:00
commit 53a03c06a5
272 files changed
+21570 -3711

No files matched your search

-2
View File
@@ -28,7 +28,6 @@ const FIXTURE_SOURCES = [
"tests/conformance/swift/**",
"tests/conformance/versions/**",
];
const READINESS_SOURCES = ["tests/conformance/readiness/**"];
// These reporters affect CI output only; they do not select a host or provider
// verification lane when changed on their own.
const PORTABLE_CI_REPORTERS = new Set([
@@ -281,7 +280,6 @@ if (tier === "reused") {
selected.fixtures = true;
selected.readiness = true;
}
if (matchesAny(file, READINESS_SOURCES)) selected.readiness = true;
if (file.startsWith("tests/")) continue;
if (
/^(src\/process\/|src\/filesystem\/|src\/contracts\/tool|src\/server\/createServer|src\/cli\.ts|scripts\/ci\/)/.test(
+6
View File
@@ -2,6 +2,10 @@
// Shared build inputs are classified once in plan.mjs rather than repeated in
// every workflow.
export const SCOPES = {
readiness: [
"tests/conformance/readiness/**",
"scripts/verify-reconstruction-readiness.mjs",
],
reverse_tools: [
".github/workflows/mutation.yml",
"src/application/reverse/**",
@@ -141,6 +145,7 @@ export const SCOPES = {
"scripts/prepack.mjs",
"scripts/prepare.mjs",
"scripts/rea.mjs",
"scripts/lib/cli-process-environment.mjs",
"install.sh",
"src/config/**",
"src/application/Setup*.ts",
@@ -373,6 +378,7 @@ export const SCOPES = {
"src/contracts/toolContracts.ts",
"src/contracts/toolEffects.ts",
"scripts/rea.mjs",
"scripts/lib/cli-process-environment.mjs",
"src/process/**",
"src/artifacts/SafeOutputTree.ts",
"scripts/verify/javascript/recovery.mjs",
+7
View File
@@ -260,6 +260,13 @@ export const providerCatalog = (sources) => {
({ name }) => name === "inspect_pe_certificates",
),
},
{
identity: sources.artifactProviders.PE_IMPORTS_PROVIDER,
contracts:
sources.binaryDiagnosticsContracts.BINARY_DIAGNOSTICS_TOOL_CONTRACTS.filter(
({ name }) => name === "inspect_pe_imports",
),
},
{
identity: sources.artifactProviders.ELF_NOTES_PROVIDER,
contracts:
+26
View File
@@ -0,0 +1,26 @@
import { join } from "node:path";
/** Prepare the selected process environment before importing CLI or MCP dependencies. */
export const prepareCliProcessEnvironment = (
environment,
platform,
packageVersion,
) => {
environment.REA_PACKAGE_VERSION = packageVersion;
// Windows home lookup throws for an empty USERPROFILE. Dependencies resolve
// home during import, before REA's own resolver can apply its account fallback.
if (platform === "win32" && environment.USERPROFILE === "") {
delete environment.USERPROFILE;
}
// Playwright's registry assumes a desktop host while choosing its cache.
// Termux needs an explicit path before the registry initializes.
if (
platform === "android" &&
environment.PLAYWRIGHT_BROWSERS_PATH === undefined
) {
const cacheRoot =
environment.XDG_CACHE_HOME ?? join(environment.HOME ?? ".", ".cache");
environment.PLAYWRIGHT_BROWSERS_PATH = join(cacheRoot, "ms-playwright");
}
};
+35
View File
@@ -0,0 +1,35 @@
/** Extract one real procedure address from the complete inline inventory. */
export function firstProcedureAddress(input: unknown): string;
/** Reject empty and success-shaped embedded decompilation failures. */
export function requirePseudocode(input: unknown, operation: string): string;
/** Validate a complete Hopper function dossier semantically. */
export function requireFunctionDossier(
input: unknown,
expectedAddress: string,
): Record<string, unknown>;
/** Positive semantic facts required from one source-owned Hopper fixture. */
export interface FunctionDossierOracle {
readonly procedure_address: string;
readonly callee_address?: string;
readonly caller_address?: string;
readonly referenced_string?: string;
readonly referenced_name?: string;
readonly comment?: string;
readonly require_cfg_successor?: boolean;
readonly require_assembly?: boolean;
}
/** Reject a structurally valid dossier that omits fixture-proven semantics. */
export function requireFunctionDossierOracle(
input: unknown,
oracle: FunctionDossierOracle,
): Record<string, unknown>;
/** Require every relationship to be an actual hexadecimal address. */
export function requireAddressArray(
input: unknown,
operation: string,
): string[];
+191
View File
@@ -0,0 +1,191 @@
import { z } from "zod";
const addressSchema = z.string().regex(/^0x[0-9a-f]+$/iu);
const identitySchema = z.object({ address: addressSchema, name: z.string() });
/** Extract one real procedure address from the complete inline inventory. */
export const firstProcedureAddress = (input) => {
const first = z
.array(z.object({ address: addressSchema }))
.min(1)
.parse(input)[0];
if (first === undefined) throw new TypeError("Procedure inventory was empty");
return first.address;
};
/** Reject empty and success-shaped embedded decompilation failures. */
export const requirePseudocode = (input, operation) => {
if (typeof input !== "string" || input.trim().length === 0)
throw new TypeError(`${operation} returned empty pseudocode`);
if (input === "No output" || input.startsWith("Error:"))
throw new TypeError(`${operation} returned an embedded failure: ${input}`);
return input;
};
/** Validate a complete Hopper function dossier semantically. */
export const requireFunctionDossier = (input, expectedAddress) => {
const value = objectValue(
input,
"analyze_function returned no function dossier",
);
const procedure = identitySchema.parse(value.procedure);
if (procedure.address !== expectedAddress)
throw new TypeError("analyze_function returned the wrong procedure");
if (procedure.name.trim().length === 0)
throw new TypeError("analyze_function omitted the procedure name");
requirePseudocode(value.pseudocode, "analyze_function");
for (const field of COLLECTION_FIELDS) list(value, field);
for (const field of ["callers", "callees"])
z.array(identitySchema).parse(list(value, field));
validateBlocks(list(value, "basic_blocks"));
return value;
};
/** Reject a structurally valid dossier that omits fixture-proven semantics. */
export const requireFunctionDossierOracle = (input, oracle) => {
const value = requireFunctionDossier(input, oracle.procedure_address);
if (oracle.callee_address !== undefined) {
const callees = z.array(identitySchema).parse(list(value, "callees"));
if (!callees.some(({ address }) => address === oracle.callee_address))
throw new TypeError(
"analyze_function omitted the expected fixture callee",
);
const references = z
.array(
z.object({
source_address: addressSchema,
target_address: addressSchema,
}),
)
.parse(list(value, "outgoing_references"));
if (
!references.some(
({ target_address }) => target_address === oracle.callee_address,
)
)
throw new TypeError(
"analyze_function omitted the expected fixture reference",
);
}
if (oracle.caller_address !== undefined) {
const callers = z.array(identitySchema).parse(list(value, "callers"));
if (!callers.some(({ address }) => address === oracle.caller_address))
throw new TypeError(
"analyze_function omitted the expected fixture caller",
);
}
if (oracle.referenced_string !== undefined)
requireReferencedValue(
value,
"referenced_strings",
oracle.referenced_string,
(candidate) => candidate,
);
if (oracle.referenced_name !== undefined)
requireReferencedValue(
value,
"referenced_names",
oracle.referenced_name,
(candidate) => candidate.replace(/^_+/u, ""),
);
if (oracle.comment !== undefined) {
const comments = z
.array(
z.object({
address: addressSchema,
kind: z.enum(["comment", "inline"]),
text: z.string(),
}),
)
.parse(list(value, "comments"));
if (
!comments.some(
({ address, kind, text }) =>
address === oracle.procedure_address &&
kind === "comment" &&
text === oracle.comment,
)
)
throw new TypeError("analyze_function omitted the verifier comment");
}
if (oracle.require_assembly === true && list(value, "assembly").length === 0)
throw new TypeError("analyze_function omitted fixture assembly");
if (oracle.require_cfg_successor) {
const blocks = z
.array(z.object({ successors: z.array(addressSchema) }))
.parse(list(value, "basic_blocks"));
if (!blocks.some(({ successors }) => successors.length > 0))
throw new TypeError("analyze_function omitted a real CFG successor");
}
return value;
};
const referencedValueSchema = z.object({
address: addressSchema,
value: z.string(),
source_address: addressSchema,
});
const referenceEdgeSchema = z.object({
source_address: addressSchema,
target_address: addressSchema,
});
const requireReferencedValue = (dossier, field, expected, normalize) => {
const values = z.array(referencedValueSchema).parse(list(dossier, field));
const references = z
.array(referenceEdgeSchema)
.parse(list(dossier, "outgoing_references"));
const found = values.some(
({ address, value, source_address: sourceAddress }) =>
normalize(value) === expected &&
references.some(
({ source_address, target_address }) =>
source_address === sourceAddress && target_address === address,
),
);
if (!found)
throw new TypeError(
`analyze_function omitted the expected fixture ${field === "referenced_strings" ? "string" : "name"}`,
);
};
/** Require every relationship to be an actual hexadecimal address. */
export const requireAddressArray = (input, operation) => {
const parsed = z.array(addressSchema).parse(input);
if (parsed.some((address) => address.length === 0))
throw new TypeError(`${operation} returned an invalid address list`);
return parsed;
};
const COLLECTION_FIELDS = [
"assembly",
"comments",
"callers",
"callees",
"incoming_references",
"outgoing_references",
"referenced_strings",
"referenced_names",
"basic_blocks",
];
const list = (value, field) => z.array(z.unknown()).parse(value[field]);
const validateBlocks = (items) => {
for (const block of items)
if (!Array.isArray(objectValue(block, "Invalid basic block").successors))
throw new TypeError("analyze_function omitted CFG successor evidence");
};
const objectValue = (input, message) => {
const parsed = z.record(z.string(), z.unknown()).safeParse(input);
if (!parsed.success) throw new TypeError(message);
return parsed.data;
};
+1 -13
View File
@@ -1,17 +1,6 @@
import assert from "node:assert/strict";
/**
* `requireFunctionDossierOracle` is only used inside `verifyRealHopperFixture`,
* which runs against the real Hopper bridge after `build:cached`. Loading it
* lazily keeps this module importable from `test:fast` (which docs promise to
* run "without build" — see docs/testing.md § test scripts) so that mock-only
* callers such as `requireCurrentDocument` and `resolveFixtureProcedure` do
* not require `dist/`.
*/
const loadFunctionDossierOracle = () =>
import("../../dist/application/RealHopperAssertions.js").then(
(module) => module.requireFunctionDossierOracle,
);
import { requireFunctionDossierOracle } from "./real-hopper-assertions.mjs";
/** Require truthful tri-state permissions from real Hopper memory regions. */
export const requireTruthfulMemoryRegions = (segments) => {
@@ -99,7 +88,6 @@ export async function verifyRealHopperFixture({
oracle,
normalizedResult,
}) {
const requireFunctionDossierOracle = await loadFunctionDossierOracle();
const main = await resolveFixtureProcedure(
client,
options,
+5 -1
View File
@@ -53,7 +53,7 @@ export const releaseInventorySchema = z.object({
pullRequests: z.array(pullRequest),
});
export async function readReleaseHistory(run, base, target) {
export async function requireCompleteReleaseHistory(run) {
if (
(await run("git", ["rev-parse", "--is-shallow-repository"])).trim() !==
"false"
@@ -61,6 +61,10 @@ export async function readReleaseHistory(run, base, target) {
throw new Error(
"Release inventory requires complete Git history; fetch --unshallow first",
);
}
export async function readReleaseHistory(run, base, target) {
await requireCompleteReleaseHistory(run);
const resolve = async (ref) =>
sha.parse(
(
+6
View File
@@ -0,0 +1,6 @@
export function applyReleasePleasePatch(directory?: string): Promise<void>;
export function releaseProposalSearchDepth(
run: (command: string, args: string[]) => Promise<string>,
sourceSha: string,
): Promise<number>;
+61
View File
@@ -0,0 +1,61 @@
import { execFile } from "node:child_process";
import { readFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { dirname, relative } from "node:path";
import { promisify } from "node:util";
import { fileURLToPath } from "node:url";
import { requireCompleteReleaseHistory } from "./release-notes.mjs";
const execute = promisify(execFile);
const require = createRequire(import.meta.url);
const project = fileURLToPath(new URL("../../", import.meta.url));
const patch = fileURLToPath(
new URL("../../patches/release-please+17.3.0.patch", import.meta.url),
);
export async function applyReleasePleasePatch(
directory = dirname(require.resolve("release-please/package.json")),
) {
const metadata = JSON.parse(
await readFile(`${directory}/package.json`, "utf8"),
);
if (metadata.version !== "17.3.0") {
throw new Error(
"The release proposal patch requires release-please 17.3.0",
);
}
const args = ["--directory", relative(project, directory), patch];
const check = async (reverse) => {
try {
await execute(
"git",
["apply", "--check", ...(reverse ? ["--reverse"] : []), ...args],
{ cwd: project },
);
return true;
} catch (error) {
if (error.code === 1) return false;
throw error;
}
};
if (await check(false)) {
await execute("git", ["apply", ...args], { cwd: project });
} else if (!(await check(true))) {
throw new Error(
"The release proposal dependency patch does not match the installed source",
);
}
}
export async function releaseProposalSearchDepth(run, sourceSha) {
if (!/^[a-f0-9]{40}$/u.test(sourceSha ?? "")) {
throw new Error("Expected an exact release proposal source SHA");
}
await requireCompleteReleaseHistory(run);
const text = (await run("git", ["rev-list", "--count", sourceSha])).trim();
const count = Number(text);
if (!/^[1-9][0-9]*$/u.test(text) || !Number.isSafeInteger(count)) {
throw new Error("Git returned an invalid release history size");
}
return count;
}
+1
View File
@@ -45,6 +45,7 @@ if (await pathExists(join(root, "src"))) {
run(process.execPath, ["scripts/generate-skill-metadata.mjs", "--check"]);
} else {
for (const path of [
"scripts/lib/cli-process-environment.mjs",
"dist/main.js",
"dist/mcp/main.js",
"dist/cli-runtime/cli.js",
+8 -22
View File
@@ -1,9 +1,11 @@
#!/usr/bin/env node
import { access } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { prepareCliProcessEnvironment } from "./lib/cli-process-environment.mjs";
// Route production MCP before importing Incur. Incur owns registration helpers
// such as `mcp add`, while the MCP runtime serves the tool catalog over stdio
// or, with `--http`, over streamable HTTP.
@@ -11,7 +13,11 @@ const args = process.argv.slice(2);
const { default: packageJson } = await import("../package.json", {
with: { type: "json" },
});
process.env.REA_PACKAGE_VERSION = packageJson.version;
prepareCliProcessEnvironment(
process.env,
process.platform,
packageJson.version,
);
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const builtinArgumentRuntime = "dist/cli-runtime/cliBuiltinArguments.js";
const builtinArguments = (await compiledRuntimeExists([builtinArgumentRuntime]))
@@ -36,26 +42,6 @@ const isMcpHttpMode =
const SIGPIPE_EXIT_STATUS = 141;
// Set once the reader of CLI output has closed the pipe.
let stdoutClosed = false;
// Windows home lookup throws for an empty USERPROFILE. Dependencies resolve
// home during import, before REA's own resolver can apply its account fallback.
if (process.platform === "win32" && process.env.USERPROFILE === "") {
delete process.env.USERPROFILE;
}
// Playwright's registry assumes one of its three desktop host platforms when
// it chooses the browser cache directory. Termux reports `android`, although
// its Node runtime and filesystem are POSIX-compatible. Keep the cache path
// explicit so REA can start on Termux. Browser launch still requires a
// caller-supplied Android-compatible Chromium executable.
if (
process.platform === "android" &&
process.env.PLAYWRIGHT_BROWSERS_PATH === undefined
) {
const cacheRoot =
process.env.XDG_CACHE_HOME ?? join(process.env.HOME ?? ".", ".cache");
process.env.PLAYWRIGHT_BROWSERS_PATH = join(cacheRoot, "ms-playwright");
}
const runtimeFiles = [
builtinArgumentRuntime,
...(isMcpMode || isMcpHttpMode
-8
View File
@@ -1,15 +1,7 @@
import semver from "semver";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
export function releaseNpmTag(version) {
if (version === undefined || semver.valid(version) !== version)
throw new Error("Expected an exact SemVer version.");
return semver.prerelease(version) === null ? "latest" : "next";
}
if (
process.argv[1] !== undefined &&
resolve(process.argv[1]) === fileURLToPath(import.meta.url)
)
process.stdout.write(releaseNpmTag(process.argv[2]));
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env node
import { execFile } from "node:child_process";
import { appendFile } from "node:fs/promises";
import { parseArgs, promisify } from "node:util";
import {
applyReleasePleasePatch,
releaseProposalSearchDepth,
} from "./lib/release-proposal.mjs";
const execute = promisify(execFile);
async function main() {
const { values } = parseArgs({
options: {
"target-branch": { type: "string" },
"source-sha": { type: "string" },
},
});
const repository = process.env.GITHUB_REPOSITORY;
const token = process.env.GH_TOKEN;
if (
!repository ||
!/^[\w.-]+\/[\w.-]+$/u.test(repository) ||
!token ||
!values["target-branch"]
) {
throw new Error("Expected GITHUB_REPOSITORY, GH_TOKEN and --target-branch");
}
const depth = await releaseProposalSearchDepth(
async (command, args) =>
(await execute(command, args, { encoding: "utf8" })).stdout,
values["source-sha"],
);
// Patch before import: only proposal generation uses this maintained upstream fix.
// Publication continues through the pinned release-please action.
await applyReleasePleasePatch();
const { GitHub, Manifest } = await import("release-please");
const [owner, repo] = repository.split("/");
const branch = values["target-branch"];
const github = await GitHub.create({
owner,
repo,
token,
defaultBranch: branch,
});
const manifest = await Manifest.fromManifest(
github,
branch,
undefined,
undefined,
{
commitSearchDepth: depth,
},
);
const prs = (await manifest.createPullRequests()).filter(Boolean);
if (process.env.GITHUB_OUTPUT) {
await appendFile(
process.env.GITHUB_OUTPUT,
`prs_created=${prs.length > 0}\n${prs.length ? `pr=${JSON.stringify(prs[0])}\n` : ""}`,
);
}
}
await main().catch((error) => {
process.stderr.write(
`Release proposal failed: ${error instanceof Error ? error.message : String(error)}\n`,
);
process.exitCode = 1;
});
+32 -14
View File
@@ -11,26 +11,44 @@ import {
import { homedir } from "node:os";
import { delimiter, dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { tsImport } from "tsx/esm/api";
import { evaluateCodexEvents } from "../dist/evaluation/CodexAgentEval.js";
import {
MISSING_TARGET_SCENARIO,
nativeProviderScenario,
} from "../dist/evaluation/AgentEvaluationScenarios.js";
import {
createAgentEvaluationFixtures,
agentFixtureClaims,
agentFixtureAnswerInstructions,
} from "../dist/evaluation/AgentEvaluationFixtures.js";
import {
agentEvaluationPassed,
summarizeFactualCorrectness,
} from "../dist/evaluation/AgentEvaluationReport.js";
import { PRODUCT_IDENTITY } from "../dist/identity.js";
import { createAgentEvaluationCommands } from "./lib/agent-evaluation-commands.mjs";
import { closeVerifierResources } from "./lib/verifier-cleanup.mjs";
import { completeVerifierRun, createVerifierRun } from "./lib/verifier-run.mjs";
const { evaluateCodexEvents } = await tsImport(
new URL("../tests/evaluation/model-evals/CodexAgentEval.ts", import.meta.url)
.href,
import.meta.url,
);
const { MISSING_TARGET_SCENARIO, nativeProviderScenario } = await tsImport(
new URL(
"../tests/evaluation/model-evals/AgentEvaluationScenarios.ts",
import.meta.url,
).href,
import.meta.url,
);
const {
createAgentEvaluationFixtures,
agentFixtureClaims,
agentFixtureAnswerInstructions,
} = await tsImport(
new URL(
"../tests/evaluation/model-evals/AgentEvaluationFixtures.ts",
import.meta.url,
).href,
import.meta.url,
);
const { agentEvaluationPassed, summarizeFactualCorrectness } = await tsImport(
new URL(
"../tests/evaluation/model-evals/AgentEvaluationReport.ts",
import.meta.url,
).href,
import.meta.url,
);
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const verifierRun = createVerifierRun();
const timeoutMs = Number(process.env.REA_AGENT_EVAL_TIMEOUT_MS ?? 480_000);
+2
View File
@@ -37,6 +37,8 @@ export async function verifyPackagePack({ root, workspace }) {
throw new Error("package did not expose both rea command entry points");
if (packedManifest.mcpName !== "io.github.morluto/rea")
throw new Error("package did not retain its MCP Registry ownership marker");
if (!packedFiles.includes("package/scripts/lib/cli-process-environment.mjs"))
throw new Error("package omitted the CLI process environment owner");
if (packedFiles.some((path) => path.startsWith("package/.agents/")))
throw new Error(
"package included authored skill sources instead of only the generated bundle",
+1 -1
View File
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { firstProcedureAddress } from "../dist/application/RealHopperAssertions.js";
import { firstProcedureAddress } from "./lib/real-hopper-assertions.mjs";
import { parseBinaryTarget } from "../dist/application/BinaryTargetResolver.js";
import { ArtifactResourceScope } from "../dist/artifacts/ArtifactResourceScope.js";
import { AnalysisCleanupLease } from "../dist/artifacts/AnalysisCleanupLease.js";
+1 -1
View File
@@ -10,7 +10,7 @@ import {
requireAddressArray,
requireFunctionDossier,
requirePseudocode,
} from "../dist/application/RealHopperAssertions.js";
} from "./lib/real-hopper-assertions.mjs";
import { HOPPER_PROVIDER_IDENTITY } from "../dist/hopper/HopperProviderCapabilities.js";
import { HOPPER_TARGET_LEASE_DIRECTORY } from "../dist/process/HopperLease.js";
import { REA_WORKFLOW_PROVIDER } from "../dist/domain/investigationProviders.js";
+152 -96
View File
@@ -6,14 +6,32 @@ import { promisify } from "node:util";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { tsImport } from "tsx/esm/api";
import { toolContract } from "../dist/contracts/toolContracts.js";
import { RECONSTRUCTION_READINESS_EXAMPLE } from "../dist/contracts/reconstructionReadinessExample.js";
import { parseEvidence } from "../dist/domain/evidence.js";
import { createEvidenceBundle } from "../dist/domain/evidenceBundle.js";
import { createReconstructionReadinessReport } from "../dist/domain/reconstructionReadiness.js";
import { reconstructionReadinessReportSchema } from "../dist/domain/reconstructionReadinessSchemas.js";
import { PACKAGE_METADATA } from "../dist/generatedPackageMetadata.js";
import { closeVerifierResources } from "./lib/verifier-cleanup.mjs";
const [
{ RECONSTRUCTION_READINESS_EXAMPLE },
{ createReconstructionReadinessReport },
{ reconstructionReadinessReportSchema },
] = await Promise.all(
[
"reconstructionReadinessExample",
"reconstructionReadiness",
"reconstructionReadinessSchemas",
].map((name) =>
tsImport(
new URL(
`../tests/conformance/readiness/report/${name}.ts`,
import.meta.url,
).href,
import.meta.url,
),
),
);
const execute = promisify(execFile);
const root = process.cwd();
@@ -50,6 +68,8 @@ const client = new Client({
capabilities: { elicitation: {} },
});
let verificationReport;
let primaryError;
try {
await client.connect(transport);
const catalog = await client.listTools();
@@ -61,6 +81,7 @@ try {
{ name: "binary_session", arguments: {} },
{ toolDefinition: sessionTool },
);
assert.notEqual(sessionReply.isError, true);
const sessionStatus = toolContract("binary_session").outputSchema.parse(
sessionReply.structuredContent,
).result;
@@ -77,76 +98,93 @@ try {
"CLI/MCP Electron analysis Evidence identity drifted",
);
const input = structuredClone(RECONSTRUCTION_READINESS_EXAMPLE);
input.identity.cli_version = PACKAGE_METADATA.version;
input.identity.server_version = PACKAGE_METADATA.version;
input.identity.catalog_digest =
sessionStatus.server_identity.catalog.digests.combined_sha256;
const records = [
...input.evidence_bundle.records,
nativeEvidence,
javascriptCliEvidence,
electronCliEvidence,
].map(parseEvidence);
input.evidence_bundle = createEvidenceBundle(records);
setFixtureDigest(input, "native", nativeEvidence);
setFixtureDigest(input, "javascript-cli", javascriptCliEvidence);
setFixtureDigest(input, "electron", electronCliEvidence);
linkCheck(input, "discover-classify", "artifact-inventory", nativeEvidence);
linkCheck(input, "static-analysis", "native-routed", nativeEvidence);
linkCheck(
input,
"static-analysis",
"javascript-routed",
javascriptCliEvidence,
);
linkCheck(
input,
"reactive-scenarios",
"electron-correlated",
electronCliEvidence,
);
const readinessReport = reconstructionReadinessReportSchema.parse(
createReconstructionReadinessReport(input),
);
assert.equal(readinessReport.status, "pass");
if (process.env.REA_READINESS_REPORT_PATH !== undefined)
await writeFile(
resolve(root, process.env.REA_READINESS_REPORT_PATH),
`${JSON.stringify(readinessReport, null, 2)}\n`,
);
const tampered = structuredClone(input);
tampered.replay.expected_source_digest = readinessReport.source_digest;
tampered.identity.server_version = "tampered";
const tamperedReport = reconstructionReadinessReportSchema.parse(
createReconstructionReadinessReport(tampered),
);
assert.equal(tamperedReport.status, "fail");
assert.ok(
tamperedReport.findings.some(
({ code }) => code === "replay-source-digest-mismatch",
),
);
process.stdout.write(
`${JSON.stringify({
status: readinessReport.status,
report_id: readinessReport.report_id,
report_digest: readinessReport.report_digest,
stages: readinessReport.summary.stages,
findings: readinessReport.summary.findings,
native_evidence_id: nativeEvidence.evidence_id,
javascript_evidence_id: javascriptCliEvidence.evidence_id,
electron_evidence_id: electronCliEvidence.evidence_id,
provider: "local-stdio",
})}\n`,
);
verificationReport = {
schema: "ReconstructionReadinessVerificationReport",
schema_version: 1,
status: "pass",
observed_journeys: {
evidence_kind: "observed",
scope: "static-cli-mcp",
status: "pass",
server_identity: sessionStatus.server_identity,
evidence_bundles: {
cli: createEvidenceBundle([
nativeEvidence,
javascriptCliEvidence,
electronCliEvidence,
]),
mcp: createEvidenceBundle([javascriptMcp, electronMcp]),
},
journeys: [
{
journey_id: "native-artifact-cli",
operation: "inspect-artifact",
surfaces: ["cli"],
status: "pass",
evidence_ids: [nativeEvidence.evidence_id],
checks: ["cli-completed", "evidence-authenticated"],
},
...[
["javascript-static-cli-mcp", javascriptCliEvidence],
["electron-static-cli-mcp", electronCliEvidence],
].map(([journeyId, evidence]) => ({
journey_id: journeyId,
operation: "analyze-javascript-application",
surfaces: ["cli", "mcp"],
status: "pass",
evidence_ids: [evidence.evidence_id],
checks: [
"cli-completed",
"mcp-completed",
"evidence-authenticated",
"evidence-identity-parity",
],
})),
],
limitations: [
"Native MCP parity was not exercised.",
"Fixture application runtime behavior was not executed.",
"Authority recovery, cancellation cleanup, behavioral comparison, and reconstruction closure were not exercised.",
],
},
evaluator_conformance: verifySyntheticEvaluator(),
};
} catch (cause) {
primaryError = cause;
throw cause;
} finally {
await client.close();
await closeVerifierResources(
[() => client.close(), () => transport.close()],
primaryError,
);
}
if (process.env.REA_READINESS_REPORT_PATH !== undefined)
await writeFile(
resolve(root, process.env.REA_READINESS_REPORT_PATH),
`${JSON.stringify(verificationReport, null, 2)}\n`,
);
process.stdout.write(
`${JSON.stringify({
schema: verificationReport.schema,
schema_version: verificationReport.schema_version,
status: verificationReport.status,
observed_journeys: {
status: verificationReport.observed_journeys.status,
scope: verificationReport.observed_journeys.scope,
journeys: verificationReport.observed_journeys.journeys,
limitations: verificationReport.observed_journeys.limitations,
},
evaluator_conformance: {
status: verificationReport.evaluator_conformance.status,
evidence_kind: verificationReport.evaluator_conformance.evidence_kind,
checks: verificationReport.evaluator_conformance.checks,
report_digest:
verificationReport.evaluator_conformance.baseline_report.report_digest,
},
})}\n`,
);
async function runCli(arguments_) {
const { stdout } = await execute(
process.execPath,
@@ -166,32 +204,50 @@ async function analyzeMcp(client_, inputPath) {
arguments: { input_path: inputPath },
});
assert.notEqual(response.isError, true);
const projected = requireObject(response.structuredContent);
return { evidence_id: projected.evidence_id };
}
function requireObject(value) {
if (typeof value !== "object" || value === null || Array.isArray(value))
throw new TypeError("Expected an object");
return value;
}
function setFixtureDigest(input, kind, evidence) {
const fixture = input.fixtures.find(
({ kind: fixtureKind }) => fixtureKind === kind,
toolContract("analyze_javascript_application").outputSchema.parse(
response.structuredContent,
);
if (fixture === undefined || evidence.subject === null)
throw new TypeError(`Missing ${kind} fixture identity`);
fixture.artifact_sha256 = evidence.subject.digest.sha256;
return parseEvidence(response.structuredContent);
}
function linkCheck(input, stageId, checkId, evidence) {
const stage = input.stages.find(({ stage_id: id }) => id === stageId);
const check = stage?.checks.find(({ check_id: id }) => id === checkId);
if (stage === undefined || check === undefined)
throw new TypeError(`Missing readiness check ${stageId}/${checkId}`);
stage.evidence_ids = [
...new Set([...stage.evidence_ids, evidence.evidence_id]),
].sort();
check.evidence_ids = [evidence.evidence_id];
function verifySyntheticEvaluator() {
const input = structuredClone(RECONSTRUCTION_READINESS_EXAMPLE);
const evaluate = (value) =>
reconstructionReadinessReportSchema.parse(
createReconstructionReadinessReport(value),
);
const baseline = evaluate(input);
assert.equal(baseline.status, "pass");
assert.deepEqual(evaluate(structuredClone(input)), baseline);
const tampered = structuredClone(input);
const record = tampered.evidence_bundle.records[0];
assert.ok(record, "Synthetic conformance requires Evidence");
record.operation += "_tampered";
assert.throws(
() => evaluate(tampered),
/Evidence semantic identifier does not match its record/u,
);
const stale = structuredClone(input);
stale.replay.expected_source_digest = baseline.source_digest;
stale.identity.server_version += "-changed";
const staleReport = evaluate(stale);
assert.equal(staleReport.status, "fail");
assert.ok(
staleReport.findings.some(
({ code }) => code === "replay-source-digest-mismatch",
),
);
return {
evidence_kind: "synthetic",
status: "pass",
checks: [
"baseline-accepted",
"deterministic-evaluation",
"tampered-evidence-rejected",
"stale-source-rejected",
].map((checkId) => ({ check_id: checkId, status: "pass" })),
baseline_report: baseline,
};
}
+55
View File
@@ -100,6 +100,12 @@ const checkObjc = (metadata, mode) => {
const protocol = metadata.objc_protocols.find(
({ name }) => name === "ReaDispatchProtocol",
);
const protocolProperty = protocol?.properties.find(
({ name }) => name === "fixtureName",
);
assert.equal(protocolProperty?.name, "fixtureName");
assert.equal(protocolProperty?.type_encoding, '@"NSString"');
assert.equal(protocolProperty?.atomicity, "nonatomic");
assert.equal(protocol?.methods[0]?.is_required, true);
assert.equal(protocol?.methods[0]?.is_optional, false);
assert.equal(protocol?.optional_methods[0]?.is_required, false);
@@ -161,6 +167,31 @@ const checkObjc = (metadata, mode) => {
const checkSwift = (metadata, mode) => {
assert.equal(fixupCoverage(metadata)?.status, "complete");
for (const context of ["ReaNestedA", "ReaNestedB"]) {
const type = `${context}.Implementation`;
const protocol = `${context}.Requirement`;
assert.ok(
metadata.swift_conformances.some(
(item) =>
item.type_name === type &&
item.protocol_name === protocol &&
item.module === "ReaWitnessFixture" &&
item.decode.status === "decoded",
),
`Nested Swift conformance ${type}: ${protocol} missing (${mode.mode})`,
);
assert.ok(
metadata.swift_dispatch_slots.some(
(item) =>
item.table_kind === "witness_table" &&
item.owner === `${type}: ${protocol}` &&
item.implementation_address !== null &&
item.decode.status === "decoded",
),
`Nested Swift witness ${type}: ${protocol} missing (${mode.mode})`,
);
}
if (
!metadata.swift_dispatch_slots.some(
(slot) =>
@@ -204,6 +235,10 @@ const EXPECTED_SWIFT_DECLS = [
["struct", "ReaDeclFixtureOuter.Inner", false],
["struct", "ReaDeclFixtureGeneric", true],
["actor", "ReaDeclFixtureActor", false],
["enum", "ReaNestedA", false],
["struct", "ReaNestedA.Implementation", false],
["enum", "ReaNestedB", false],
["struct", "ReaNestedB.Implementation", false],
];
/** Stable declaration facts compared across the symbols and stripped builds. */
@@ -336,6 +371,26 @@ try {
await checkSwiftSymbolAddresses(swiftPath, declarations);
unstrippedDeclarations = declarations;
} else assert.deepEqual(declarations, unstrippedDeclarations);
if (mode.mode === "chained" && variant === "stripped") {
const cli = await artifactCliEvidence(
"inspect-native-dispatch-metadata",
swiftPath,
);
const cliResult = nativeDispatchMetadataResultSchema.parse(
cli.normalized_result,
);
assert.equal(cliResult.target_sha256, target.sha256);
checkSwift(cliResult.result, mode);
await withArtifactMcp(swiftPath, async (client) => {
const mcpResult = nativeDispatchMetadataResultSchema.parse(
await artifactMcpResult(client, "inspect_native_dispatch_metadata"),
);
assert.equal(mcpResult.target_sha256, target.sha256);
checkSwift(mcpResult.result, mode);
assert.deepEqual(mcpResult.result, cliResult.result);
});
}
results.push({
variant: `swift-${mode.mode}-${variant}`,
target_sha256: target.sha256,