fix: retain matching-host runner development overrides

Use the controller override only for matching remote OS and CPU, preserving explicit remote priority and strict target header verification.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-12 14:34:41 -05:00
1 parent e79254b2fa
commit bb7591dfd0
3 files changed
+75 -3

No files matched your search

+20 -1
View File
@@ -78,7 +78,26 @@ These corrections update server setup/execution and guide source. The qualified
`a200495` package startup and historical task evidence retain their literal
component boundaries; no new managed live proof or paid turn is claimed.
Next action: require fresh CI/review on the corrected integrated candidate, refresh the
Fresh review at `e79254b` verifies the preceding fixes and reports one valid
development override edge. The common remote resolver now preserves
`PAPERCLIP_RUNNER_BINARY` only when the measured target OS/CPU matches the
controller. Explicit remote overrides retain priority; every selected artifact
still requires a matching native header. Cross-target selection ignores the
controller override. Seven new baseline failures reproduce the defect; 133
target/setup/backend/retained/version controls and server typecheck pass after
the correction. Receipt:
`/private/tmp/codex-default-same-host-override-fix-receipt.json`.
The `e79254b` preview restart preserves all four configurations and both
historical native runs. Its actual served guide now reports the shared GNU floor
and qualified Mac/ARM targets. No native build, Docker build, or provider call
runs locally. Main CI's selector remains unallocated; other PRs share this queue.
No environment approval, owned concurrency conflict, or routing failure was
found. Org capacity versus GitHub allocation is unconfirmed. Receipt:
`/private/tmp/pr2-e792-selector-queue-investigation.json`. Required final CI
aggregates and fresh review remain pending, not passed.
Next action: require fresh CI/review on the final corrected candidate, refresh the
matched preview, and publish final conclusions in the canonical PR's evidence
index. Human code-owner approval, managed staging onboarding, and actual
post-merge Cloud composition remain outstanding. No merge or production
@@ -12,7 +12,7 @@ import {
resolveRemotePaperclipRunnerBinary,
} from "./native-codex-runner.js";
afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); });
afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); vi.unstubAllEnvs(); });
describe("installed runner selection", () => {
it("uses the runner's platform-aware packaged selection", async () => {
@@ -79,6 +79,9 @@ describe("selected remote daemon target", () => {
try {
vi.stubGlobal("process", { ...process, platform: "darwin", arch: controllerArch });
await writeFile(binary, header(`linux-${targetArch}`));
const controllerBinary = join(root, "controller-runnerd");
await writeFile(controllerBinary, header(`darwin-${controllerArch}`), { mode: 0o700 });
vi.stubEnv("PAPERCLIP_RUNNER_BINARY", controllerBinary);
const select = vi.spyOn(runner, "defaultCapabilityRunnerdBinary").mockReturnValue(binary);
const target = remote(`Linux\n${targetArch === "arm64" ? "aarch64" : "x86_64"}\n`);
expect(await resolveRemotePaperclipRunnerBinary({ runner: target, remoteCwd: "/sandbox-workspace" })).toBe(binary);
@@ -87,10 +90,54 @@ describe("selected remote daemon target", () => {
} finally { await rm(root, { recursive: true, force: true }); }
},
);
it.each(["linux", "darwin"].flatMap(platform => ["arm64", "x64"].map(architecture => ({ platform, architecture }))))(
"prefers the supervisor's fresh $platform/$architecture override for the same-host target", async ({ platform, architecture }) => {
const root = await mkdtemp(join(tmpdir(), "paperclip-same-host-daemon-"));
try {
vi.stubGlobal("process", { ...process, platform, arch: architecture });
const fresh = join(root, "fresh-runnerd"), stale = join(root, "stale-packaged-runnerd");
await writeFile(fresh, Buffer.concat([header(`${platform}-${architecture}`), Buffer.from("fresh")]), { mode: 0o700 });
await writeFile(stale, Buffer.concat([header(`${platform}-${architecture}`), Buffer.from("stale")]), { mode: 0o700 });
vi.stubEnv("PAPERCLIP_RUNNER_BINARY", fresh);
const select = vi.spyOn(runner, "defaultCapabilityRunnerdBinary").mockReturnValue(stale);
const target = remote(`${platform === "linux" ? "Linux" : "Darwin"}\n${architecture === "arm64" ? "arm64" : "x86_64"}\n`);
expect(await resolveRemotePaperclipRunnerBinary({ runner: target, remoteCwd: "/workspace" })).toBe(fresh);
expect(select).not.toHaveBeenCalled();
} finally { await rm(root, { recursive: true, force: true }); }
},
);
it.each(["missing", "relative", "malformed", "wrong-architecture"])("retains same-host override policy and target validation (%s)", async scenario => {
const root = await mkdtemp(join(tmpdir(), "paperclip-same-host-daemon-"));
try {
vi.stubGlobal("process", { ...process, platform: "linux", arch: "x64" });
const override = join(root, "configured-runnerd"), fallback = join(root, "packaged-runnerd");
if (scenario !== "missing") await writeFile(override, scenario === "malformed" ? Buffer.alloc(64) : header("linux-arm64"), { mode: 0o700 });
await writeFile(fallback, header("linux-x64"), { mode: 0o700 });
vi.stubEnv("PAPERCLIP_RUNNER_BINARY", scenario === "relative" ? "relative-runnerd" : override);
vi.spyOn(runner, "defaultCapabilityRunnerdBinary").mockReturnValue(fallback);
const request = resolveRemotePaperclipRunnerBinary({ runner: remote("Linux\nx86_64\n"), remoteCwd: "/workspace" });
if (scenario === "missing") expect(await request).toBe(fallback);
else await expect(request).rejects.toThrow(scenario === "relative" ? "PAPERCLIP_RUNNER_BINARY must be an absolute path" : "runner_remote_artifact_platform_mismatch");
} finally { await rm(root, { recursive: true, force: true }); }
});
it("ignores a relative controller override when the actual target differs", async () => {
const root = await mkdtemp(join(tmpdir(), "paperclip-cross-host-daemon-"));
try {
vi.stubGlobal("process", { ...process, platform: "darwin", arch: "arm64" });
vi.stubEnv("PAPERCLIP_RUNNER_BINARY", "relative-controller-runnerd");
const binary = join(root, "linux-runnerd");
await writeFile(binary, header("linux-arm64"));
const select = vi.spyOn(runner, "defaultCapabilityRunnerdBinary").mockReturnValue(binary);
expect(await resolveRemotePaperclipRunnerBinary({ runner: remote("Linux\naarch64\n"), remoteCwd: "/workspace" })).toBe(binary);
expect(select).toHaveBeenCalledExactlyOnceWith("linux", "arm64");
} finally { await rm(root, { recursive: true, force: true }); }
});
it.each(["matching", "wrong-architecture", "malformed", "missing"])("verifies an explicit controller-owned remote artifact (%s)", async scenario => {
const root = await mkdtemp(join(tmpdir(), "paperclip-ssh-daemon-"));
const binary = join(root, "configured-runnerd");
try {
vi.stubGlobal("process", { ...process, platform: "linux", arch: "arm64" });
vi.stubEnv("PAPERCLIP_RUNNER_BINARY", "invalid-relative-controller-override");
if (scenario !== "missing") await writeFile(binary, scenario === "malformed" ? Buffer.alloc(64) : header(scenario === "matching" ? "linux-arm64" : "linux-x64"));
const select = vi.spyOn(runner, "defaultCapabilityRunnerdBinary");
const request = resolveRemotePaperclipRunnerBinary({ runner: remote("Linux\naarch64\n"), remoteCwd: "/workspace", configuredPath: binary });
@@ -224,7 +224,13 @@ export async function resolveRemotePaperclipRunnerBinary(input: {
}
const configured = input.configuredPath?.trim();
if (configured && !isAbsolute(configured)) throw new Error("PAPERCLIP_RUNNER_REMOTE_BINARY_PATH must be an absolute path");
const binary = configured || defaultCapabilityRunnerdBinary(platform, architecture);
// A same-host sandbox can use the supervisor's fresh development artifact.
// Cross-platform targets must never inherit the controller's override.
const controllerOverride = platform === process.platform && architecture === process.arch
? process.env.PAPERCLIP_RUNNER_BINARY : undefined;
const binary = configured || (controllerOverride
? resolvePaperclipRunnerBinary(controllerOverride)
: defaultCapabilityRunnerdBinary(platform, architecture));
let bytes: Buffer;
try { bytes = readFileSync(binary); }
catch (cause) { throw new Error(`runner_remote_artifact_unavailable: install the Paperclip release containing the ${platform}-${architecture} daemon or configure PAPERCLIP_RUNNER_REMOTE_BINARY_PATH`, { cause }); }