Chat: a running Claude Code workflow (background subagents) reads as needs-input / idle, not working #39
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab#39
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
When a Claude Code workflow (multi-agent orchestration) is running, it
dispatches subagents that keep working in the background. lab does not recognize
this as "working": the run shows as needs-input (or idle) and the composer
unlocks as if the agent were waiting on the operator, even though background
agents are still running.
Reproduction
main turn ends with an ordinary assistant message while the subagents keep
going).
chat composer presents an unlocked Send box.
Expected: the run reads as "working" while the workflow's subagents run.
Notes for triage
Root cause is that lab derives activity state from only the type of the last
content block in the main transcript
.jsonl:deriveState—internal/provider/claudecode/chat.go:229-250: a trailingassistant:textblock →StateNeedsInput; onlyuser:text | tool_use | tool_result | assistant:thinkingas the last block →StateWorking.end_turnassistant textmessage, so
lastKey == "assistant:text"→ needs-input (or idle if thelocated transcript is empty / subagent-only).
stop_reason, noisSidechain/parentUuid, and has nonotion of an in-flight
Task/ subagent (tItemininternal/provider/claudecode/chat_types.go:18-26omits them;compat.md§5says every other transcript key is ignored). The
Tasktool is recognizedcosmetically only (
chat_types.go:130-133) and counts as "working" only whileit is the literal last block.
blocked marker → needs-input, via
dialogspool.go); there is no "backgroundworking" signal.
.jsonl, the main transcript is frozen,so the tailer's single-file mtime/size watch (
internal/chat/tailer.go:150)never fires and the UI stops refetching.
web/src/lib/conversation.ts:14-25) gives idle/endedno badge, and the working/interrupt affordance is gated on
state === 'working'(
web/src/routes/RunChat.tsx:793).Likely fix surface (for the maintainer to weigh): teach
deriveState/tItemabout
stop_reasonand/or an unresolved-Task/isSidechain"sub-task inflight → working" rule, plus widen the tailer's single-file watch to observe
sidecar session writes. Shares the
deriveStatecoupling with #38 ("Interruptbutton does nothing").
Triage: ready-for-human
Category: bug
Summary: shares its root cause with #62 (filed as the umbrella issue for
this symptom plus #38) —
deriveStatederives state from only the lasttranscript content-block type, with no notion of an in-flight
Task/isSidechainsubagent. A workflow's background subagents keep running afterthe main transcript's visible turn ends in an ordinary
assistant:text, so therun reads
needs-input/idle instead ofworking.Why this is
ready-for-human, notready-for-agent: fixing this properlymeans picking the same liveness mechanism #62 is blocked on (parsing
stop_reason/isSidechain/in-flightTask, a pane/CPU probe, and/or wideningthe tailer past the single main-transcript file) — that choice is a design
call for the maintainer, not something to decide independently per-issue.
Landing a narrow fix here without that decision would risk a second
transcript-state heuristic alongside #62's, which is exactly the kind of
heuristic-on-a-heuristic #62's notes already flag as rejected.
Acceptance criteria (once a direction is picked, shared with #62):
working, notidle/needs-input, for as long as those subagents are running.
state === 'working'in
RunChat.tsx) reflects that background-working state correctly.Out of scope:
the open design question this is blocked on.
Maintainer note: #62 itself suggests "consider folding #39 in, or closing
both against this once designed" — leaving both open and cross-linked for now
rather than merging them, since that consolidation call is yours to make.
Relabeling
needs-triage→ready-for-human, cross-linked to #62.Root fix designed: #62 now carries the full decision record (2026-07-10 grill). A working-edge from the liveness markers overrides the
assistant:texttail, so a run with live background work reads as working — with one honest caveat: whether background subagents are reliably hook-observable is a pinned spike question (does Stop fire while they run; does subagent tool-use refresh the parent's edges; SubagentStop pairing). Per decision 5 on #62, this issue's acceptance criterion is scoped to what the spike proves — if hooks can't see background work, the criterion is renegotiated on evidence rather than met with inference. Stays open as the symptom tracker; #62's implementation closes it (prerequisites: #92, #93).