Skip to content

fix(server): Claude's lone explanation before a question shows as text - #27271

Open
juliusmarminge wants to merge 5 commits into
mainfrom
fix/issue-27238
Open

juliusmarminge wants to merge 5 commits into
mainfrom
fix/issue-27238

Conversation

@juliusmarminge

Copy link
Copy Markdown
Member

Problem

On Claude 5.1+, a reply sometimes has no text block, just one thinking block followed by an AskUserQuestion call. That thinking block is the explanation the question refers to. The adapter stored it as reasoning, so it appeared only as a collapsed Thought row, and the question card asked about context the user never saw. Since https://git.cafe/pingdotgg/t3code/pulls/18923, pre-tool thinking is promoted to text only when it is the second of two consecutive thinking blocks.

Reproduced on main (812c0a71c7) by driving the real Claude Agent SDK with T3's thinking options (claude-opus-5-5, effort high, thinking: { type: "adaptive", display: "summarized" }) on a synthetic "review these three PRs, then ask me which to merge" task, 6 times. In 1 of the 6, the question's message was thinking (532 chars) -> tool_use AskUserQuestion, and that single block was the review the question relied on. In the other 5, the review came as the second of two thinking blocks, which main already promotes.

Change

In ClaudeAdapterV2's assistant-snapshot loop, a lone non-empty thinking block is now promoted to assistant text when the tool call right after it is AskUserQuestion. The rest of the existing rule is unchanged: Claude 5.1+ only, the same message, and blank thinking is never promoted. Promotion reuses the same path as the second-of-two case: same item id and ordinal, and the reasoning item becomes an assistant_message. A lone thinking block before any other tool still stays reasoning.

Scope and approval

Fixes #27238

Triaged through the automated issue flow; GitCafe issue https://git.cafe/pingdotgg/t3code/issues/27238. The issue is a triaged bug report that names this mechanism and proposes this fix, limited to AskUserQuestion so https://git.cafe/pingdotgg/t3code/pulls/18923's behaviour for ordinary tool calls is kept.

Verification

  • vp test run apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts -t "lone claude-opus-5-5 thinking block":
    • on main, the AskUserQuestion row fails with expected [ 'reasoning' ] to deeply equal [ 'assistant_message' ];
    • at the fix, both rows pass;
    • the Bash row stays reasoning on both sides.
  • The whole ClaudeAdapterV2.test.ts passes: 256/256.
  • vp run check:changed passes (format, lint, knip, typecheck, tests).
  • Runtime transcript: the 157 recorded live frames of that thinking -> AskUserQuestion message (stream events and snapshots) were replayed through the adapter on each side, using a scratch harness test that is not committed:
== BEFORE: adapter at main 812c0a71c7
msg_011CgXR1p3ibXZmTjTYeK7Tk:thinking:0 reasoning: Without the repo I can only go by the notes: PR 101 is a minor parser version bump with passing checks, probably safe but can't verify behavior changes without …
== AFTER: fix head 8785bb35f7
msg_011CgXR1p3ibXZmTjTYeK7Tk:thinking:0 assistant_message: Without the repo I can only go by the notes: PR 101 is a minor parser version bump with passing checks, probably safe but can't verify behavior changes without …
  • No client code changes. Web, desktop and mobile already render a reasoning item that becomes an assistant_message under the same id (https://git.cafe/pingdotgg/t3code/pulls/18923's path), so there are no captures.
  • Not checked:
    • a live T3 session end to end, because the model picks this shape only sometimes;
    • turns already stored as reasoning: they stay reasoning.

Model and harness: Claude Opus 5.5 (claude-opus-5-5) in Claude Code, orchestrated by T3 Code. Reviewed by Codex gpt-6.1-sol and Claude Opus 5.5.

🤖 Generated with Claude Code

juliusmarminge and others added 2 commits October 29, 2026 23:42
…tays reasoning

These tests reproduce #27238: on Claude 5.1+, a lone non-empty thinking
block right before an AskUserQuestion tool_use should become assistant
text, while a lone thinking block before Bash stays reasoning (#18923).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Claude 5.1+, a single thinking block right before an AskUserQuestion
tool_use carries the explanation the question refers to, but only a second
consecutive thinking block was promoted to assistant text, so the user saw
a question without its context. A lone thinking block before
AskUserQuestion is now promoted too; before any other tool it stays
reasoning.

Fixes #27238

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Oct 29, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 30, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 1d1b0f5

Macroscope's review found this PR approvable — The adapter now shows Claude’s lone explanation before AskUserQuestion as assistant text. The change is limited to eligible models and reuses existing message-emission safeguards without altering tool execution or permissions.

No code changes detected at d7950d7. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.5 KiB 21.5 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 1 1 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB 0 B (0.0%) 24.4 KiB ✅
Claude Live turn messages 1 1 0 (0.0%) 8 ✅

Baseline: c90b678 · PR result: d7950d7 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 1d1b0f5 and d7950d7.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review

No actionable comments were generated in the recent review.

Walkthrough

Claude snapshot handling now promotes a lone pre-tool thinking block to an assistant message when the next tool is AskUserQuestion. A regression test checks this behavior and confirms that a block before Bash remains reasoning.

Changes: Claude question explanation

Layer / File(s) Summary
Thinking block promotion and regression coverage
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts, apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts
The adapter emits the preceding block as an assistant message before AskUserQuestion, even when it does not follow another thinking block. The test checks this case and confirms that a block before Bash is represented as reasoning.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related issues

  • pingdotgg/t3code#32877 — This PR promotes a lone thinking block. It does not address possible loss of full text when a promoted second block contains only a summary.
  • pingdotgg/t3code#28775 — This PR addresses the Claude 5.1+ case where a lone thinking block before AskUserQuestion is not shown as an assistant message.
  • pingdotgg/t3code#26789 — This PR changes how a thinking block is represented, but does not address errors when users quote thinking-origin assistant messages.

Suggested reviewers: t3dotgg

Merge Risk: Minimal · previous review 1d1b0

The change addresses the missing question explanation without an established regression that needs resolution before merge.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: showing Claude's lone explanation before an AskUserQuestion as text. It uses a valid conventional commit format.
Description check Passed The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the bug, fix, scope, linked issue, test results, replay evidence, and unverified cases…
Linked Issues check Passed Issue [#27238] requires Claude 5.1+ to promote a non-empty lone thinking block to assistant text before AskUserQuestion. The adapter now accepts block.name === "AskUserQuestion" in the existing sa…
Out of Scope Changes check Passed The whole-PR changes are limited to the Claude adapter condition and comment, plus a regression test for [#27238]. The Bash case verifies that ordinary tool behavior remains unchanged. No unrelated …
Full details: Description check

Explanation

The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the bug, fix, scope, linked issue, test results, replay evidence, and unverified cases.

Full details: Linked Issues check

Explanation

Issue [#27238] requires Claude 5.1+ to promote a non-empty lone thinking block to assistant text before AskUserQuestion. The adapter now accepts block.name === "AskUserQuestion" in the existing same-message promotion path. The model gate and item emission path remain unchanged. The regression test verifies assistant_message for AskUserQuestion and reasoning for Bash.

Full details: Out of Scope Changes check

Explanation

The whole-PR changes are limited to the Claude adapter condition and comment, plus a regression test for [#27238]. The Bash case verifies that ordinary tool behavior remains unchanged. No unrelated production or client changes appear.

Finishing Touches

Generate docstrings

  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 8785bb3 and 1d1b0f5.

Files selected for processing: 2

  • apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts
  • apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts

Limit details: You’ve used all 10 included reviews currently available.

@juliusmarminge juliusmarminge added bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed labels Oct 30, 2026
@jacksonloughmiller-netizen

Copy link
Copy Markdown

I filed #27238. The code path this PR changes is the same at v0.1.6 and at main 3ee5515. In ClaudeAdapterV2.ts, a thinking block only becomes assistant text when it directly follows another thinking block in the same message (v0.1.6 :1772-1778 and :8410-8451). So a single pre-question thinking block still shows as a collapsed "Thought", and the user answers AskUserQuestion without reading it. I can't reproduce it on demand, because the model decides when it writes thinking.

This PR has carried pr:gate-failed since 2026-10-30. Every check that ran passed, and no comment says which gate failed. Which gate is it, and can I provide or test anything to move it along?

Drafted with Claude Opus 5.5 (claude-opus-5-5) in Claude Code, via t3 triage.

Copy link
Copy Markdown
Member Author

The automated pipeline stopped on this pull request and will not merge it:

someone else pushed to fix/issue-27238 (head is now 8785bb3); the pipeline does not build on commits it did not review

It stays open for a person to finish or close.

@macroscopeapp
macroscopeapp Bot dismissed their stale review November 11, 2026 03:02

Dismissing prior approval to re-evaluate 1d1b0f5

@juliusmarminge

Copy link
Copy Markdown
Member Author

The automated pipeline stopped on this pull request and will not merge it:

the capture step could not show the problem on the base commit, so there is nothing to prove fixed.

On both commits, create a synthetic project containing three payment proposals: automatic retries without idempotency keys, reusable idempotency keys, and full payload logging. Start fresh threads with the identical prompt: 'Review the three proposals in review.md. Explain your recommendations briefly, then use AskUserQuestion to ask which changes to merge. Do not implement anything.' Repeat and inspect native message shapes. Observed 14 question turns on base and 21 on head; every question message contained two thinking blocks. Open representative threads in each real client and inspect the explanation above the unanswered question. Both clients displayed assistant text. No lone-thinking-block case occurred.

It stays open for a person to finish or close.

This branch has not been deployed

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

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed size:XS 0-9 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude 5.x explanation written as a single thinking block before AskUserQuestion stays a collapsed Thought

2 participants