From dab2ccb2c34904546da7c7f394b6011017a45fd0 Mon Sep 17 00:00:00 2001 From: Behruz Nassre Esfahani Date: Mon, 20 Jul 2026 17:52:09 -0700 Subject: [PATCH] fix: preserve sandboxed background jobs on EPERM Treat permission-denied zero-signal probes as evidence that a process exists so the stale-job reaper does not fail live Claude jobs. Add deterministic EPERM and ESRCH coverage.\n\nRefs #79 --- scripts/lib/process.mjs | 9 +++++---- tests/process.test.mjs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/scripts/lib/process.mjs b/scripts/lib/process.mjs index ba532df..0e91d78 100644 --- a/scripts/lib/process.mjs +++ b/scripts/lib/process.mjs @@ -163,11 +163,12 @@ export function validateProcessIdentity(pid, expectedIdentity) { } } -export function isProcessAlive(pid) { +export function isProcessAlive(pid, options = {}) { + const killImpl = options.killImpl ?? process.kill.bind(process); try { - process.kill(pid, 0); + killImpl(pid, 0); return true; - } catch { - return false; + } catch (error) { + return error?.code === "EPERM"; } } diff --git a/tests/process.test.mjs b/tests/process.test.mjs index b6514b6..07b1ecf 100644 --- a/tests/process.test.mjs +++ b/tests/process.test.mjs @@ -324,6 +324,36 @@ describe("isProcessAlive", () => { // PID 99999999 is extremely unlikely to exist assert.equal(isProcessAlive(99999999), false); }); + + it("treats EPERM as alive when the sandbox denies the probe", () => { + const permissionError = Object.assign(new Error("operation not permitted"), { + code: "EPERM", + }); + + assert.equal( + isProcessAlive(12345, { + killImpl: () => { + throw permissionError; + }, + }), + true, + ); + }); + + it("treats ESRCH as dead when the process does not exist", () => { + const missingError = Object.assign(new Error("no such process"), { + code: "ESRCH", + }); + + assert.equal( + isProcessAlive(12345, { + killImpl: () => { + throw missingError; + }, + }), + false, + ); + }); }); // ---------------------------------------------------------------------------