Skip to content

fix(acp_client): reap agents orphaned by a host that died without close - #930

Closed
akushonkamen wants to merge 1 commit into
EverMind-AI:mainfrom
akushonkamen:fix/928-acp-orphan-sweep
Closed

akushonkamen wants to merge 1 commit into
EverMind-AI:mainfrom
akushonkamen:fix/928-acp-orphan-sweep

Conversation

@akushonkamen

Copy link
Copy Markdown
Contributor

Summary

launch() starts every ACP agent with start_new_session=True, so the only teardown path is close()'s killpg (raven/acp_client/client.py:261, raven/acp_client/client.py:301-315). A host that dies without reaching close_pool — SIGKILL, OOM killer, crash — leaves the agent orphaned, parented to init. Observed in #928: an agent running for 22 days holding 523 MiB PSS.

Root cause: no fallback teardown when the owning host process dies abruptly.

Fix: launch() records (pgid, agent birth, owner pid, owner birth) to a per-boot temp file, and every launch() sweeps that file first. A group is killed with killpg only when its owner is dead (or its pid was recycled, detected via birth mismatch) and the agent's birth still matches — so a recycled pid is neither killed nor mistaken for a live owner. close() removes its own record. On Windows the same record is used and the tree is killed with taskkill /T /F; no real Windows machine was available, so that branch is written to the #918/#919 standard and declared untested here.

Process: timeline check confirmed no earlier claim/PR by this account on #928; a claim comment with AI disclosure was posted first (issuecomment-6135634396).

Type

  • Fix

Verification

  • Relevant tests pass locally

Red (bug reproduced, pre-fix): timeout 20 $V /tmp/repro_928.py + ps -o pid,ppid,stat -p <AGENT_PID> → after the stub agent's host was SIGKILLed the agent stayed alive with ppid=1 (bug evidence; first reproduction pid 40717).

Green (post-fix e2e): same script, host kill -9ed, then
$V -c "from raven.acp_client.client import sweep_orphaned_agents; print(sweep_orphaned_agents())" → KILLED [58468]; ps -p 58468 confirms the process is gone.

New tests: $V -m pytest tests/test_acp_orphan_sweep.py -v → 5 passed (owner dead → group killed; live owner spared; recycled pid not killed; dead pid with birth=None; boot token).

Regression subsets ($V = pre-existing venv python, Python 3.13.15):

  • $V -m pytest tests/test_acp_methods.py tests/test_acp_protocol.py tests/test_acp_stdio.py tests/test_acp_journal.py tests/test_acp_orphan_sweep.py -q → 228 passed
  • $V -m pytest tests/test_subagent_host_env.py tests/test_acp_backend_cwd.py tests/test_cli_gateway_commands.py tests/test_cli_tui_commands.py -q → 171 passed
  • $V -m pytest tests/test_subagent_acp_mcp_lifecycle.py -q → 21 passed

Risk

  • Security impact considered — sweep only kills process groups it recorded itself in the same boot, and only after birth verification, so pid reuse cannot cause a misdirected kill.
  • Backward compatibility considered — sweep runs inside launch(), no API change; close() semantics unchanged.
  • Rollback path is clear — single commit; reverting restores the prior behavior exactly.

Related Issues

Fixes #928

AI disclosure: written with AI assistance (Claude / GLM-5.3-Flash); commit carries Co-authored-by: Claude (GLM-5.3-Flash) <noreply@anthropic.com>.

launch() starts every ACP agent with start_new_session=True, so the
process group hears none of raven's signals and the only teardown path
is close()'s killpg. A host killed by SIGKILL, the OOM killer, or a
crash never reaches close_pool, and the agent stays behind parented to
init -- observed in EverMind-AI#928 running for 22 days holding 523 MiB PSS.

The orphan guard closes that: launch records (pgid, agent birth, owner
pid, owner birth) in a per-boot file under the temp dir, and the next
launch sweeps the file first, killing the groups whose owner is gone
and whose birth still matches -- so a recycled pid is neither killed
nor mistaken for a live owner. close() drops its own entry. Windows
rides the same record and kills via taskkill /T /F; no real Windows
machine was available, so that branch is written to the EverMind-AI#918/EverMind-AI#919
standard and declared untested here.

Reproduced on macOS (darwin 24.6.0 arm64) before the fix: a stub agent
outlived its SIGKILLed host with ppid=1; after the fix the next launch
kills the orphaned group.

Co-authored-by: Claude (GLM-5.3-Flash) <noreply@anthropic.com>
Signed-off-by: akushonkamen <akushonkamen@163.com>
@LivXue

LivXue commented Oct 14, 2026

Copy link
Copy Markdown
Member

This PR has not been verified on a real Windows machine, and its CI has been red (coverage gates and pull request title, since 2026-10-13) with no follow-up since. Closing it; #928 is now addressed by #951 (Windows) and #953 (macOS).

You are welcome to open PRs against either branch: fix/windows_acp_process_lifetime for #951, and fix/acp_orphans_on_macos for #953. Thanks for the work on #928.

@LivXue LivXue closed this Oct 14, 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.

fix(acp_client): on macOS and Windows an ACP agent outlives a host that dies without closing it

2 participants