Skip to content

[Bug]: Inline turn diff card is missing or attached mid-turn when a Claude turn ends quickly #14216

Description

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

This is intermittent. It is timing-dependent rather than deterministic, and affects roughly 1 in 6 Claude turns that edit files.

  1. Use the Claude provider with Claude Code CLI 2.1.280 or later.
  2. Send prompts that edit files and end with a short final reply.
  3. After each turn, check where the inline "changed files" card renders in the chat timeline.

Expected behavior

The card renders under the turn's final assistant message.

Actual behavior

The card is either missing entirely, or it renders under an earlier assistant message from the same turn. The diff panel and sidebar still show the turn's changes correctly.

The stored checkpoint's assistantMessageId is one of:

  • an earlier assistant message from the same turn, so the card renders mid-turn, or
  • the fallback assistant:<turnId>, which matches no message, so no card renders.

Cause (line numbers as of d2c9281b81):

  • ProviderRuntimeIngestion and CheckpointReactor consume providerService.streamEvents on independent workers (ProviderRuntimeIngestion.ts:2711, CheckpointReactor.ts:1040). Nothing orders them relative to each other.
  • On turn.completed, the reactor reads the thread projection and picks the latest assistant message it can see for the turn. It falls back to assistant:<turnId> if there is none (CheckpointReactor.ts:335-340).
  • Claude Code CLI sends result (which becomes turn.completed) right after the final assistant message. If ingestion hasn't committed that message yet, the reactor attaches the checkpoint to the previous message, or to the fallback id.
  • The client keys the card strictly by assistantMessageId, with no fallback (apps/web/src/components/chat/MessagesTimeline.logic.ts:982, :1405).

Why it started recently: Claude Code 2.1.280 shortened the gap between its final assistant message and result. The median gap went from about 40 ms on 2.1.278 to about 13 ms, and 15–29% of turns now have a gap under 5 ms, down from almost none. That gap was ingestion's whole head start. The mismatch rate tracks it closely (see Logs).

Possible fixes:

  • Client: attach the card to the last assistant message of the checkpoint's turnId. This also repairs checkpoints already stored with a stale id, and applies to the web and mobile timelines alike.
  • Server: have the reactor wait for ingestion to process the turn's events before picking the message, or update the checkpoint's assistantMessageId when the final message lands. This only fixes new turns.

Impact

Minor bug or occasional failure

Version or commit

main @ d7819c1 (plus unrelated fork changes; the code involved is identical on main @ d2c9281)

Environment

macOS 26.6.2, T3 Code desktop (dev build), Claude provider with Claude Opus 5.5 (medium effort), Claude Code CLI 2.1.280–2.1.284 (affected) and 2.1.263–2.1.278 (not affected)

Logs or stack traces

# Measured over ~330 Claude turns on one machine, using the provider native event logs and the projection DB.

# Gap between the CLI's final `assistant` message and its `result`, by CLI version
version   turns  median   gap<5ms
2.1.278   66     39.5ms   0%
2.1.280   68     13.5ms   15%
2.1.281   27     12.0ms   19%
2.1.283   38     11.5ms   29%
2.1.284   34     21.5ms   21%
(2.1.263-2.1.275: medians 25-46ms)

# Turns whose checkpoint assistantMessageId is not the turn's final assistant message, by that gap
gap        turns  wrong
<5ms       34     21
5-15ms     59     4
15-30ms    95     2
>=30ms     137    0

# One affected turn: the final message is in the event log before the checkpoint,
# yet the checkpoint is attached to the previous message (bde0c332...)
seq    event_type                   messageId
25409  thread.message-sent          assistant:a84d4c5f...  (streaming)
25411  thread.message-sent          assistant:a84d4c5f...  (final)
25415  thread.turn-diff-completed   assistantMessageId=assistant:bde0c332...

Screenshots, recordings, or supporting files

No response

Workaround

Open the diff panel manually. The turn's diff is still available there and in the sidebar.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.needs-triageIssue needs maintainer review and initial categorization.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions