diff --git a/src/resources/extensions/gsd/crash-recovery.ts b/src/resources/extensions/gsd/crash-recovery.ts index 1186d5ed8..9d5caa8ef 100644 --- a/src/resources/extensions/gsd/crash-recovery.ts +++ b/src/resources/extensions/gsd/crash-recovery.ts @@ -76,12 +76,16 @@ export function readCrashLock(basePath: string): LockData | null { /** * Check whether the process that wrote the lock is still running. * Uses `process.kill(pid, 0)` which sends no signal but checks liveness. - * Returns false if the PID matches our own (recycled PID from a prior run). + * Returns true if the PID matches our own — we are the lock holder (#2470). */ export function isLockProcessAlive(lock: LockData): boolean { const pid = lock.pid; if (!Number.isInteger(pid) || pid <= 0) return false; - if (pid === process.pid) return false; + // Our own PID means WE hold this lock — we are alive. (#2470) + // Callers that need to distinguish "our lock" from "someone else's lock" + // (e.g. startAuto checking for a prior crashed session with a recycled PID) + // already guard with `crashLock.pid !== process.pid` before calling us. + if (pid === process.pid) return true; try { process.kill(pid, 0); return true; diff --git a/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts b/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts index 5189e96f0..0ff8d963e 100644 --- a/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +++ b/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts @@ -140,7 +140,7 @@ test("isLockProcessAlive returns false for dead PID", () => { assert.equal(isLockProcessAlive(lock), false, "dead PID should return false"); }); -test("isLockProcessAlive returns false for own PID (recycled)", () => { +test("#2470: isLockProcessAlive returns true for own PID (we hold the lock)", () => { const lock = { pid: process.pid, startedAt: new Date().toISOString(), @@ -148,7 +148,7 @@ test("isLockProcessAlive returns false for own PID (recycled)", () => { unitId: "M001/S01/T01", unitStartedAt: new Date().toISOString(), }; - assert.equal(isLockProcessAlive(lock), false, "own PID should return false (recycled)"); + assert.equal(isLockProcessAlive(lock), true, "own PID means we are alive — not stale (#2470)"); }); test("isLockProcessAlive returns false for invalid PID", () => { diff --git a/src/resources/extensions/gsd/tests/crash-recovery.test.ts b/src/resources/extensions/gsd/tests/crash-recovery.test.ts index 7c34599e1..39323681f 100644 --- a/src/resources/extensions/gsd/tests/crash-recovery.test.ts +++ b/src/resources/extensions/gsd/tests/crash-recovery.test.ts @@ -68,8 +68,10 @@ test("clearLock is safe when no lock exists", (t) => { // ─── isLockProcessAlive ────────────────────────────────────────────────── -test("isLockProcessAlive returns true for current process (different pid)", () => { - // Our own PID is explicitly excluded (recycled PID guard) +test("#2470: isLockProcessAlive returns true for own PID (we hold the lock)", () => { + // Own PID means we ARE the lock holder — alive, not stale. (#2470) + // Callers that need recycled-PID detection (e.g. startAuto) already + // guard with `crashLock.pid !== process.pid` before calling us. const lock: LockData = { pid: process.pid, startedAt: new Date().toISOString(), @@ -77,7 +79,7 @@ test("isLockProcessAlive returns true for current process (different pid)", () = unitId: "M001/S01/T01", unitStartedAt: new Date().toISOString(), }; - assert.equal(isLockProcessAlive(lock), false, "own PID should return false"); + assert.equal(isLockProcessAlive(lock), true, "own PID should return true — we are alive"); }); test("isLockProcessAlive returns false for dead PID", () => {