Skip to content

Claude: narration between tool calls renders as a summarized message, not reasoning - #1013

Merged
Tryanks merged 1 commit into
mainfrom
claude-narration
Oct 29, 2026
Merged

Tryanks merged 1 commit into
mainfrom
claude-narration

Conversation

@Tryanks

@Tryanks Tryanks commented Oct 29, 2026

Copy link
Copy Markdown
Owner

Summary

With thinking on, Claude Code 2.1.312 + Opus 5.5 do not deliver the prose the model writes to the user between tool calls as text. The server replaces it with its own summary, sent as a thinking block. Only the frame's narration_block_indexes tells this block apart from reasoning. Claude Code's terminal UI shows it as a message marked "· summarized". Tcode mapped it to Reasoning, so it was folded into the work log. A reply to a mid-turn steer therefore looked as if the steer had been ignored. In one local database, 335 of 2555 first replies after a steer (13%) ended up folded this way.

 on_assistant(frame)
   thinking block, non-empty
-    → Reasoning
+    listed in frame.narration_block_indexes → AssistantMessage { summarized: true }
+    otherwise                               → Reasoning

The streamed placeholder (Reasoning, same msg:index id) is replaced in place by the completed message, so the text renders once, as a message.

 ItemContent::AssistantMessage {
     text,
+    summarized: bool,   // serde default false, omitted when false
 }
 <AssistantMessage>
   markdown
+  "Summarized" / "已概括" caption (muted, xs) when summarized

summarized is a field on AssistantMessage, not a new variant. Narration is still the assistant talking to the user, so every existing reader of assistant text keeps it: relay, search, export, titles, the latest-message action row. Before the server began summarizing, that same interim prose arrived as plain text and those readers already included it.

Not covered: Claude transcript import and subagent transcript tails. Their JSONL has no narration_block_indexes. Telling narration apart there would mean decoding the signature envelope, which the CLI does internally and does not expose. Event logs written before this change keep their Reasoning entries.

Evidence

  • Before: the status update the model sent mid-turn was persisted as {"kind":"reasoning","text":"…"}. The CLI's own transcript has the same block as type: "thinking", and its signature tags it block_kind narration. A live CLI run shows the frame field:
    assistant [('thinking', "f.txt contains two lines…")] {'narration_block_indexes': [0], 'thinking_display': 'updates'}
  • After: narration_completes_its_streamed_block_as_a_summarized_message replays those recorded frames. The completion is AssistantMessage { summarized: true } under the streamed id msg_n:1. The test fails with the narration branch disabled and passes with it.
  • assistant_messages_are_summarized_only_when_recorded checks the wire literals. A legacy record with no field decodes to false, and "summarized":true is written only when set.
  • Gallery section 7 has a new "SUMMARIZED NARRATION" card, checked in light and dark at 1100 and 420 pt.
  • cargo fmt --check, cargo clippy --workspace --all-targets -D warnings and cargo nextest run --workspace (1359 passed) were run on macOS. The iOS, Android and Web checks are left to CI.

Merge Danger

Door: two-way

The new field is additive and optional on the wire. Older logs and peers decode it as false; an older client ignores it and shows the narration as a plain message. Noted as Unreleased above PROTOCOL_VERSION.

Blast Radius: Claude timelines

Mid-turn narration moves out of the collapsed work log into the timeline as messages, so Claude turns with thinking on show more prose between activity runs.

@Tryanks
Tryanks merged commit b66ce36 into main Oct 29, 2026
13 checks passed
@Tryanks
Tryanks deleted the claude-narration branch October 29, 2026 18:57
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