Problem
A parent conversation can start detached work with spawn_agent / run_async, finish its foreground turn while that work is pending, and never progress after the detached work completes.
The background wrapper successfully appends a terminal event to the parent conversation ref, but the parent remains idle and no continuation turn is admitted. This is especially confusing when the agent says it will continue or merge the result once the subagent completes.
Reproduction
- Start two subagents from a TUI conversation.
- Poll both with
run_async.
- Let one finish and merge it while the other remains pending.
- Allow the parent agent to end its foreground turn with
status: "idle".
- Let the remaining subagent finish.
The conversation ref advances with a terminal event such as:
{"async":{"result":"32d3537d346ce9f50943a910f9693f99794b6c96","status":"complete","task":"5e61048b87fae5db46e1d6754ce9b1f647408045"}}
The preceding parent event is still the last top-level lifecycle status and is idle. No further model turn runs, so the completed result is not inspected or merged until the user sends another message.
Observed behavior
- The detached request and
run-and-update-ref finish successfully.
- The result object exists and the
async.status: complete event is durably appended.
- The TUI follows the new ref head, but the conversation projection remains idle because the event has no top-level queued/running status.
- No component admits a new parent
llm-step request.
This is separate from a subagent failure or a cold worker start.
Expected behavior
Completion should not silently strand a parent that is waiting for detached work. The eventual policy may require an explicit durable wait/subscription rather than waking on every detached completion, but the system and agent-facing contract should make the behavior reliable and visible.
At minimum, an idle parent should surface that background work completed and requires attention; ideally a parent that durably elected to wait should resume exactly once.
Related implementation notes
run-and-update-ref currently appends the terminal async event and returns. Conversation status projection reads top-level status, and TUI request reconciliation only joins conversations projected as queued or running. There is therefore no wakeup edge from a terminal async event to a new foreground request.
design/chat.md already lists durable async recovery as deferred work, but that note focuses on redispatching pending tasks after foreground exit. This report covers the corresponding terminal-completion/parent-continuation gap.
Problem
A parent conversation can start detached work with
spawn_agent/run_async, finish its foreground turn while that work is pending, and never progress after the detached work completes.The background wrapper successfully appends a terminal event to the parent conversation ref, but the parent remains idle and no continuation turn is admitted. This is especially confusing when the agent says it will continue or merge the result once the subagent completes.
Reproduction
run_async.status: "idle".The conversation ref advances with a terminal event such as:
{"async":{"result":"32d3537d346ce9f50943a910f9693f99794b6c96","status":"complete","task":"5e61048b87fae5db46e1d6754ce9b1f647408045"}}The preceding parent event is still the last top-level lifecycle status and is idle. No further model turn runs, so the completed result is not inspected or merged until the user sends another message.
Observed behavior
run-and-update-reffinish successfully.async.status: completeevent is durably appended.llm-steprequest.This is separate from a subagent failure or a cold worker start.
Expected behavior
Completion should not silently strand a parent that is waiting for detached work. The eventual policy may require an explicit durable wait/subscription rather than waking on every detached completion, but the system and agent-facing contract should make the behavior reliable and visible.
At minimum, an idle parent should surface that background work completed and requires attention; ideally a parent that durably elected to wait should resume exactly once.
Related implementation notes
run-and-update-refcurrently appends the terminal async event and returns. Conversation status projection reads top-levelstatus, and TUI request reconciliation only joins conversations projected as queued or running. There is therefore no wakeup edge from a terminal async event to a new foreground request.design/chat.mdalready lists durable async recovery as deferred work, but that note focuses on redispatching pending tasks after foreground exit. This report covers the corresponding terminal-completion/parent-continuation gap.