Skip to content

fix: fall back to the liveness probe when the identity probe is denied - #88

Merged
sf-jin-ku merged 1 commit into
mainfrom
fix/identity-probe-denied-83
Jul 29, 2026
Merged

fix: fall back to the liveness probe when the identity probe is denied#88
sf-jin-ku merged 1 commit into
mainfrom
fix/identity-probe-denied-83

Conversation

@sf-jin-ku

Copy link
Copy Markdown
Contributor

Summary

validateProcessIdentity() collapsed "the probe was denied" into "the process is dead" — the same defect #79 reported for isProcessAlive(), at the branch #80 deliberately left alone.

Inside a Codex workspace-write sandbox, spawnSync ps fails with EPERM unconditionally (not only for non-descendants, the way the zero-signal probe does), so getProcessIdentity() always throws, the bare catch returns false, and reapStaleJobs() auto-reaps every live job that recorded a pidIdentity.

Why it was latent

claude-cli.mjs swallows the identical ps failure at spawn time and persists pidIdentity: null, so the reaper falls through to isProcessAlive() — the branch #80 fixed. That is two independent failures cancelling, not a design, and it breaks as soon as a job is spawned where ps is permitted and reaped where it is not.

Fix

Treat EPERM/EACCES from the identity probe as "identity is unknowable" and defer to isProcessAlive(pid), which still reports ESRCH for a genuinely dead PID. One liveness answer, at the same boundary #80 established:

  • genuine identity mismatch → not alive (unchanged)
  • absent PID / ps exiting non-zero → not alive (unchanged)
  • denied probe → whatever the zero-signal probe says

PID-reuse detection is only given up in the case where identity cannot be established at all, which is the alternative the issue itself proposes.

Validation

  • new test fails before the production change (pass 34) and passes after (pass 35)
  • both denial branches covered deterministically via an injected identityImpl/killImpl, matching the pattern fix: preserve sandboxed background jobs on EPERM #80 introduced
  • unit: 509 passed, 0 failed; lint + typecheck clean

Closes #83

🤖 Generated with Claude Code

validateProcessIdentity() collapsed "the probe was denied" into "the
process is dead", the same defect #79 reported for isProcessAlive().
Inside a Codex workspace-write sandbox `spawnSync ps` fails with EPERM
unconditionally, so every job that recorded a pidIdentity would be
auto-reaped while still running. It is latent today only because
claude-cli.mjs swallows the same ps failure at spawn time and persists
pidIdentity: null.

Treat EPERM/EACCES from the identity probe as "identity unknowable" and
defer to isProcessAlive(), which still reports ESRCH for a dead PID.
Genuine mismatches and absent PIDs stay classified as not alive, so
PID-reuse detection is only given up when identity cannot be established
at all.

Closes #83

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sf-jin-ku
sf-jin-ku merged commit f38aeed into main Jul 29, 2026
5 checks passed
@sf-jin-ku
sf-jin-ku deleted the fix/identity-probe-denied-83 branch July 29, 2026 07:04

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df13c13771

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/lib/process.mjs
Comment on lines +167 to +168
if (error?.code === "EPERM" || error?.code === "EACCES") {
return isProcessAlive(pid, options);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not treat an unverified PID as an identity match

When the identity probe is denied and the original PID has been reused, the zero-signal check succeeds for the unrelated process, so this function incorrectly reports that its identity matches. cancelClaudeProcess() relies on this result at lines 940 and 961 before sending SIGTERM/SIGKILL to the process group, meaning cancellation can terminate an unrelated process; preserve an unknown/denied state so liveness fallback is used by the stale-job reaper without bypassing cancellation's identity check, and cover that caller behavior.

AGENTS.md reference: AGENTS.md:L5-L5

Useful? React with 👍 / 👎.

@sf-jin-ku sf-jin-ku mentioned this pull request Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

validateProcessIdentity() treats a denied ps probe as process death — same defect as #79, unfixed by #80

1 participant