What happens
A foreground bash tool call returns nothing at all until the process exits. For a command that takes minutes — a test suite, a build, a deploy — the session goes completely silent, with no output, no elapsed time, and no indication anything is alive. From the outside it is indistinguishable from a hang, and it blocks the whole turn: nothing else can happen while it runs.
Reported by a user mid-session as "why does codegraff always hang here tho" and "it js stops everything".
Evidence from one real session
Harness v0.0.243-4-g18e3f31-dirty, measured from one session's local event trace:
- 9
bash calls over 60s
- 4302s (72 min) of wall time inside those 9 calls
- 5120s (85 min) total tool wall time across 198 calls
So ~84% of all tool time was spent in blocking calls that displayed nothing while running. Individual offenders:
| ms |
what it was |
| 2977129 (50 min) |
a vendor CLI's log-streaming subcommand — streams, never exits, had to be cancelled by the user |
| 355904 (5.9 min) |
vitest run against a remote database |
| 260087 |
same suite |
| 259741 |
same suite |
| 163511 |
vitest run single file |
Why it is worse than "just slow"
- No partial output. The command is producing output the whole time; none of it is shown. The buffer is only flushed on exit.
- Streaming commands never exit at all. A log-streaming CLI subcommand,
tail -f, a dev server — these have no exit. In the foreground they consume the turn until a human notices and cancels. The 50-minute call above is exactly this.
- The documented workaround is invisible at the call site.
run_in_background + bash_output exists and works, but nothing about a foreground call signals "this one is going to be long" until it is already too late. An agent only learns after burning the time.
- Piping defeats the workaround too. Backgrounding a job but piping through
tail -40 reproduces the silence, because tail buffers to EOF — so bash_output returns "(no new output)" for minutes on a job that is working fine. Easy trap to fall into.
Suggested fixes, roughly in order of value
- Stream partial stdout/stderr for foreground bash as it arrives, even truncated to the last N lines, instead of buffering to exit.
- Auto-promote to background after a threshold (say 30-60s): return a job id and the output so far, with a note that it kept running, rather than holding the turn open indefinitely.
- Emit a heartbeat — elapsed seconds and bytes-so-far — so a silent-but-working command is distinguishable from a wedged one.
- Warn on obviously-streaming commands (
logs, tail -f, dev, watch) by suggesting run_in_background at the call site.
- Document the
| tail buffering trap in the bash tool description, since it silently defeats bash_output.
Impact
On any task that runs a real project's own test/build/deploy commands — which the system prompt explicitly asks for — this is the dominant source of dead time and the main thing that makes the harness feel broken to a watching user.
What happens
A foreground
bashtool call returns nothing at all until the process exits. For a command that takes minutes — a test suite, a build, a deploy — the session goes completely silent, with no output, no elapsed time, and no indication anything is alive. From the outside it is indistinguishable from a hang, and it blocks the whole turn: nothing else can happen while it runs.Reported by a user mid-session as "why does codegraff always hang here tho" and "it js stops everything".
Evidence from one real session
Harness
v0.0.243-4-g18e3f31-dirty, measured from one session's local event trace:bashcalls over 60sSo ~84% of all tool time was spent in blocking calls that displayed nothing while running. Individual offenders:
vitest runagainst a remote databasevitest runsingle fileWhy it is worse than "just slow"
tail -f, a dev server — these have no exit. In the foreground they consume the turn until a human notices and cancels. The 50-minute call above is exactly this.run_in_background+bash_outputexists and works, but nothing about a foreground call signals "this one is going to be long" until it is already too late. An agent only learns after burning the time.tail -40reproduces the silence, becausetailbuffers to EOF — sobash_outputreturns "(no new output)" for minutes on a job that is working fine. Easy trap to fall into.Suggested fixes, roughly in order of value
logs,tail -f,dev,watch) by suggestingrun_in_backgroundat the call site.| tailbuffering trap in thebashtool description, since it silently defeatsbash_output.Impact
On any task that runs a real project's own test/build/deploy commands — which the system prompt explicitly asks for — this is the dominant source of dead time and the main thing that makes the harness feel broken to a watching user.