mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 00:07:18 +00:00
* feat(native): retain LLDB source coordinates and verify Pascal call entries Preserve partial line-table attribution for native locations and historical evidence compatibility. Verify owned Free Pascal call entries through CLI/MCP. Refs #2382, #2372, #2397 Co-Authored-By: Codex <noreply@openai.com> * docs: namespace native specifications by domain and feature Use stable descriptive paths instead of numeric prefixes to avoid parallel contribution collisions. Co-Authored-By: Codex <noreply@openai.com> * fix(native): bound Pascal verification and compare caller coordinates Address review findings at their producing boundaries and preserve directly affected consumers. Source inspection and git diff --check completed. Tests and automated checks were not run at the user’s explicit request. * fix(native): bound compiler shutdown and source-coordinate fixtures * docs: clarify historical native source verification * fix(native): preserve source attribution and compiler ownership * docs(native): qualify retained source attribution * fix(ci): clean up native Pascal verifier processes * test(native): type the Pascal verifier helper import after merging main Main now typechecks this boundary test, which imports the untyped canonical-temporary-directory helper and reads an unknown catch value. Add the helper's declaration file and narrow the caught error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(native): verify partial source attribution caveat * fix(native): retain verifier compiler cleanup ownership * fix(native): forward verifier cancellation and measure path bytes CLI and MCP artifact runs observe the verifier abort signal, and a failed Pascal verifier test stops the compiler group before deleting its directory. Repeated source locations grow by measured UTF-8 JSON bytes; workflow tokens, CPU and peak memory stay unmeasured. --------- Co-authored-by: Matheus Breguêz <matbrgz@gmail.com> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
124 lines
4.2 KiB
TypeScript
124 lines
4.2 KiB
TypeScript
import { spawn } from "node:child_process";
|
|
import {
|
|
chmod,
|
|
readFile,
|
|
readdir,
|
|
realpath,
|
|
rm,
|
|
symlink,
|
|
writeFile,
|
|
} from "node:fs/promises";
|
|
import { basename, join } from "node:path";
|
|
import { expect, it } from "vitest";
|
|
import { createCanonicalTemporaryDirectory } from "../../../scripts/lib/canonical-temporary-directory.mjs";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
it("uses the canonical spelling for a verifier directory allocated through a symlink", async () => {
|
|
const root = await createTestTempDirectory("rea-pascal-verifier-path-");
|
|
const alias = join(root, "alias");
|
|
await symlink(root, alias, process.platform === "win32" ? "junction" : "dir");
|
|
const allocated = await createCanonicalTemporaryDirectory(
|
|
"rea-pascal-calls-",
|
|
alias,
|
|
);
|
|
try {
|
|
expect(allocated.path).toBe(await realpath(allocated.allocatedPath));
|
|
expect(allocated.path).not.toBe(
|
|
join(alias, basename(allocated.allocatedPath)),
|
|
);
|
|
} finally {
|
|
await rm(allocated.allocatedPath, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it.skipIf(process.platform !== "darwin")(
|
|
"forwards verifier SIGTERM to the owned compiler group and removes its temporary directory",
|
|
async () => {
|
|
const root = await createTestTempDirectory("rea-pascal-verifier-cancel-");
|
|
const fakeCompiler = join(root, "fake-fpc");
|
|
const pidFile = join(root, "compiler-pids.txt");
|
|
await writeFile(
|
|
fakeCompiler,
|
|
'#!/usr/bin/env node\nimport { spawn } from "node:child_process";\nimport { writeFileSync } from "node:fs";\nconst child = spawn(process.execPath, ["-e", "setInterval(() => undefined, 1000)"], { stdio: "ignore" });\nwriteFileSync(process.env.REA_VERIFY_TEST_PID_FILE, `${process.pid} ${child.pid}\\n`);\nawait new Promise(() => undefined);\n',
|
|
);
|
|
await chmod(fakeCompiler, 0o700);
|
|
const verifier = spawn(
|
|
process.execPath,
|
|
["scripts/verify-native-pascal-calls.mjs"],
|
|
{
|
|
cwd: process.cwd(),
|
|
env: {
|
|
...process.env,
|
|
TMPDIR: root,
|
|
REA_VERIFY_FPC_COMMAND: fakeCompiler,
|
|
REA_VERIFY_TEST_PID_FILE: pidFile,
|
|
},
|
|
stdio: "ignore",
|
|
},
|
|
);
|
|
try {
|
|
const deadline = Date.now() + 10_000;
|
|
let pids: number[] | undefined;
|
|
while (Date.now() < deadline) {
|
|
try {
|
|
pids = (await readFile(pidFile, "utf8"))
|
|
.trim()
|
|
.split(/\s+/u)
|
|
.map(Number);
|
|
break;
|
|
} catch (cause: unknown) {
|
|
if ((cause as NodeJS.ErrnoException).code !== "ENOENT") throw cause;
|
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
}
|
|
}
|
|
expect(pids, "the fake compiler started").toHaveLength(2);
|
|
verifier.kill("SIGTERM");
|
|
const result = await new Promise<{
|
|
code: number | null;
|
|
signal: NodeJS.Signals | null;
|
|
}>((resolve, reject) => {
|
|
verifier.once("error", reject);
|
|
verifier.once("close", (code, signal) => resolve({ code, signal }));
|
|
});
|
|
expect(result).toEqual({ code: 143, signal: null });
|
|
for (const pid of pids ?? [])
|
|
expect(() => process.kill(pid, 0)).toThrow();
|
|
expect(
|
|
(await readdir(root)).filter((name) =>
|
|
name.startsWith("rea-pascal-calls-"),
|
|
),
|
|
).toEqual([]);
|
|
} finally {
|
|
if (verifier.exitCode === null && verifier.signalCode === null) {
|
|
verifier.kill("SIGTERM");
|
|
await new Promise((resolve) => {
|
|
const timer = setTimeout(() => {
|
|
verifier.kill("SIGKILL");
|
|
resolve(undefined);
|
|
}, 1_000);
|
|
verifier.once("close", () => {
|
|
clearTimeout(timer);
|
|
resolve(undefined);
|
|
});
|
|
});
|
|
}
|
|
try {
|
|
const recorded = (await readFile(pidFile, "utf8"))
|
|
.trim()
|
|
.split(/\s+/u)
|
|
.map(Number);
|
|
for (const pid of recorded) {
|
|
try {
|
|
process.kill(pid, "SIGKILL");
|
|
} catch {
|
|
// The owned compiler group already exited.
|
|
}
|
|
}
|
|
} catch (cause: unknown) {
|
|
if ((cause as NodeJS.ErrnoException).code !== "ENOENT") throw cause;
|
|
}
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
},
|
|
);
|