"Turn finished · no closing message" kept appearing over an agent that was plainly still working, with output resuming a moment later. isGenerating was set in exactly three places: sendPrompt, and the two reconnect handshakes. So it answered "am I waiting for a reply I asked for", never "is this conversation working". Every other way a turn begins left it false — a commander waking on its own schedule, a queued prompt the server delivered, another tab driving the same session, or a turn that started before this tab was looking. The events still render, but buildRows reads isGenerating to decide whether the last turn is finished, so a live turn was `complete` and the moment it sat on a completed tool call with nothing said after it, the banner fired. Same gap left the composer enabled mid-turn, and a message typed then was not marked injected — which splits the live turn and prints the banner a second way, the exact failure injected exists to prevent. session:claude now sets it: the harness's own turn-start signal, and the only one that arrives before any work does. tool:start sets it too, because session:claude is not durable and a socket connecting mid-turn never receives one, while a tool cannot start outside a turn. Two candidates were measured rather than argued. A genuinely silent turn ending earns the banner honestly, but 667 of 673 recorded turns end with assistant:text immediately before result, so that is ~1% and cannot be it. And a stale sync:live is real but rare: correlating every attach in the officer log against the agent's own later output, 15 of 549 "not generating" attaches were contradicted and one provably so. That residue is left alone — the tool:start fallback now corrects it on the next event — and noted on the refresh card, which is the same seam. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: f7a62308-0977-4cf4-aa29-c020853fd420
51 lines
3.3 KiB
Markdown
51 lines
3.3 KiB
Markdown
---
|
|
id: known-bugs/a-turn-this-tab-did-not-start-reads-as-finished
|
|
title: 'A turn this tab did not start reads as finished — "no closing message" over a working agent'
|
|
theme: known-bugs
|
|
status: done
|
|
created: 2026-08-25
|
|
date_source: stated
|
|
order: 6
|
|
---
|
|
|
|
**"Turn finished · no closing message" appears while the agent is still working, and output resumes a
|
|
moment later.** Reported by the owner 2026-08-25 with a screenshot: the session list shows Platform Harry
|
|
`generating`, and Platform Harry's own panel shows the banner under two completed Bash calls. He reports it
|
|
happens often.
|
|
|
|
**Not the same bug as the two below it, and not a race.** `isGenerating` was set in exactly three places:
|
|
`sendPrompt`, and the two reconnect handshakes (`sync:live`, `sync:messages`). So it answered _"am I
|
|
waiting for a reply I asked for"_, never _"is this conversation working"_. Every other way a turn begins
|
|
left it **false** — a commander waking on its own schedule, a queued prompt the server delivered, another
|
|
tab or another device driving the same session, or simply a turn that started before this tab was looking.
|
|
|
|
The tab still renders the work, because the events arrive regardless. But `buildRows` reads `isGenerating`
|
|
to decide whether the last turn is finished (`generatingNow = isLast && isGenerating`), so with it false a
|
|
live turn is `complete` — and the moment that turn sits on a completed tool call with nothing said after
|
|
it, the banner fires. Then the next event arrives and the conversation carries on, which is exactly what
|
|
"still outputting in a bit" is.
|
|
|
|
Two further consequences of the same gap, both visible in the screenshot: the composer stays **enabled**
|
|
during someone else's turn, and a message typed then is not marked `injected` — which splits the live turn
|
|
and prints the banner a second way, the precise failure `injected` was added to prevent (`types.ts`).
|
|
|
|
**Ruled out on the way, with evidence rather than reading.** A silent turn ending would earn the banner
|
|
honestly, so that was measured first: across every durable event ever recorded, **667 of 673 turns end with
|
|
`assistant:text` immediately before `result`**. Genuine silent endings are ~1% and cannot account for "a lot
|
|
of times". A stale `sync:live` was the other candidate and is real but rare — correlating every attach in
|
|
the officer log against the agent's own subsequent output, 15 of 549 "not generating" attaches were
|
|
contradicted, and only **one** provably so (the first event after it was a `tool:result`, so a tool was
|
|
already in flight when officer answered).
|
|
|
|
---
|
|
|
|
**Fixed 2026-08-25.** `session:claude` — the harness's own turn-start signal, and the only one that arrives
|
|
before any work does — now sets `isGenerating`. `tool:start` sets it too, as a fallback: `session:claude` is
|
|
not durable, so a socket that connects mid-turn never receives one, and a tool cannot start outside a turn.
|
|
|
|
Residual, and left alone deliberately: `sync:live` can still report `isGenerating: false` for a turn that is
|
|
running (the one proven case above, `sidecar.isClaudeGenerating` answering for an adopted session). The
|
|
`tool:start` fallback now corrects that on the next event rather than leaving the turn wrongly settled, so
|
|
the symptom is covered even where the answer is not. Fixing the answer itself belongs with
|
|
[[known-bugs/refresh-mid-tool-call-renders-the-turn-folded]], which is the same seam.
|