feat: strengthen reconstruction and runtime workflows

This commit is contained in:
heaven-hm committed 2026-10-14 23:58:57 +05:30
1 parent 70ac34d6b6
commit 85fc7c58d1
18 files changed
+1408 -28

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
import { spawnSync } from "node:child_process";
import { existsSync } from "node:fs";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
const recipes = [
"scripts/calculator/build.sh",
"benchmarks/native-windows-ports/snake/replica/build.sh",
"benchmarks/native-windows-ports/pixeltetris/replica/build.sh",
"benchmarks/native-windows-ports/tetris/replica/build.sh",
"benchmarks/native-windows-ports/notepad/replica/build.sh",
"benchmarks/native-windows-ports/clipjar/replica/build.sh",
"benchmarks/native-windows-ports/gconv/replica/build.sh",
"benchmarks/native-windows-ports/imgview/replica/build.sh",
"benchmarks/plink/replica/build.sh",
"benchmarks/native-batch-2/reconstructed/fd/build.sh",
"benchmarks/native-batch-2/reconstructed/ripgrep/build.sh",
"benchmarks/native-batch-2/reconstructed/bat/build.sh",
];
if (process.platform !== "darwin") {
throw new Error(
"The checked-in desktop replicas currently target macOS; run this build on macOS.",
);
}
const failures = [];
for (const recipe of recipes) {
const path = resolve(root, recipe);
if (!existsSync(path)) {
failures.push(`${recipe}: build recipe is missing`);
continue;
}
process.stdout.write(`\n==> ${recipe}\n`);
const result = spawnSync("bash", [path], {
cwd: root,
stdio: "inherit",
});
if (result.error !== undefined)
failures.push(`${recipe}: ${result.error.message}`);
else if (result.status !== 0)
failures.push(`${recipe}: exited with ${result.status}`);
}
if (failures.length > 0) {
process.stderr.write(`\n${failures.length} benchmark build(s) failed:\n`);
for (const failure of failures) process.stderr.write(`- ${failure}\n`);
process.exitCode = 1;
} else {
process.stdout.write(`\nBuilt ${recipes.length} macOS benchmark replicas.\n`);
process.stdout.write(
"Outputs are host-specific and remain under ignored build/ directories.\n",
);
}
+3
View File
@@ -0,0 +1,3 @@
# Local Swift executable and compiler cache are reproducible build output.
build
build/**
+30
View File
@@ -0,0 +1,30 @@
import AppKit
@MainActor
final class CalculatorApplicationDelegate: NSObject, NSApplicationDelegate {
private var calculatorWindow: NSWindow?
func applicationDidFinishLaunching(_ notification: Notification) {
let engine = CalculatorEngine()
let window = createCalculatorWindow(engine: engine)
calculatorWindow = window
window.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
true
}
}
@main
@MainActor
struct CalculatorApp {
static func main() {
let application = NSApplication.shared
let applicationDelegate = CalculatorApplicationDelegate()
application.delegate = applicationDelegate
application.setActivationPolicy(.regular)
application.run()
}
}
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
set -eu
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
output_dir="$script_dir/build"
mkdir -p "$output_dir"
: "${SWIFTC:=swiftc}"
"$SWIFTC" -O -module-cache-path "$output_dir/module-cache" \
-o "$output_dir/calculatormac" \
"$script_dir/CalculatorEngine.swift" \
"$script_dir/CalculatorWindow.swift" \
"$script_dir/CalculatorApp.swift" \
-framework Cocoa
file "$output_dir/calculatormac"
+338
View File
@@ -0,0 +1,338 @@
/** Bounded file-conversion benchmark using REA's shared owned-process boundary.
* Work directories are retained evidence, not an OS sandbox. Commands must be
* authorized and isolated by the caller's selected backend where required.
*/
import { createHash, randomUUID } from "node:crypto";
import { constants } from "node:fs";
import { mkdir, mkdtemp, open, rename, rm, writeFile } from "node:fs/promises";
import { basename, join, resolve } from "node:path";
import { runOwnedCommand } from "../../dist/process/OwnedCommand.js";
const hash = (bytes) => createHash("sha256").update(bytes).digest("hex");
const dimension = (equal) => ({ status: equal ? "PASS" : "FAIL" });
const positive = (value, name) => {
if (!Number.isSafeInteger(value) || value <= 0)
throw new TypeError(`${name} must be a positive safe integer`);
return value;
};
const vector = (value, name) => {
if (
!Array.isArray(value) ||
value.length === 0 ||
value.some((part) => typeof part !== "string" || part.includes("\0")) ||
value[0].length === 0
)
throw new TypeError(`${name} must be a nonempty command vector`);
return value;
};
const safeName = (name) => {
if (
typeof name !== "string" ||
!name ||
basename(name) !== name ||
name === "." ||
name === ".." ||
/[\\/\0]/u.test(name)
)
throw new TypeError("outputName must be a single file name");
return name;
};
const failureProjection = (cause) => ({
name: cause?.name ?? "Error",
reason: cause?.reason ?? cause?.code ?? "exception",
message: cause?.message ?? String(cause),
...(typeof cause?.cleanupFailure === "string"
? { cleanupFailure: cause.cleanupFailure }
: {}),
...(cause?.cleanupOwner ? { unresolvedCleanupOwner: true } : {}),
...(Array.isArray(cause?.resources)
? {
resources: cause.resources.filter((value) => typeof value === "string"),
}
: {}),
});
async function atomicReport(path, report) {
const temporary = path + "." + randomUUID() + ".tmp";
try {
await writeFile(temporary, JSON.stringify(report, null, 2) + "\n", {
flag: "wx",
mode: 0o600,
});
await rename(temporary, path);
} finally {
await rm(temporary, { force: true });
}
}
async function boundedFile(path, limit) {
const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
try {
const before = await handle.stat();
if (!before.isFile() || before.size > limit)
throw new Error(`Not a regular bounded file: ${path}`);
const bytes = Buffer.alloc(before.size);
let offset = 0;
while (offset < bytes.length) {
const read = await handle.read(
bytes,
offset,
bytes.length - offset,
offset,
);
if (!read.bytesRead) break;
offset += read.bytesRead;
}
const after = await handle.stat();
if (
offset !== bytes.length ||
before.size !== after.size ||
before.mtimeMs !== after.mtimeMs ||
before.ctimeMs !== after.ctimeMs
)
throw new Error(`File changed during observation: ${path}`);
return bytes;
} finally {
await handle.close();
}
}
/** Command arguments support {input}, {output}, {workdir}; no shell is used.
* Cases are serial and both sides receive independent copies of the same input.
* Missing output, timeouts, cancelled runs and truncated diagnostics never PASS.
* reportPath is retained on exceptions; onProgress receives a detached journal
* after each atomic write. Reports are supervision evidence, not a canonical
* ProcessCapture. Atomic rename prevents partial JSON, not power-loss durability.
*/
export async function runFileDifferentialBenchmark(options) {
const original = vector(options.originalCommand, "originalCommand");
const replica = vector(options.replicaCommand, "replicaCommand");
const timeoutMs = positive(options.timeoutMs ?? 15_000, "timeoutMs");
const maxFileBytes = positive(
options.maxFileBytes ?? 16 * 1024 * 1024,
"maxFileBytes",
);
const diagnosticBytes = positive(
options.diagnosticBytes ?? 1024 * 1024,
"diagnosticBytes",
);
if (
options.onProgress !== undefined &&
typeof options.onProgress !== "function"
)
throw new TypeError("onProgress must be a function");
if (!Array.isArray(options.cases) || options.cases.length === 0)
throw new TypeError("A nonempty declared case suite is required");
const seen = new Set();
for (const item of options.cases) {
if (
typeof item.id !== "string" ||
!/^[a-zA-Z0-9_-]+$/u.test(item.id) ||
seen.has(item.id)
)
throw new TypeError("Case IDs must be unique path-safe identifiers");
seen.add(item.id);
safeName(item.outputName ?? "output.bin");
if (
item.arguments !== undefined &&
(!Array.isArray(item.arguments) ||
item.arguments.some(
(part) => typeof part !== "string" || part.includes("\0"),
))
)
throw new TypeError("Case arguments must be strings without NUL");
}
options.signal?.throwIfAborted();
const directory = resolve(options.outputDirectory);
await mkdir(directory, { recursive: true });
const runDirectory = await mkdtemp(join(directory, "file-diff-"));
const report = {
schema: "rea.file-differential-benchmark.v1",
status: "UNVERIFIED",
scope:
"Exact output-file bytes, retained UTF-8 diagnostic text, and exit status on the declared cases only.",
universalEquivalence: false,
platform: { os: process.platform, architecture: process.arch },
limits: { timeoutMs, maxFileBytes, diagnosticBytes },
runDirectory,
reportPath: join(runDirectory, "report.json"),
phase: "running",
cases: options.cases.map((item) => ({
id: item.id,
status: "UNVERIFIED",
phase: "pending",
})),
};
const persist = async (notify = true) => {
await atomicReport(report.reportPath, report);
if (notify && options.onProgress)
await options.onProgress(structuredClone(report));
};
try {
await persist();
for (const item of options.cases) {
options.signal?.throwIfAborted();
const record = report.cases.find((candidate) => candidate.id === item.id);
record.phase = "reading-input";
await persist();
const input = await boundedFile(resolve(item.inputPath), maxFileBytes);
record.input = {
path: resolve(item.inputPath),
bytes: input.length,
sha256: hash(input),
};
await persist();
const runSide = async (side, command, environment) => {
const cwd = join(runDirectory, `${item.id}-${side}`);
await mkdir(cwd);
const inputPath = join(
cwd,
"input" +
(basename(item.inputPath).match(/\.[^.]+$/u)?.[0] ?? ".bin"),
);
const outputPath = join(cwd, item.outputName ?? "output.bin");
if (inputPath === outputPath)
throw new TypeError("Output must differ from staged input");
await writeFile(inputPath, input, { flag: "wx", mode: 0o600 });
const expand = (arg) =>
arg
.split("{input}")
.join(inputPath)
.split("{output}")
.join(outputPath)
.split("{workdir}")
.join(cwd);
const expanded = [...command, ...(item.arguments ?? [])].map(expand);
const observation = {
command: expanded,
workDirectory: cwd,
status: "UNVERIFIED",
};
record[side] = observation;
record.phase = `${side}-running`;
await persist();
options.signal?.throwIfAborted();
try {
const captured = await runOwnedCommand(
{
command: expanded[0],
arguments: expanded.slice(1),
cwd,
env: environment ?? process.env,
runId: "rea-file-benchmark-" + randomUUID(),
expectedCommand: null,
},
{ timeoutMs, diagnosticBytes, acceptNonZeroExit: true },
{ signal: options.signal },
);
Object.assign(observation, {
status: "OBSERVED",
exitCode: captured.exitCode,
signal: captured.signal,
stdout: captured.stdout,
stderr: captured.stderr,
});
} catch (cause) {
// Cleanup failures are exceptional: preserve the owner's error/resources.
if (cause.cleanupFailure || cause.cleanupOwner) {
observation.failure = failureProjection(cause);
if (cause.snapshot) observation.snapshot = cause.snapshot;
throw cause;
}
Object.assign(observation, {
status: "UNVERIFIED",
reason: cause.reason ?? "launch",
message: cause.message,
snapshot: cause.snapshot ?? null,
});
}
record.phase = `${side}-captured`;
await persist();
options.signal?.throwIfAborted();
let output;
try {
output = await boundedFile(outputPath, maxFileBytes);
observation.output = {
path: outputPath,
bytes: output.length,
sha256: hash(output),
};
} catch (cause) {
observation.output = {
path: outputPath,
status: "UNVERIFIED",
reason: cause.code ?? cause.message,
};
}
record.phase = `${side}-complete`;
await persist();
return { observation, output };
};
const left = await runSide(
"original",
original,
options.originalEnvironment,
);
options.signal?.throwIfAborted();
const right = await runSide(
"replica",
replica,
options.replicaEnvironment,
);
const complete =
left.observation.status === "OBSERVED" &&
right.observation.status === "OBSERVED";
const compare = (equal) =>
complete ? dimension(equal) : { status: "UNVERIFIED" };
const dimensions = {
outputFileBytes:
complete && left.output && right.output
? dimension(left.output.equals(right.output))
: { status: "UNVERIFIED" },
stdoutDiagnosticText: compare(
left.observation.stdout?.text === right.observation.stdout?.text,
),
stderrDiagnosticText: compare(
left.observation.stderr?.text === right.observation.stderr?.text,
),
exitStatus: compare(
left.observation.exitCode === right.observation.exitCode &&
left.observation.signal === null &&
right.observation.signal === null,
),
};
const statuses = Object.values(dimensions).map((value) => value.status);
Object.assign(record, {
dimensions,
phase: "complete",
status: statuses.includes("FAIL")
? "FAIL"
: statuses.every((status) => status === "PASS")
? "PASS"
: "UNVERIFIED",
});
await persist();
}
options.signal?.throwIfAborted();
report.status = report.cases.some((item) => item.status === "FAIL")
? "FAIL"
: report.cases.every((item) => item.status === "PASS")
? "PASS"
: "UNVERIFIED";
report.phase = "complete";
await persist();
return report;
} catch (cause) {
report.status = "UNVERIFIED";
report.phase = options.signal?.aborted ? "cancelled" : "interrupted";
report.failure = failureProjection(cause);
try {
await persist(false);
} catch (journalCause) {
// A report write failure must not replace unresolved cleanup ownership.
if (cause && typeof cause === "object" && Object.isExtensible(cause))
cause.journalFailure = failureProjection(journalCause);
}
if (cause && typeof cause === "object" && Object.isExtensible(cause))
cause.reportPath = report.reportPath;
throw cause;
}
}
@@ -0,0 +1,218 @@
import assert from "node:assert/strict";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { runFileDifferentialBenchmark } from "./file-differential-benchmark.mjs";
const fixture = `
const fs = require('node:fs');
const [mode,input,output] = process.argv.slice(1);
if (mode === 'hang') setInterval(() => {},1000);
else if (mode === 'overflow') process.stdout.write('x'.repeat(10000));
else if (mode !== 'missing') {
const bytes=fs.readFileSync(input);
if (mode === 'different') bytes[0] ^= 255;
fs.writeFileSync(output,bytes);
if (mode === 'stdout') process.stdout.write('different stdout');
if (mode === 'stderr') process.stderr.write('different stderr');
if (mode === 'exit') process.exitCode=7;
}
`;
const command = (mode) => [
process.execPath,
"-e",
fixture,
mode,
"{input}",
"{output}",
];
async function scope(operation) {
const directory = await mkdtemp(join(tmpdir(), "rea-file-diff-test-"));
try {
const inputPath = join(directory, "fixture.dat");
await writeFile(inputPath, Buffer.from([0, 255, 13, 10, 128]));
return await operation({
outputDirectory: directory,
originalCommand: command("copy"),
replicaCommand: command("copy"),
cases: [{ id: "binary", inputPath }],
timeoutMs: 10000,
});
} finally {
await rm(directory, { recursive: true, force: true });
}
}
test("exact binary outputs and retained report from independent fresh directories", () =>
scope(async (options) => {
const report = await runFileDifferentialBenchmark(options);
assert.equal(report.status, "PASS");
assert.equal(report.universalEquivalence, false);
const item = report.cases[0];
assert.notEqual(item.original.workDirectory, item.replica.workDirectory);
assert.equal(item.original.output.sha256, item.input.sha256);
assert.equal(item.replica.output.sha256, item.input.sha256);
assert.deepEqual(
JSON.parse(await readFile(report.reportPath, "utf8")),
report,
);
const repeat = await runFileDifferentialBenchmark(options);
assert.notEqual(report.runDirectory, repeat.runDirectory);
}));
for (const [mode, dimension] of [
["different", "outputFileBytes"],
["stdout", "stdoutDiagnosticText"],
["stderr", "stderrDiagnosticText"],
["exit", "exitStatus"],
]) {
test(`reports ${dimension} mismatch independently`, () =>
scope(async (options) => {
const report = await runFileDifferentialBenchmark({
...options,
replicaCommand: command(mode),
});
assert.equal(report.status, "FAIL");
for (const [name, value] of Object.entries(report.cases[0].dimensions))
assert.equal(value.status, name === dimension ? "FAIL" : "PASS");
}));
}
for (const mode of ["missing", "hang", "overflow"]) {
test(`never passes ${mode}`, () =>
scope(async (options) => {
const report = await runFileDifferentialBenchmark({
...options,
originalCommand: command(mode),
replicaCommand: command(mode),
timeoutMs: mode === "hang" ? 100 : 10000,
diagnosticBytes: 1000,
});
assert.equal(report.status, "UNVERIFIED");
assert.equal(
report.cases[0].dimensions.outputFileBytes.status,
"UNVERIFIED",
);
if (mode !== "missing") {
assert.equal(report.cases[0].original.status, "UNVERIFIED");
assert.equal(
report.cases[0].original.reason,
mode === "hang" ? "timeout" : "output-limit",
);
}
}));
}
test("rejects duplicate IDs and output path traversal before target execution", () =>
scope(async (options) => {
await assert.rejects(
runFileDifferentialBenchmark({
...options,
cases: [options.cases[0], options.cases[0]],
}),
/unique/u,
);
await assert.rejects(
runFileDifferentialBenchmark({
...options,
cases: [{ ...options.cases[0], outputName: "../escape" }],
}),
/single file/u,
);
}));
test("pre-cancelled suite cannot run or pass", () =>
scope(async (options) => {
const abort = new AbortController();
abort.abort();
await assert.rejects(
runFileDifferentialBenchmark({ ...options, signal: abort.signal }),
{ name: "AbortError" },
);
}));
test("retains completed first case when second input is missing", () =>
scope(async (options) => {
let failure;
try {
await runFileDifferentialBenchmark({
...options,
cases: [
options.cases[0],
{
id: "absent",
inputPath: join(options.outputDirectory, "missing.dat"),
},
],
});
} catch (cause) {
failure = cause;
}
assert.equal(failure?.code, "ENOENT");
const report = JSON.parse(await readFile(failure.reportPath, "utf8"));
assert.equal(report.status, "UNVERIFIED");
assert.equal(report.phase, "interrupted");
assert.equal(report.cases[0].status, "PASS");
assert.equal(
report.cases[0].original.output.sha256,
report.cases[0].input.sha256,
);
assert.equal(report.cases[1].phase, "reading-input");
assert.equal(report.failure.reason, "ENOENT");
}));
test("cancellation between cases preserves earlier real observations", () =>
scope(async (options) => {
const abort = new AbortController();
let failure;
try {
await runFileDifferentialBenchmark({
...options,
signal: abort.signal,
cases: [options.cases[0], { ...options.cases[0], id: "second" }],
onProgress(report) {
if (report.cases[0].phase === "complete") abort.abort();
},
});
} catch (cause) {
failure = cause;
}
assert.equal(failure?.name, "AbortError");
const report = JSON.parse(await readFile(failure.reportPath, "utf8"));
assert.equal(report.status, "UNVERIFIED");
assert.equal(report.phase, "cancelled");
assert.equal(report.cases[0].status, "PASS");
assert.equal(report.cases[1].phase, "pending");
assert.equal(report.cases[1].original, undefined);
}));
test("failure projection preserves original owner error and prior evidence (simulated fault)", () =>
scope(async (options) => {
// This tests exceptional journal propagation, not real cleanup failure.
const fault = new Error("simulated unresolved cleanup");
fault.cleanupOwner = { simulated: true };
fault.cleanupFailure = "simulated failure";
fault.resources = ["simulated-owned-resource"];
let failure;
try {
await runFileDifferentialBenchmark({
...options,
cases: [options.cases[0], { ...options.cases[0], id: "second" }],
onProgress(report) {
if (report.cases[1].phase === "original-captured") throw fault;
},
});
} catch (cause) {
failure = cause;
}
assert.equal(failure, fault);
assert.equal(failure.cleanupOwner.simulated, true);
const report = JSON.parse(await readFile(failure.reportPath, "utf8"));
assert.equal(report.status, "UNVERIFIED");
assert.equal(report.cases[0].status, "PASS");
assert.equal(report.cases[1].original.status, "OBSERVED");
assert.equal(report.failure.unresolvedCleanupOwner, true);
assert.equal(report.failure.cleanupFailure, "simulated failure");
assert.deepEqual(report.failure.resources, ["simulated-owned-resource"]);
}));
+20 -8
View File
@@ -102,11 +102,20 @@ export async function verifyTicTacToe(
captured.push({ e, c, raw });
}
const [a, b] = captured;
const boards = a.raw
.toString()
.split(/\r?\n/)
.filter((l) => /^\d+:(-1|0|1|2):/.test(l));
assert.equal(boards.length, 19683);
const boardRecords = (raw) =>
raw
.toString()
.split(/\r+\n/)
.filter((line) => /^\d+:(-1|0|1|2):/.test(line));
const originalBoards = boardRecords(a.raw);
const replicaBoards = boardRecords(b.raw);
assert.equal(originalBoards.length, 19683);
assert.equal(replicaBoards.length, 19683);
const boardRecordMismatchCount = originalBoards.reduce(
(count, record, index) => count + Number(record !== replicaBoards[index]),
0,
);
const boardRecordsEqual = boardRecordMismatchCount === 0;
const frames = [];
for (const name of (await readdir(join(output, "original-frames")))
.filter((n) => n.endsWith(".png"))
@@ -123,7 +132,7 @@ export async function verifyTicTacToe(
assert.equal(h(await readFile(original)), hashBefore);
const report = {
status:
a.raw.equals(b.raw) &&
boardRecordsEqual &&
frames.length === 6 &&
frames.every((f) => f.pngBytesEqual)
? "suite-passed"
@@ -132,7 +141,9 @@ export async function verifyTicTacToe(
originalSha256: hashBefore,
replicaSha256: h(await readFile(replica)),
probeSha256: h(await readFile(probe)),
boardStates: boards.length,
boardStates: originalBoards.length,
boardRecordsEqual,
boardRecordMismatchCount,
terminalBytesEqual: a.raw.equals(b.raw),
originalTerminalSha256: h(a.raw),
replicaTerminalSha256: h(b.raw),
@@ -144,14 +155,15 @@ export async function verifyTicTacToe(
limitations: [
"Exhaustive board configurations cover only the CheckGameStatus method and observed indicator properties.",
"Six images are actual Form.DrawToBitmap renders following real button handlers; not Windows OS screenshots.",
"Raw PTY terminal bytes are retained separately from exact board-record comparisons; an extra carriage return in the terminal capture can make raw streams differ while all 19,683 board records and rendered frames match.",
"Original and replica are executed on the same macOS ARM Mono/X11 runtime.",
"AI, RNG, all menus, timing, sound and whole-game equivalence remain unverified.",
"Probe executes original and reconstructed code; no game logic is implemented in the probe.",
],
};
await save("report.json", report);
return report;
assert.equal(report.status, "suite-passed");
return report;
}
if (
process.argv[1] &&
+120
View File
@@ -0,0 +1,120 @@
import { mkdir, writeFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { expect, it } from "vitest";
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
import { systemDoctorHost } from "./Doctor.js";
it("threads the selected host and environment into system diagnostics", async () => {
const environment = {
HOME: "/selected/home",
HOPPER_LAUNCHER_PATH: "/selected/bin/hopper",
PATH: "/selected/bin",
};
const calls: Array<{
command: string;
arguments: readonly string[];
environment: NodeJS.ProcessEnv | undefined;
}> = [];
const host = systemDoctorHost({
platform: "linux",
architecture: "x64",
environment,
execFileOutput: async (command, arguments_, options) => {
calls.push({ command, arguments: arguments_, environment: options?.env });
if (command === "sw_vers") return { stdout: "15.0\n", stderr: "" };
if (command === "ldd") return { stdout: "linux-vdso.so.1", stderr: "" };
return { stdout: "/selected/bin/rea\n", stderr: "" };
},
});
expect(host.homeDirectory).toBe("/selected/home");
expect(host.platform).toBe("linux");
expect(host.architecture).toBe("x64");
await host.installationPaths?.();
await host.macosVersion();
await host.executable(process.execPath);
expect(calls.map(({ command }) => command)).toEqual([
"which",
"sw_vers",
"ldd",
]);
expect(
calls.every(({ environment: observed }) => observed === environment),
).toBe(true);
});
it("does not report a directory as an available executable", async () => {
const host = systemDoctorHost({
platform: "darwin",
architecture: "arm64",
environment: {},
execFileOutput: () => Promise.reject(new Error("unexpected command")),
});
await expect(host.executable(dirname(process.execPath))).resolves.toBe(false);
await expect(host.executable(process.execPath)).resolves.toBe(true);
});
it.each([
[{ default: { appdir: "/Applications" } }, "/Applications"],
[
{
default: { appdir: "/Applications" },
env: { appdir: "/Env Apps" },
explicit: { appdir: "~/Tools" },
},
"HOME/Tools",
],
[
{ default: { appdir: "/Applications" }, env: { appdir: "/Env Apps" } },
"/Env Apps",
],
])(
"locates the Hopper cask from its Caskroom receipt without a cask command (%j)",
async (config, expectedAppdir) => {
const root = await createTestTempDirectory("rea-doctor-caskroom-");
const caskroom = join(root, "Caskroom");
const metadata = join(caskroom, "hopper-disassembler", ".metadata");
await mkdir(metadata, { recursive: true });
await writeFile(join(metadata, "config.json"), JSON.stringify(config));
const calls: (readonly string[])[] = [];
const host = systemDoctorHost({
platform: "darwin",
environment: { HOME: join(root, "home") },
execFileOutput: async (_command, arguments_) => {
calls.push(arguments_);
return { stdout: `${caskroom}\n`, stderr: "" };
},
});
expect(await host.brewHopperPath()).toBe(
join(
expectedAppdir.replace("HOME", join(root, "home")),
"Hopper Disassembler.app/Contents/MacOS/hopper",
),
);
// A cask argument makes Homebrew fetch and cache its API data.
expect(calls).toEqual([["--caskroom"]]);
},
);
it("reports no Homebrew Hopper when the cask has no receipt", async () => {
const caskroom = await createTestTempDirectory("rea-doctor-caskroom-");
const calls: (readonly string[])[] = [];
const host = systemDoctorHost({
platform: "darwin",
environment: { HOME: caskroom },
execFileOutput: async (_command, arguments_) => {
calls.push(arguments_);
return { stdout: `${caskroom}\n`, stderr: "" };
},
});
expect(await host.brewHopperPath()).toBeUndefined();
expect(
calls.every((arguments_) => arguments_.join(" ") === "--caskroom"),
).toBe(true);
});
+174
View File
@@ -0,0 +1,174 @@
import type { FileHandle } from "node:fs/promises";
import type {
MipsAbiFlags,
MipsElfMetadata,
} from "../domain/binaryTargetTypes.js";
import { err, ok, type Result } from "../domain/result.js";
/** Observed ABI declarations and coverage of their source tables. */
export interface MipsAbiFlagsInspection {
readonly abiFlags: MipsAbiFlags | null;
readonly limitations: readonly string[];
}
/**
* Read ELF32 MIPS ABI declarations from the caller's already-open file.
* Inspect both typed tables (not section names), including records beyond the
* initial header probe. A missing record is unknown, not a default FP ABI.
* The caller supplies its cancellation check so its tagged error is preserved.
*/
export const readMipsElfAbiFlags = async (
handle: FileHandle,
metadata: MipsElfMetadata,
checkCancelled: () => void,
): Promise<Result<MipsAbiFlagsInspection, string>> => {
checkCancelled();
if (metadata.elfClass !== 32)
return err("MIPS ABI record inspection currently supports ELF32 only");
const fileSize = (await handle.stat()).size;
checkCancelled();
const read = async (
offset: number,
length: number,
label: string,
): Promise<Result<Buffer, string>> => {
checkCancelled();
if (
!Number.isSafeInteger(fileSize) ||
offset < 0 ||
length < 0 ||
offset > fileSize - length
)
return err(`MIPS ${label} lies outside the file`);
const bytes = Buffer.alloc(length);
let total = 0;
while (total < length) {
checkCancelled();
const observed = await handle.read(
bytes,
total,
length - total,
offset + total,
);
checkCancelled();
if (observed.bytesRead === 0) return err(`truncated MIPS ${label}`);
total += observed.bytesRead;
}
return ok(bytes);
};
const header = await read(0, 52, "ELF32 header");
if (!header.ok) return header;
const bytes = header.value;
const little = metadata.byteOrder === "little";
const u16 = (data: Buffer, offset: number): number =>
little ? data.readUInt16LE(offset) : data.readUInt16BE(offset);
const u32 = (data: Buffer, offset: number): number =>
little ? data.readUInt32LE(offset) : data.readUInt32BE(offset);
if (
bytes.toString("hex", 0, 4) !== "7f454c46" ||
bytes[4] !== 1 ||
bytes[5] !== (little ? 1 : 2) ||
u16(bytes, 18) !== 8 ||
u16(bytes, 16) !== metadata.type ||
u32(bytes, 36) !== metadata.flags
)
return err("MIPS ELF header no longer matches the resolved identity");
if (bytes[6] !== 1 || u32(bytes, 20) !== 1 || u16(bytes, 40) !== 52)
return err("invalid MIPS ELF32 version or header size");
const tables = [
{
label: "program header table",
offset: u32(bytes, 28),
count: u16(bytes, 44),
stride: u16(bytes, 42),
entrySize: 32,
typeOffset: 0,
dataOffset: 4,
sizeOffset: 16,
recordType: 0x70000003, // PT_MIPS_ABIFLAGS
optional: false,
},
{
label: "section header table",
offset: u32(bytes, 32),
count: u16(bytes, 48),
stride: u16(bytes, 46),
entrySize: 40,
typeOffset: 4,
dataOffset: 16,
sizeOffset: 20,
recordType: 0x7000002a, // SHT_MIPS_ABIFLAGS
optional: true,
},
];
// Extended numbering needs a separate section-zero reader. Refuse it rather
// than silently treating an unexamined table as having no ABI declarations.
if (u16(bytes, 44) === 0xffff || u16(bytes, 48) >= 0xff00)
return err("extended or reserved MIPS ELF table numbering is unsupported");
let record: Buffer | undefined;
const limitations: string[] = [];
for (const table of tables) {
if (table.count === 0) {
if (table.offset !== 0)
return err(`unsupported extended or inconsistent MIPS ${table.label}`);
continue;
}
if (table.offset < 52 || table.stride !== table.entrySize)
return err(`invalid MIPS ${table.label} offset or entry size`);
const count = table.optional
? Math.min(
table.count,
Math.max(0, Math.floor((fileSize - table.offset) / table.entrySize)),
)
: table.count;
if (count < table.count)
limitations.push(
`MIPS ${table.label} at file offset ${table.offset} extends beyond the captured file: examined ${count} of ${table.count} complete headers. ABI declarations in unavailable headers remain unknown.`,
);
if (count === 0) continue;
// Counts are bounded by the ELF16 fields and strides by their exact ELF32
// structure sizes. Never allocate according to an untrusted section size.
const contents = await read(
table.offset,
count * table.entrySize,
table.label,
);
if (!contents.ok) return contents;
let found = false;
for (let i = 0; i < count; i += 1) {
checkCancelled();
const position = i * table.entrySize;
if (u32(contents.value, position + table.typeOffset) !== table.recordType)
continue;
if (found) return err(`duplicate MIPS ABI records in ${table.label}`);
found = true;
const offset = u32(contents.value, position + table.dataOffset);
const size = u32(contents.value, position + table.sizeOffset);
if (size !== 24)
return err("unsupported MIPS ABI record size; expected 24 bytes");
const candidate = await read(offset, 24, "ABI record");
if (!candidate.ok) return candidate;
if (record !== undefined && !record.equals(candidate.value))
return err("conflicting MIPS program/section ABI declarations");
record = candidate.value;
}
}
if (record === undefined) return ok({ abiFlags: null, limitations });
// Preserve declarations verbatim, including unknown versions/values. Provider
// admission, not parsing, decides which interpretations have been verified.
const abiFlags: MipsAbiFlags = {
version: u16(record, 0),
isaLevel: record.readUInt8(2),
isaRevision: record.readUInt8(3),
gprSize: record.readUInt8(4),
cpr1Size: record.readUInt8(5),
cpr2Size: record.readUInt8(6),
fpAbi: record.readUInt8(7),
isaExtension: u32(record, 8),
ases: u32(record, 12),
flags1: u32(record, 16),
flags2: u32(record, 20),
};
return ok({ abiFlags, limitations });
};
+24 -15
View File
@@ -2,6 +2,7 @@ import { join, resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { PRODUCT_IDENTITY } from "../identity.js";
import { err, ok, type Result } from "../domain/result.js";
import {
runUpdate,
@@ -25,7 +26,7 @@ class FakeUpdateHost implements UpdateHost {
installationResult: UpdateInstallation | undefined = {
manager: "npm",
prefix: "/fixture",
packageRoot: "/fixture/lib/node_modules/rea-agents",
packageRoot: `/fixture/lib/node_modules/${PRODUCT_IDENTITY.packageName}`,
};
installResult: Result<void, string> = ok(undefined);
verified: Result<string, string> = ok("3.3.0");
@@ -121,7 +122,7 @@ describe("REA update", () => {
"--ignore-scripts",
"--prefix",
"/fixture",
"rea-agents@3.3.0",
`${PRODUCT_IDENTITY.packageName}@3.3.0`,
],
});
expect(host.installedVersions).toEqual(["3.3.0"]);
@@ -134,7 +135,7 @@ describe("REA update", () => {
host.installationResult = {
manager: "bun",
globalDirectory: "/home/user/.bun/install/global",
packageRoot: "/home/user/.bun/install/global/node_modules/rea-agents",
packageRoot: `/home/user/.bun/install/global/node_modules/${PRODUCT_IDENTITY.packageName}`,
};
expect(await runUpdate("3.2.1", host, "structured")).toMatchObject({
status: "updated",
@@ -145,7 +146,7 @@ describe("REA update", () => {
"add",
"--global",
"--ignore-scripts",
"rea-agents@3.3.0",
`${PRODUCT_IDENTITY.packageName}@3.3.0`,
],
});
expect(host.verifiedInstallation).toEqual(host.installationResult);
@@ -320,17 +321,25 @@ describe("update failures and version boundaries", () => {
"remediation",
expect.not.stringContaining("npm install --global"),
);
expect(result).toHaveProperty(
"remediation",
expect.stringContaining(`npx ${PRODUCT_IDENTITY.packageSpecifier}`),
);
expect(host.installedVersions).toEqual([]);
});
it("reports actual npm failures and skips verification", async () => {
const host = new FakeUpdateHost();
host.installResult = err("EACCES /fixture/lib/node_modules/rea-agents");
host.installResult = err(
`EACCES /fixture/lib/node_modules/${PRODUCT_IDENTITY.packageName}`,
);
const result = await runUpdate("3.2.1", host);
expect(result).toMatchObject({ status: "failed", reason: "install" });
expect(result).toHaveProperty(
"remediation",
expect.stringContaining("EACCES /fixture/lib/node_modules/rea-agents"),
expect.stringContaining(
`EACCES /fixture/lib/node_modules/${PRODUCT_IDENTITY.packageName}`,
),
);
expect(host.verifiedInstallation).toBeUndefined();
});
@@ -344,14 +353,14 @@ describe("npm installation ownership", () => {
});
it("identifies the global prefix and package root", async () => {
const packageRoot = "/usr/local/lib/node_modules/rea-agents";
const packageRoot = `/usr/local/lib/node_modules/${PRODUCT_IDENTITY.packageName}`;
await expect(
detectNpmInstallation(packageRoot, host("/usr/local/lib/node_modules")),
).resolves.toEqual({ manager: "npm", prefix: "/usr/local", packageRoot });
});
it("retains the curl installer's custom Unix prefix", async () => {
const packageRoot = "/home/user/.local/lib/node_modules/rea-agents";
const packageRoot = `/home/user/.local/lib/node_modules/${PRODUCT_IDENTITY.packageName}`;
await expect(
detectNpmInstallation(packageRoot, host("/usr/local/lib/node_modules")),
).resolves.toEqual({
@@ -363,8 +372,8 @@ describe("npm installation ownership", () => {
it.each([
"/work/rea",
"/work/node_modules/rea-agents",
"/cache/_npx/abc/node_modules/rea-agents",
`/work/node_modules/${PRODUCT_IDENTITY.packageName}`,
`/cache/_npx/abc/node_modules/${PRODUCT_IDENTITY.packageName}`,
])("rejects unrelated invocation %s", async (packageRoot) => {
await expect(
detectNpmInstallation(packageRoot, host("/usr/local/lib/node_modules")),
@@ -378,7 +387,7 @@ describe("npm installation ownership", () => {
};
await expect(
detectNpmInstallation(
"/home/.local/lib/node_modules/rea-agents",
`/home/.local/lib/node_modules/${PRODUCT_IDENTITY.packageName}`,
failing,
),
).resolves.toBeUndefined();
@@ -387,7 +396,7 @@ describe("npm installation ownership", () => {
describe("Bun installation ownership", () => {
const globalDirectory = "/home/user/.bun/install/global";
const packageRoot = `${globalDirectory}/node_modules/rea-agents`;
const packageRoot = `${globalDirectory}/node_modules/${PRODUCT_IDENTITY.packageName}`;
// Fixture paths are POSIX; join() may produce Windows separators on Windows hosts.
const host = (
manifests: readonly string[] = [`${globalDirectory}/package.json`],
@@ -410,9 +419,9 @@ describe("Bun installation ownership", () => {
});
it.each([
"/home/user/project/node_modules/rea-agents",
"/usr/local/lib/node_modules/rea-agents",
"/home/user/.bun/install/cache/[email protected]@@@1",
`/home/user/project/node_modules/${PRODUCT_IDENTITY.packageName}`,
`/usr/local/lib/node_modules/${PRODUCT_IDENTITY.packageName}`,
`/home/user/.bun/install/cache/${PRODUCT_IDENTITY.packageName}@${PRODUCT_IDENTITY.packageVersion}@@@1`,
])("rejects package root %s outside the global project", async (root) => {
await expect(
detectBunInstallation(root, globalDirectory, host()),
+2 -3
View File
@@ -15,7 +15,7 @@ export interface NpmInstallation {
/** Bun global installation that owns the running package and its entry point. */
export interface BunInstallation {
readonly manager: "bun";
/** Bun's global project directory, which owns `node_modules/rea-agents`. */
/** Bun's global project directory, which owns the package's `node_modules` entry. */
readonly globalDirectory: string;
readonly packageRoot: string;
}
@@ -202,8 +202,7 @@ const runUpdateWorkflow = async (
currentVersion,
latestVersion: targetVersion,
reason: "unknown-install-method",
remediation:
"Update this installation with the package manager or source checkout that owns it. For a one-off latest invocation, use npx rea-agents@latest.",
remediation: `Update this installation with the package manager or source checkout that owns it. For a one-off latest invocation, use npx ${PRODUCT_IDENTITY.packageSpecifier}.`,
};
const installed = await host.installVersion(
installation,
@@ -12,7 +12,7 @@ import {
const installation = {
manager: "npm",
prefix: "/opt/rea",
packageRoot: "/opt/rea/lib/node_modules/rea-agents",
packageRoot: `/opt/rea/lib/node_modules/${PRODUCT_IDENTITY.packageName}`,
} as const;
describe("npm latest release metadata", () => {
+101
View File
@@ -0,0 +1,101 @@
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
captureProcessScenarioFile,
projectProcessCliError,
} from "./ProcessCli.js";
import { JSON_BYTE_ORDER_MARK_MESSAGE } from "../Utf8JsonInput.js";
const roots: string[] = [];
afterEach(async () => {
await Promise.all(
roots.splice(0).map((root) => rm(root, { recursive: true, force: true })),
);
});
const fixture = async (): Promise<string> => {
const root = await mkdtemp(join(tmpdir(), "rea-process-cli-diagnostics-"));
roots.push(root);
return root;
};
describe("process CLI input diagnostics", () => {
it("preserves local diagnostics for unexpected process failures", () => {
const projected = projectProcessCliError(
new Error("SECRET internal failure /private/path"),
);
expect(projected).toEqual({
error: "Process command failed",
category: "execution_failure",
message:
"Process command could not complete: SECRET internal failure /private/path",
});
expect(JSON.stringify(projected)).not.toContain("stack");
});
it("projects missing, malformed JSON, and invalid scenario files", async () => {
const root = await fixture();
const malformed = join(root, "malformed.json");
const invalid = join(root, "invalid.json");
const invalidUtf8 = join(root, "invalid-utf8.json");
await writeFile(malformed, "not-json");
await writeFile(invalid, "{}");
await writeFile(
invalidUtf8,
Buffer.concat([
Buffer.from('{"executable":"'),
Buffer.from([0xff]),
Buffer.from('"}'),
]),
);
expect(
await captureProcessScenarioFile(join(root, "missing.json")),
).toEqual({
error: "Process command failed",
category: "invalid_input",
message: expect.stringContaining(
`Process input file could not be read: ${join(root, "missing.json")} (ENOENT`,
),
});
expect(await captureProcessScenarioFile(malformed)).toEqual({
error: "Process command failed",
category: "invalid_input",
message: expect.stringContaining(
`Process input file is not valid UTF-8 JSON: ${malformed}`,
),
});
expect(await captureProcessScenarioFile(invalidUtf8)).toEqual({
error: "Process command failed",
category: "invalid_input",
message: expect.stringContaining(
`Process input file is not valid UTF-8 JSON: ${invalidUtf8}`,
),
});
expect(await captureProcessScenarioFile(invalid)).toEqual({
error: "Process command failed",
code: "invalid_request",
category: "invalid_input",
message: "Analysis input is invalid. Check the arguments and try again.",
retryable: true,
remediation: { action: "Correct the listed arguments and retry." },
details: expect.objectContaining({
operation: "capture_process_scenario",
issues: expect.arrayContaining([
expect.objectContaining({ path: ["executable"] }),
]),
}),
});
});
it("names a leading byte-order mark instead of quoting it as a token", async () => {
const path = join(await fixture(), "scenario.json");
await writeFile(path, '\uFEFF{"executable":"/bin/echo"}');
expect(await captureProcessScenarioFile(path)).toEqual({
error: "Process command failed",
category: "invalid_input",
message: `Process input file is not valid UTF-8 JSON: ${path} (${JSON_BYTE_ORDER_MARK_MESSAGE}). Repair the file, then try again.`,
});
});
});
+62
View File
@@ -0,0 +1,62 @@
import {
lstat,
mkdtemp,
open,
rm,
symlink,
unlink,
writeFile,
} from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { expect, it } from "vitest";
import { readStableArtifact } from "./readStableArtifact.js";
const checkChange = async (
stage: "before-open" | "after-read" | "symlink-replacement",
): Promise<void> => {
const root = await mkdtemp(join(tmpdir(), "rea-stable-read-"));
const path = join(root, "selected");
await writeFile(path, "original evidence");
let inspected = 0;
try {
const reading = readStableArtifact(path, 1024, undefined, {
async lstat(selected) {
inspected++;
if (stage === "after-read" && inspected === 2) await unlink(selected);
const stat = await lstat(selected, { bigint: true });
if (inspected === 1 && stage !== "after-read") {
await unlink(selected);
if (stage === "symlink-replacement")
await symlink("missing", selected);
}
return stat;
},
open,
});
await expect(reading).rejects.toMatchObject({ reason: "integrity" });
} finally {
await rm(root, { recursive: true, force: true });
}
};
it.each(["before-open", "after-read"] as const)(
"retains an actual post-validation %s change as integrity failure",
checkChange,
);
it.skipIf(process.platform === "win32")(
"retains a post-validation symlink replacement as an integrity failure",
() => checkChange("symlink-replacement"),
);
it("keeps initially missing inputs distinguishable from later changes", async () => {
const root = await mkdtemp(join(tmpdir(), "rea-stable-missing-"));
try {
await expect(
readStableArtifact(join(root, "missing"), 1024),
).rejects.toMatchObject({ code: "ENOENT" });
} finally {
await rm(root, { recursive: true, force: true });
}
});
+116
View File
@@ -0,0 +1,116 @@
import { visit } from "jsonc-parser";
import { LosslessNumber } from "lossless-json";
import { WEB_NETWORK_CAPTURE_LIMITS } from "../../domain/webNetworkCapture.js";
import { CaptureFormatError } from "./CaptureFormatError.js";
type Frame =
| {
kind: "object";
pointer: string;
members: Map<string, unknown>;
pendingKey?: string;
}
| { kind: "array"; pointer: string; items: unknown[] };
/** Materialize upstream JSON events without duplicate folding, prototype setters or numeric rounding. */
export const parseHarJson = (text: string): unknown => {
const frames: Frame[] = [];
let result: unknown;
const valuePointer = (): string => {
const parent = frames.at(-1);
if (parent === undefined) return "";
if (parent.kind === "array")
return `${parent.pointer}/${parent.items.length}`;
return parent.pendingKey === undefined
? parent.pointer
: `${parent.pointer}/${parent.pendingKey.replaceAll("~", "~0").replaceAll("/", "~1")}`;
};
const checkDepth = (): void => {
if (frames.length > WEB_NETWORK_CAPTURE_LIMITS.depth)
throw new CaptureFormatError(
"input-limit",
"HAR exceeds the 64-level complete-evidence nesting budget.",
valuePointer(),
);
};
const append = (value: unknown): void => {
const parent = frames.at(-1);
if (parent === undefined) result = value;
else if (parent.kind === "array") parent.items.push(value);
else {
if (parent.pendingKey === undefined)
throw new TypeError(
"JSON visitor omitted an object property identity.",
);
parent.members.set(parent.pendingKey, value);
delete parent.pendingKey;
}
};
visit(
text,
{
onObjectBegin: () => {
checkDepth();
frames.push({
kind: "object",
pointer: valuePointer(),
members: new Map(),
});
},
onObjectProperty: (key) => {
// Prototype-named members are kept as ordinary own properties: the
// members map keys them without prototype setters, and
// Object.fromEntries materializes them as own data properties.
const frame = frames.at(-1);
if (frame?.kind !== "object")
throw new TypeError(
"JSON visitor emitted a property outside an object.",
);
if (frame.members.has(key))
throw new CaptureFormatError(
"format",
"HAR JSON contains duplicate object members.",
);
frame.pendingKey = key;
},
onObjectEnd: () => {
const frame = frames.pop();
if (frame?.kind !== "object")
throw new TypeError("JSON visitor ended an unexpected object.");
append(Object.fromEntries(frame.members));
},
onArrayBegin: () => {
checkDepth();
frames.push({ kind: "array", pointer: valuePointer(), items: [] });
},
onArrayEnd: () => {
const frame = frames.pop();
if (frame?.kind !== "array")
throw new TypeError("JSON visitor ended an unexpected array.");
append(frame.items);
},
onLiteralValue: (value: unknown, offset, length) => {
checkDepth();
if (typeof value === "number")
append(new LosslessNumber(text.slice(offset, offset + length)));
else if (
value === null ||
typeof value === "boolean" ||
typeof value === "string"
)
append(value);
else
throw new TypeError("JSON visitor emitted an unsupported literal.");
},
onError: () => {
throw new CaptureFormatError("format", "HAR is malformed JSON.");
},
},
{
disallowComments: true,
allowTrailingComma: false,
allowEmptyContent: false,
},
);
return result;
};
@@ -0,0 +1,96 @@
import { fc, it } from "@fast-check/vitest";
import { describe, expect } from "vitest";
import {
createDeclaringTypeLookup,
declaringType,
type TypeRange,
} from "./ManagedMemberInspectorCore.js";
const range = (
name: string,
fieldStart: number,
fieldEnd: number,
methodStart = fieldStart,
methodEnd = fieldEnd,
): TypeRange => ({
token: name,
fullName: name,
fieldStart,
fieldEnd,
methodStart,
methodEnd,
});
describe("managed declaring type ownership", () => {
it("resolves field and method boundaries independently, including empty types and gaps", () => {
const ranges = [
range("first", 1, 3, 1, 1),
range("empty", 3, 3, 1, 1),
range("last", 5, 7, 1, 4),
];
const field = createDeclaringTypeLookup(ranges, "field");
const method = createDeclaringTypeLookup(ranges, "method");
expect([0, 1, 2, 3, 4, 5, 6, 7].map(field)).toEqual([
null,
{ token: "first", fullName: "first" },
{ token: "first", fullName: "first" },
null,
null,
{ token: "last", fullName: "last" },
{ token: "last", fullName: "last" },
null,
]);
expect([0, 1, 2, 3, 4].map(method)).toEqual([
null,
{ token: "last", fullName: "last" },
{ token: "last", fullName: "last" },
{ token: "last", fullName: "last" },
null,
]);
});
it("retains the first observed owner for overlapping, reversed, or out-of-order ranges", () => {
const ranges = [
range("first", 5, 9),
range("overlap", 1, 7),
range("reversed", 10, 2),
];
const lookup = createDeclaringTypeLookup(ranges, "method");
expect(lookup(6)).toEqual({ token: "first", fullName: "first" });
expect(lookup(2)).toEqual({ token: "overlap", fullName: "overlap" });
expect(lookup(10)).toBeNull();
expect(createDeclaringTypeLookup([], "method")(1)).toBeNull();
});
it.prop([
fc.array(
fc.record({
fieldStart: fc.integer({ min: 0, max: 30 }),
fieldEnd: fc.integer({ min: 0, max: 30 }),
methodStart: fc.integer({ min: 0, max: 30 }),
methodEnd: fc.integer({ min: 0, max: 30 }),
}),
{ maxLength: 20 },
),
])(
"agrees with observed first-match ownership for arbitrary metadata ranges",
(raw) => {
const ranges = raw.map((value, index) => ({
...value,
token: String(index),
fullName: `Type${String(index)}`,
}));
for (const table of ["field", "method"] as const) {
const lookup = createDeclaringTypeLookup(ranges, table);
for (const row of [
NaN,
-Infinity,
Infinity,
...Array.from({ length: 32 }, (_, i) => i),
])
expect(lookup(row)).toEqual(declaringType(ranges, table, row));
}
},
);
});
+29
View File
@@ -49,6 +49,35 @@ export interface TypeRange {
readonly methodEnd: number;
}
export type ManagedMemberTable = "field" | "method";
/** Return the first TypeDef whose observed metadata range owns this row. */
export const declaringType = (
ranges: readonly TypeRange[],
table: ManagedMemberTable,
row: number,
): Pick<TypeRange, "token" | "fullName"> | null => {
if (!Number.isSafeInteger(row) || row < 1) return null;
const startKey = table === "field" ? "fieldStart" : "methodStart";
const endKey = table === "field" ? "fieldEnd" : "methodEnd";
for (const range of ranges) {
const start = range[startKey];
const end = range[endKey];
if (start <= row && row < end)
return { token: range.token, fullName: range.fullName };
}
return null;
};
/** Build an immutable row lookup while preserving the input's first-match ownership. */
export const createDeclaringTypeLookup =
(
ranges: readonly TypeRange[],
table: ManagedMemberTable,
): ((row: number) => Pick<TypeRange, "token" | "fullName"> | null) =>
(row) =>
declaringType(ranges, table, row);
export interface MethodCore {
readonly token: string;
readonly name: string;
+5 -1
View File
@@ -212,7 +212,11 @@
"scripts/generate-completion-ledger.mjs",
"scripts/lib/generated-file.mjs",
"scripts/verify/managed/**",
"scripts/fixtures/managed/**"
"scripts/fixtures/managed/**",
{
"mode": "dependencyOutputs",
"globs": ["skills/**"]
}
],
"outputs": [
"docs/verification/managed-conformance-manifest.json",