Skip to content

CL-6557: Chat: false "no reply arrived" notice after a fully rendered reply - #270

Merged
TheGreatAxios merged 2 commits into
mainfrom
cl-false-no-reply
Aug 21, 2026
Merged

TheGreatAxios merged 2 commits into
mainfrom
cl-false-no-reply

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

Summary

  • The reply-timeout backstop in packages/chat-ui/src/streaming-reply.ts fires even after a complete reply has rendered, because it only ever moves a turn to "replied" on the chat.agent stream's connector.reply event — a signal a parked folded run's stream may never carry, and which can otherwise be dropped/delayed independent of the actual reply.
  • The reply a reader sees actually arrives as its own chat.message event (posted by postReply in chat-orchestrator.ts), an entirely separate pipeline from chat.agent. When connector.reply never lands but the chat.message does, the client's streamingReply state stays stuck in "awaiting" and the 120s backstop eventually shows the false notice right under the already-rendered reply.
  • Fix: an awaiting turn's own chat.message (agent sender, real content, not postUndeliveredNotice's turnFailed part) now also moves the turn to "replied", independent of whether connector.reply ever arrives. The transition is keyed on rendered content — an observable fact — rather than a lifecycle event that may never arrive.
  • This is the third instance of this bug family (CL-6432: turn-phase typing indicator (reopened) #183, CL-6486: time out a reply that stalls mid-stream #220); both prior fixes tuned when the backstop arms rather than what disarms it.

Test plan

  • packages/chat-ui/src/streaming-reply.test.ts — new pure-reducer cases for the chat.message-driven "replied" transition (agent sender + content → replied; turnFailed notice never counts; human sender never counts; empty parts never count)
  • packages/chat-ui/test/use-streaming-reply.test.tsx — hook-level repro: a rendered reply followed by silence with no terminal run event (mimicking a parked folded run) shows no notice; a rendered reply followed by a post-reply tool round shows no notice; a turn with no content at all still times out correctly
  • bun test in packages/chat-ui: 661 pass, 0 fail
  • tsc --noEmit and eslint clean on changed files
  • prettier --check clean on changed files

Linear: CL-6557

A parked folded run's chat.agent stream never carries connector.reply,
so the reply-timeout backstop can fire after the reply has already
rendered via its own chat.message. These tests reproduce that: a
rendered reply followed by silence (no terminal run event) or a
post-reply tool round must never show the timeout notice, while a
turn with no content at all must still time out.
… connector.reply

connector.reply only ever arrives on the chat.agent stream, which a
parked folded run's post-turn park never emits a terminal event on;
the persisted reply's own chat.message posts from a separate pipeline
and was the only thing actually on screen. The reply-timeout backstop
stayed armed off connector.reply alone, so it could fire after a
complete reply had already rendered.

Now an awaiting turn's chat.message also moves it to replied the
moment the agent's own reply content is observed to have posted,
independent of whether connector.reply ever arrives on the chat.agent
stream.
@TheGreatAxios
TheGreatAxios merged commit 4707c1f into main Aug 21, 2026
5 checks passed
@TheGreatAxios
TheGreatAxios deleted the cl-false-no-reply branch August 25, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant