Header shows "done"/idle while a harness-owned background Bash task is still running

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 22, 2026

What happened

When the assistant turn ends (Stop), the terminal header flips to the "done"/idle indicator even while a harness-owned background Bash task (run_in_background: true) is still running and has not yet delivered its return.

Concretely: a turn dispatches a background shell whose completion is expected to re-invoke the agent (e.g. an external code-review/plan-review dispatched with timeout … <reviewer> … under run_in_background: true). The turn ends, the header shows "done", but the session is not actually idle in the meaningful sense — it is waiting for that background job to finish and re-trigger the agent. The indicator therefore reports "done" during a window in which work is still pending.

Why the current behavior is misleading

The harness collapses two distinct states into one idle:

  1. idle — awaiting the user (nothing pending)
  2. idle — awaiting a background task the harness owns (a re-invocation is guaranteed to arrive)

From the operator's side these are semantically different: in (2) the session will continue on its own, so "done" is a false signal. There is currently no way to tell them apart from the header.

Why this can't be worked around today

Verified against the hooks documentation and behavior:

  • The Stop hook payload contains no field listing active background tasks (session_id, prompt_id, transcript_path, cwd, permission_mode, effort, last_assistant_message). Same for the Notification hook. Hooks cannot see task state, so they can't gate the indicator.
  • There is no setting to make the completion/idle indicator conditional on pending background tasks.
  • Stopdecision: "block" is the only lever that prevents the "done" transition, but it does so by forcing the agent to keep generating — an immediate re-invocation with nothing to do, i.e. a tight loop that burns tokens and pollutes the transcript. It is not passive waiting.
  • The harness does not distinguish a long-lived service background shell (e.g. a dev server) from a transient one (a review dispatch that exits and re-invokes). Both are opaque "a background task with an ID".

Requested behavior

While there is at least one harness-owned background Bash task still running, suppress the "done"/idle header indicator (or render a distinct "awaiting background job" state) instead of "done".

Refinements worth considering:

  • Gate only on background tasks whose completion re-invokes the agent, not on fire-and-forget long-lived services — or expose a per-dispatch flag (e.g. run_in_background + an awaits_return/transient marker) so the caller declares which shells should hold the indicator.
  • Alternatively (or additionally): expose active background tasks in the Stop/Notification hook payload (an array of {id, command, status}) so operators can implement the gating themselves via a hook. This is the lower-effort path and unblocks custom statuslines/notifiers even if the native indicator stays as-is.

Environment

  • Platform: WSL2 (Linux)
  • Interactive terminal mode
  • Background shells dispatched via the Bash tool with run_in_background: true + a shell timeout wrapper

Summary

The Stop → idle → "done" transition is decoupled from the background-task lifecycle and cannot be made task-aware from user space (no task context in any hook payload, no setting). Either make the native indicator background-task-aware, or surface active background tasks in hook payloads so it can be gated externally.

View original on GitHub ↗