Foreground Bash/TaskStop don't kill subprocesses; harness tracks ghost shells

Resolved 💬 1 comment Opened Apr 22, 2026 by superbiche Closed May 26, 2026

Summary

In a single 40-minute session, three different harness paths left processes running after they should have been dead, and the UI kept reporting activity for work that had long finished. The result was ~35 minutes of wasted wall time plus orphan processes holding container / DB resources.

Repros

1. task test -- --filter=X hangs silently for 30+ min with zero feedback

The task wrapper (our project's taskfile) eventually invokes php artisan test which passes --no-output to Pest. With coverage-mode Xdebug enabled, Pest can stall at ~0% CPU while the UI shows a running Bash call producing zero output. No way to tell from the UI whether the call is working, hung, or deadlocked.

Expected: either periodic heartbeat output from the harness, or a timeout warning when a Bash call goes quiet for N minutes.

2. TaskStop removes the task from the harness but leaves the OS process alive

I called TaskStop on a task test:parallel shell. Harness reported \"Successfully stopped task\". \ps\ showed the PHP / pest processes still running 28 minutes later at 0% CPU — they weren't sent SIGTERM/SIGKILL.

Expected: TaskStop should propagate the signal to the process group (setpgrp + killpg), or at least warn \"process still alive after stop signal\".

3. Rejecting a Bash tool call doesn't kill the subprocess it already started

I hit \"reject\" on a foreground \docker exec … pest …\ call about 10 seconds after it started. Four minutes later the pest invocation was still running inside the container. I had to manually \kill -9\ the host-side \sh\ wrapper.

Expected: user-side rejection should trigger SIGKILL of the spawned subprocess tree, not just disconnect the harness's read handle.

4. UI \"N shell still running\" banner is stale

After (1) / (2) / (3) were manually reaped via \kill -9\, the UI kept displaying \"1 shell still running\" for the remainder of the session. No way to clear it without starting fresh tool work.

Impact

  • ~35 minutes of wasted wall time in one session.
  • Resource leak: orphan PHP / docker processes holding DB connections + container memory.
  • Status indicators can't be trusted — had to shell out to \ps\ / \docker exec\ repeatedly to know the real state, which defeats the purpose of an agentic harness.

Environment

  • Claude Code CLI
  • Model: Opus 4.7 (1M context)
  • OS: Fedora 43, Linux 6.19.12
  • Tools involved: Bash (foreground), TaskStop, Monitor

Suggested fixes

  • Spawn subprocesses in their own process group; on rejection / TaskStop / session end, \killpg(-PID, SIGKILL)\.
  • Emit a \"no output for Nm\" notification so silent hangs are visible without polling \ps\.
  • Reconcile the \"shells running\" counter against real PIDs at each tool call; auto-clear ghosts.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗