Skip to content

fix(liveness): treat kill -0 EPERM as alive under the command sandbox#447

Open
TakahiroHaruyama wants to merge 1 commit into
fujibee:mainfrom
TakahiroHaruyama:fix/sandbox-eperm-liveness
Open

fix(liveness): treat kill -0 EPERM as alive under the command sandbox#447
TakahiroHaruyama wants to merge 1 commit into
fujibee:mainfrom
TakahiroHaruyama:fix/sandbox-eperm-liveness

Conversation

@TakahiroHaruyama

Copy link
Copy Markdown

Problem

Under Claude Code's command sandbox, kill -0 <pid> on a live process outside the current session returns EPERM ("Operation not permitted"), not ESRCH ("No such process"). The liveness checks treated any non-zero kill -0 exit as "dead", so a live parent/sibling pid was misread as dead, causing:

  • the monitor watcher to self-exit on its first loop (silent monitor outage);
  • SessionStart/SessionEnd GC to kill live watchers / delete live project markers.

Fix

  • New helper _agmsg_pid_alive (scripts/lib/instance-id.sh): inspects kill -0 stderr and treats only No such process (ESRCH) as dead; every other failure — EPERM included — is alive.
  • Routed the destructive / dedup liveness gates through it: session-start.sh, session-end.sh, check-inbox.sh, watch.sh prev-watcher dedup, resolve-project.sh marker GC (+ a guard: skip GC if the helper isn't loaded), and the emit_monitor_directive dedup in delivery.sh / grok-build.
  • Left as raw kill -0 by design: the codex "wait until a pid dies" loops (fail-alive would hang them), and non-destructive checks whose ps follow-up fails under the sandbox anyway.

Tests

  • test_instance_id.bats: _agmsg_pid_alive for real live/dead pids and stubbed signalable / ESRCH / EPERM / unknown-error cases.
  • test_resolve_project.bats: marker GC keeps on EPERM, drops on ESRCH, skips when the helper is absent.
  • Verified locally with bats tests/ (green except pre-existing sandbox-only failures unrelated to this change).

🤖 Generated with Claude Code

Under Claude Code's command sandbox, `kill -0 <pid>` on a live process
outside the current session returns EPERM ("Operation not permitted"),
not ESRCH ("No such process"). The liveness checks treated any non-zero
`kill -0` exit as dead, so a live parent/sibling pid was misread as dead,
causing the monitor watcher to self-exit (silent outage) and the
SessionStart/SessionEnd GC to kill live watchers / delete live markers.

- _agmsg_pid_alive now inspects kill -0 stderr and treats only ESRCH as
  dead; every other failure (EPERM included) is alive.
- Route the destructive / dedup liveness gates through it: session-start,
  session-end, check-inbox, watch.sh prev-watcher dedup, resolve-project
  marker GC (+ guard when the helper isn't loaded), and the
  emit_monitor_directive dedup in delivery.sh / grok-build.
- Leave "wait until a pid dies" loops and non-destructive checks as raw
  kill -0 by design.

Tests: _agmsg_pid_alive ESRCH/EPERM/unknown cases and marker-GC keep/drop/guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant