@juliusmarminge , whenever you/agent re-visit this, #10991 was for antigravity not grok.
Turn folding hides the substantive answer when a provider ends a turn with a short progress note (Grok)#8879
Summary
deriveTurnFolds keeps exactly one entry visible per settled turn — the last assistant message, per deriveTerminalAssistantMessageIds. With providers that interleave short progress commentary with the actual answer, the last message of a turn is frequently a one-line "now I'll check X" note, so the real answer is folded away under Worked for Ns and the visible remainder is a fragment that reads like a non sequitur.
This is not the "collapse noisy tool calls" case the fold is designed for: the folded content is the response itself.
Environment
- Server
t3@0.0.33(headlesst3 serve, Linux x64, Bun runtime) - Desktop client
T3Code(Alpha)/0.0.33, Electron 41.5.0, macOS - Provider: Grok Build
1.0.4(ACP overgrok agent stdio), modelgrok-4.6
What happens
A Grok turn emits assistant messages as separate segments (assistant:…:segment:N) interleaved with tool calls. The turn commonly ends on a progress note rather than on the answer, e.g. turn a210c1f8:
| segment | time | length | content |
|---|---|---|---|
segment:5 | 10:07:34 | 3178 chars | the actual answer |
segment:6 | 10:10:12 | 146 chars | a progress note ("looks like a shared session — checking where live dev redirects to") |
segment:6 is the terminal message, so it stays visible; segment:5 is hidden behind Worked for ….
How common
One thread, 108 messages, 68 assistant messages across 36 turns. 19 turns had more than one assistant message; in 18 of those 19 the last message was not the longest. Terminal notes clustered at 77–146 chars while the answers they hid ran to 3178 chars.
Turn size distribution: 17 turns × 1 message, 14 × 2, 3 × 3, 1 × 5, 1 × 9.
Expected
The visible entry after folding should be the turn's substantive response. Folding should not be able to hide every assistant message that carries the answer.
Code
apps/web/src/components/chat/MessagesTimeline.logic.ts
deriveTerminalAssistantMessageIds—lastAssistantMessageIdByResponseKey.set(responseKey, message.id)on every assistant message, so the winner is positional: whichever message came last.deriveTurnFolds— everything in the group exceptterminalEntrygoes intohiddenEntryIds(onlyworkentries withagentSpawnare exempt).
apps/mobile/src/lib/threadActivity.ts appears to carry the same rule, so mobile is affected identically.
Possible directions
- Keep all assistant messages visible and fold only
workentries — the fold then does what its docstring says ("commentary and tool activity") without depending on which message happens to be last. - Treat a trailing assistant message that is immediately followed by tool activity as commentary rather than as the terminal message.
- Pick the terminal message by content rather than position.
Option 1 is the smallest change and does not need heuristics.
Notes
Reproduced against a live grok agent stdio session; the segmentation is the provider's normal output shape, not an error path.
Also hits the Claude Code provider: images the agent shows mid-turn get folded under "Worked for …" and I do not know they exist. +1 for merging #8903.
I see this with Claude Code v2.1.286 and T3 Code 0.0.44. It is highly frustrating when the final response is something like I answered your question., but the answer is nowhere to be seen. Given most of the time what is hiding in the Working/Thought expandables is nothing of use, it trains you not to look in them.
Same with the Claude provider on T3 Code 0.0.45 (headless t3 serve, macOS arm64, model claude-opus-5-5).
Pattern from one turn:
- The agent writes its full answer for the user (about 2,500 characters with several tables).
- It then makes one more tool call (cleaning up temporary files).
- It ends the turn with a one-line note: "The temporary files from the calculation are deleted."
After the turn settles, only that one-line note is visible. The answer is folded under "Worked for 1m 19s", and the user had to ask for it again. The user sees this often in long Claude threads, so it is not specific to Grok.
In the same threads, user-facing text written just before a tool call sometimes shows up as a collapsed "Thought" (#18355), which hides responses the same way.
Our workaround is to tell the agent to end every turn with the complete answer and make no tool calls after it. Agents should not need that: the fold should keep a turn's substantive response visible, as #8903 and #14698 aim to do.
Still happens on 0.1.1-nightly.20261019.3348, with OpenCode 2 and Claude Opus 5.5, and it's not only a Grok thing.
In one run and one provider turn, the agent wrote a 4.3K-character answer, then launched a background subagent and added an 885-character status note. Nothing folds while the run is live, so I was reading the answer. When the run settled 22 seconds later, the answer went under "Worked for 2m 6s" and only the status note was left in view. It looked like the reply had been replaced. Nothing was lost; expanding the fold shows it.
How often, over a week of runs: an earlier assistant message of 1,000+ characters that is more than twice as long as the final one happened in 14 of 1,805 runs on one machine and 3 of 723 on another.
#14698 fixes this case for us.
Model and harness: Claude Opus 5.5 via OpenCode 2 in T3 Code.
Additional Claude example observed on October 20, 2026, on a newer nightly.
Environment:
- Running T3 Code server:
0.1.1-nightly.20261020.3411, verified through the server's environment API. - Server platform: Linux x64.
- Affected provider/model: Claude /
claude-opus-5-5. - Client app/browser version has not been independently verified.
Observed sequence:
- The assistant supplied a detailed answer with alternatives and a recommendation.
- It updated a Markdown file through a tool.
- It sent a separate follow-up question.
- After completion, the substantive answer was hidden under “Worked for 25s,” while only the follow-up question remained visible.
- Expanding the fold restored the answer.
The stored original turn confirms separate assistant messages around the file-change tool activity. No independent synthetic reproduction or root-cause verification was performed. Expected behavior: the substantive answer should remain readable with work activity collapsed. The current workaround is manually expanding the fold.
Screenshots supplied by the reporter:
Default collapsed state: only the later follow-up question is visible.
Expanded state: the detailed explanation, alternatives, and recommendation appear.
We initially added this evidence to closed issue #7518: https://github.com/pingdotgg/t3code/issues/7518#issuecomment-6292305496. Adding it here because this open issue tracks the same behavior and already includes Claude examples. The Codex screenshot pair in that earlier comment is comparison material: its final answer remains visible, so it is not another demonstrated hidden-answer occurrence.
Prepared with GPT-6.1-Sol through the Codex harness in T3 Code.
Same here on 0.1.1-nightly.20261020.3386 (macOS), with Claude (claude-opus-5-5) and also Codex.
My case: the agent wrote a 2,243-character answer, made two tool calls to save a memory note, then ended with an 89-character sentence. The answer was on screen for about 8 seconds. When the run settled, everything except the 89-character line went behind "Worked for 1m 28s". The full text is in the projection DB, so this is only the client fold.
I ran the same rule (last assistant message per completed run stays visible) over a week of my own local history to see how often it happens:
- 376 completed runs, 130 of them with more than one assistant message, 515 messages folded in total
- most of those are short narration, where folding is what you want
- 6 runs folded a message of 600+ characters
- in 3 runs the folded message was longer than the visible final one: 2,243 vs 89 (Claude), 5,063 vs 2,540 (Claude), 2,194 vs 863 (Codex)
So it's not specific to one provider. Any run where the real answer isn't the last text block loses it from view. In my history about 1 run in 60 hid a substantial message.
I agree that a length cutoff isn't the right fix, per the reasoning on #8075. Folding only tool calls and reasoning, and leaving assistant prose in the timeline, would cover every case above. That looks like what #8903 does. Would it be worth reopening now that it has screenshots and has been moved onto main?
Confirmed with Codex / gpt-6.1-sol on macOS desktop, 0.1.3-nightly.20261027.3748.
Reviewed #14698 with GPT-6.1-Sol, high reasoning. The change looks right for this case: keep assistant messages visible and fold tools/reasoning. This was a code review; we haven't tested the patch against this thread.
Steering can hide a complete answer even with no tool calls or progress notes:
- Ask for a table of optimizations and their impact.
- While it’s running, steer with “and any potential tradeoffs”.
- Let it finish. Only the tradeoffs answer stays visible. The optimization table disappears into “Worked for 56s”.
The Codex transcript confirms two final_answer messages in the same run and provider turn. Both are saved intact. Expanding the fold reveals the first answer.
T3 keeps only the last assistant message per run visible. That rule is still on current main, and this build already includes #19023 and #22783.
The tradeoff in #14698 is that progress prose also stays visible, which seems preferable to hiding answers.
A useful regression case: prompt → steer → final answer → final answer, all in one completed run. Both answers should remain visible with work collapsed.
Additional confirmed Claude case, with a proposed direction that preserves final answers without exposing every progress message.
What happened
On Windows desktop with the Claude Agent provider, an assistant gave a long answer. Delegated-task notifications then continued the same run, and the assistant posted a shorter status update. The long answer moved inside the collapsed Worked-for fold. Expanding the fold restores it; this case does not involve missing stored text.
The user explicitly rejected keeping all assistant messages visible. The intended behavior is to keep messages identified as final answers visible and continue folding progress commentary.
Evidence and diagnosis
Read-only thread activity on 2026-10-31 shows this sequence (UTC):
- 04:12:57: user question.
- 04:13:39: completed assistant answer.
- 04:13:42: delegated-task completion notification.
- 04:14:07: new delegated review starts.
- 04:14:12: another delegated-task completion notification.
- 04:14:33: completed assistant status update.
Both assistant messages and the intervening activity belong to the same run. The supplied screenshot pair shows the earlier answer inside the expanded Worked-for fold, then absent with the fold collapsed while task cards and the newer reply remain visible. Private conversation text, thread identifiers, and screenshots are omitted.
In the fork checkout at 3967fbf23abf4a5de1ab5a898dfba036e010e252, deriveTerminalAssistantMessageIds chooses the last assistant message per run, and deriveTurnFolds exempts only that terminal message from folding. The same rule is present in freshly fetched upstream main at 80a2351fcb909034e65a512dfabeb3cdfd367762. Mobile has analogous last-message-per-run selection. This establishes the presentation cause, rather than a fork-only regression. The installed release version was not established.
Proposed direction, not implemented
Preserve provider-reported answer intent through the event/projection contract, and have web/desktop and mobile keep every explicitly classified final answer outside the work fold. Later messages, steers, or task notifications must not demote an already classified answer. Continue folding commentary. Keep unknown distinct from commentary.
There are existing provider signals to investigate and retain:
- Codex
agentMessage.phasedistinguishesfinal_answerandcommentary.CodexAdapterV2already reads that distinction for internal final-answer handling, but its emitted assistant-message turn item does not include it. An absent phase should not be treated as explicit final-answer evidence. - Claude has native stop reasons.
ClaudeAdapterV2already recognizes transcript turn endings, and its tests contain streamingmessage_deltaevents withend_turnversustool_use. The current stream handler processes usage there without retaining answer intent in the assistant-message projection. Associate the eventual stop reason with the correct native message, including separately emitted text blocks. Confirm successful response completion; a completed text block, truncation, interruption, or any arbitrary non-tool stop reason is not sufficient evidence by itself.
This is a feasible implementation direction based on source inspection, not a tested patch or proof of the exact native stop reason for the historical answer above. Other providers need an explicit supported/unknown decision. Old persisted history may need to retain existing behavior unless trustworthy provider evidence is available; guessing from length, wording, or position would not solve the distinction reliably.
Meaningful regression coverage would include two explicitly final answers in one run, commentary between them, delayed Claude stop metadata, and interruption/truncation remaining unclassified as successful final answers. A fixture matching this notification sequence should keep both answers visible while work is collapsed, including after reload.
No source changes, database writes, running-app changes, or PR were made. No tests were run because this is investigation only. Adding this evidence to the existing report avoids a duplicate issue. Related: #5297 and #17382. PR #14698 takes the broader keep-all-assistant-text approach; that is not the requested behavior here.
Investigated by GPT-6.1 Sol through the Codex harness in T3 Code.
The metadata-based fix is now in #27991. The original Claude SDK log and saved transcript both confirm end_turn for the affected answer; the SDK marker arrives 14 ms after its text snapshot. The patch preserves that provider signal and explicit Codex phases through storage and client folding, keeping confirmed answers visible while commentary still folds. The PR includes before/after captures from an isolated app fixture, focused regressions, known Windows baseline test failures, and the exact-commit CI link. Old history without metadata retains its existing fallback.
Another Claude case, this one caused by a steer, plus an ordering problem that may be separate.
What happened (Claude Agent provider, macOS desktop, model claude-opus-5-5)
- A user prompt (
$learn) started a run, and Claude worked on it for about 80 s. - While Claude was streaming its final answer to that prompt, I sent a second message, and it went in as a steer.
- The steer was stored at 13:42:35.291. The final answer to the first prompt was stored at 13:42:35.339, 48 ms later. Both are in the same run.
- Claude then answered the steer, and that answer became the run's last assistant message.
What the timeline shows
- First prompt, then "Worked for 2m 13s", then the steer message, then "Continued for 53s", then the answer to the steer.
- The full answer to the first prompt is folded inside "Continued for 53s", after the steer, even though it was written before the steer was sent. Expanding the fold shows it.
So there are two problems:
- Folding: a confirmed final answer is folded because a steer extended the run. This looks like what #27991 fixes.
- Ordering: an answer that was still streaming when the steer arrived is placed after the steer, apparently because items are ordered by when they finished rather than when they started. #27991 doesn't change this: its client changes in
MessagesTimeline.logic.tsonly decide which messages stay visible, and nothing in the PR sorts or repositions timeline items. With the PR, the answer would stay visible but still appear after the steer, so the ordering needs its own fix.
Same on the macOS desktop app, 0.1.8-nightly.20261106.4275 (server version from /.well-known/t3/environment), Claude Agent provider, claude-opus-5-5.
In one run, Claude wrote a 2,311-character answer, then wrote a file (one file_change item), then ended with a 213-character note saying the file was ready. After the run settled, only the 213-character note was visible. The answer was under "Worked for 1m 42s", with nothing to show that the actual reply was in there. Both messages are in orchestration_v2_projection_messages with the same run_id, so this is the client fold, not lost data. deriveTerminalAssistantMessageIds is unchanged on main as of 2026-11-08, so it is still present. +1 for landing #14698 or #27991.
Filed via t3 triage by Claude Code (Opus 5.5).