hermes-agent/tools/environments
claude-ceo-assistant d6fca4f623
Some checks failed
Contributor Attribution Check / check-attribution (pull_request) Failing after 15s
Supply Chain Audit / Scan PR for critical supply chain risks (pull_request) Successful in 20s
Nix / nix (ubuntu-latest) (pull_request) Failing after 9m23s
Tests / test (pull_request) Failing after 11m50s
Tests / e2e (pull_request) Successful in 1m4s
Nix / nix (macos-latest) (pull_request) Has been cancelled
fix(local-env): use SIGKILL directly on cleanup, don't wait for zombie group reap
Two real bugs in tools/environments/local.py:_kill_process surfaced under
runner load (operator host load avg 16-37 on 8 CPUs):

1. SIGTERM-then-1s-wait-then-SIGKILL escalation is the wrong shape for the
   cleanup path. _kill_process is invoked from base.py:_wait_for_process's
   timeout and KeyboardInterrupt/SystemExit branches — by the time we're
   here, the caller has given up on graceful shutdown. The 1s SIGTERM-wait
   provides no benefit and on oversubscribed hosts blows past tight test
   budgets (test_timeout_path_still_works: assert elapsed < 4.0s, was 5.05s).
   Send SIGKILL directly: it's unblockable and the kernel processes it
   synchronously.

2. _wait_for_group_exit polled os.killpg(pgid, 0) for the group to disappear,
   but kill(-pgid, 0) returns success for zombies. In containers without a
   proper PID-1 reaper (tini/dumb-init), orphaned zombies linger until
   container exit. The function sat in its 2.0s ceiling waiting for kernel
   bookkeeping that would never happen. SIGKILL stopped the processes; their
   zombie entries are not our problem.

Replace both with a single os.killpg(SIGKILL) + proc.wait(timeout=0.5) to
reap our direct child. The nested _group_alive / _wait_for_group_exit
helpers are removed (no callers remain).

Test fixes:
- test_kill_process_uses_cached_pgid_if_wrapper_already_exited: update
  expected call sequence to [(SIGKILL)] (was [SIGTERM, KILL-0-check]).
- _pgid_still_alive helper: use ps STAT field to skip zombies, instead of
  os.killpg(pgid, 0). Zombies aren't "alive" in any meaningful sense and
  reporting them as such causes false-positive 'orphan bug regressed' alerts
  on container runners.

Verified: tests/tools/test_local_interrupt_cleanup.py +
tests/tools/test_local_background_child_hang.py — all 10 tests pass 3/3
runs on the act_runner image (load avg 16+).

Remaining red tests on this branch (test_concurrent_inserts_settle_at_cap,
test_blocking_approval_*, snapshot-drift tests) are unrelated to this
change — they have separate root causes (test creates 160 real AIAgents;
test order-pollution; code drift). Tracking separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 02:33:43 +00:00
..
__init__.py feat(environments): add Daytona cloud sandbox backend 2026-03-05 10:02:21 -08:00
base.py fix(cli): respect terminal.cwd config in local terminal backend 2026-04-28 22:16:08 -07:00
daytona.py fix: harden sync_back — PID-suffix temp path, size cap, lifecycle guards 2026-04-16 19:39:21 -07:00
docker.py feat(docker): run container as host user to avoid root-owned bind mounts 2026-04-29 16:16:43 +10:00
file_sync.py fix(ci): stabilize current main test regressions 2026-04-30 06:36:50 -07:00
local.py fix(local-env): use SIGKILL directly on cleanup, don't wait for zombie group reap 2026-05-08 02:33:43 +00:00
managed_modal.py feat(environments): unified spawn-per-call execution layer 2026-04-08 17:23:15 -07:00
modal_utils.py fix: follow-up for salvaged PR #10854 2026-04-16 06:42:45 -07:00
modal.py feat(file-sync): sync remote changes back to host on teardown 2026-04-16 19:39:21 -07:00
singularity.py feat(environments): unified spawn-per-call execution layer 2026-04-08 17:23:15 -07:00
ssh.py fix(ssh): prevent tar from overwriting remote home dir permissions 2026-04-30 03:26:35 -07:00
vercel_sandbox.py fix: address self-review findings for Vercel Sandbox salvage 2026-04-29 07:22:33 -07:00