From 5bacf5a4b69986f625b33cd269c0dfdb3a96b271 Mon Sep 17 00:00:00 2001 From: abhigyantrumio Date: Tue, 12 May 2026 20:00:51 +0530 Subject: [PATCH] fix(hooks): distinguish EPERM from ESRCH in PID liveness check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cursor Bugbot caught a contradiction with the stated design: the bare `catch` after `process.kill(owner, 0)` was treating EPERM (process exists but owned by another user) the same as ESRCH (process gone), which would evict a live slot whenever the lock dir straddled user boundaries. Inspect the error code: ESRCH → dead, evict; EPERM → still alive, keep the slot; anything else → assume alive (be conservative under unexpected failure rather than over-evict). Applied to all three hook variants. Co-Authored-By: Claude Opus 4.7 (1M context) --- gitnexus-claude-plugin/hooks/gitnexus-hook.js | 11 +++++++++-- gitnexus-cursor-integration/hooks/gitnexus-hook.cjs | 11 +++++++++-- gitnexus/hooks/claude/gitnexus-hook.cjs | 11 +++++++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/gitnexus-claude-plugin/hooks/gitnexus-hook.js b/gitnexus-claude-plugin/hooks/gitnexus-hook.js index 5d0c722e1..577e7f16c 100644 --- a/gitnexus-claude-plugin/hooks/gitnexus-hook.js +++ b/gitnexus-claude-plugin/hooks/gitnexus-hook.js @@ -248,8 +248,15 @@ function acquireHookSlot(gitNexusDir) { try { process.kill(owner, 0); isLive = true; - } catch { - /* ESRCH (or EPERM under cross-user) — treat as dead */ + } catch (e) { + // ESRCH = process gone → treat as dead. EPERM = process exists + // but owned by another user (cross-user lock dir) → still alive, + // keep the slot. Anything else: be conservative, assume alive. + if (e && e.code === 'ESRCH') { + isLive = false; + } else { + isLive = true; + } } } } diff --git a/gitnexus-cursor-integration/hooks/gitnexus-hook.cjs b/gitnexus-cursor-integration/hooks/gitnexus-hook.cjs index 245c5fcfc..d4c3035fb 100644 --- a/gitnexus-cursor-integration/hooks/gitnexus-hook.cjs +++ b/gitnexus-cursor-integration/hooks/gitnexus-hook.cjs @@ -278,8 +278,15 @@ function acquireHookSlot(gitNexusDir) { try { process.kill(owner, 0); isLive = true; - } catch { - /* ESRCH (or EPERM under cross-user) — treat as dead */ + } catch (e) { + // ESRCH = process gone → treat as dead. EPERM = process exists + // but owned by another user (cross-user lock dir) → still alive, + // keep the slot. Anything else: be conservative, assume alive. + if (e && e.code === 'ESRCH') { + isLive = false; + } else { + isLive = true; + } } } } diff --git a/gitnexus/hooks/claude/gitnexus-hook.cjs b/gitnexus/hooks/claude/gitnexus-hook.cjs index 7db0e23e1..23baf9af1 100755 --- a/gitnexus/hooks/claude/gitnexus-hook.cjs +++ b/gitnexus/hooks/claude/gitnexus-hook.cjs @@ -248,8 +248,15 @@ function acquireHookSlot(gitNexusDir) { try { process.kill(owner, 0); isLive = true; - } catch { - /* ESRCH (or EPERM under cross-user) — treat as dead */ + } catch (e) { + // ESRCH = process gone → treat as dead. EPERM = process exists + // but owned by another user (cross-user lock dir) → still alive, + // keep the slot. Anything else: be conservative, assume alive. + if (e && e.code === 'ESRCH') { + isLive = false; + } else { + isLive = true; + } } } }