[Bug]: Android shows model thinking as regular messages
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Related, not a duplicate: #12134 is about desktop collapsing traces to a generic Thought label. Julius's triage there already names this mobile path: clients that do not send reasoningMessages=true get reasoning remapped to system, and mobile then prints that text as a normal message. This report is that Android bug.
Area
apps/mobile
Steps to reproduce
- Run a current T3 server that persists provider thinking traces (#11784 and later).
- Open the same thread in the Android app (Play build, or any client that does not send
reasoningMessages=true, or one that resumes a pre-opt-in SQLite snapshot). - Send a prompt that produces a thinking/reasoning trace (Codex summary, Claude thinking, OpenCode, etc.).
- Compare the same turn on web/desktop.
Expected behavior
Thinking is visually distinct from the answer: a Thinking / Thought disclosure (brain icon, muted chrome), not assistant body text. The actual answer stays a regular message.
Actual behavior
On Android the remapped system traces render with the same assistant markdown as the answer. There is no Thought row. You cannot tell thinking from the reply. Web/desktop either hide system rows or show real Thought disclosures after they opt in.
This is the compatibility remap in ActivityPayloadProjection: without reasoningMessages=true, reasoning becomes system with the same id/text/sequence. Mobile ThreadFeed only special-cases role === "reasoning"; everything else that is not a user bubble is assistant markdown.
Android hits it more than iOS because:
- The Play build is often behind the server that just started sending traces, so it never opts in.
- SQLite snapshots written under the remap keep
role: "system".afterSequenceresume does not rewrite them. - Thread subscribe only opts in when
initialConfig.reasoningMessages === true. If that config is missing (orElseSucceed({})after Android suspends the socket), the client stays on the old wire format.
Impact
Major degradation or frequent failure
Version or commit
main after #11784 (052c7ae and later)
Environment
Android mobile app talking to a current T3 server.
Screenshots, recordings, or supporting files
Workaround
Use web/desktop, or an Android build that sends reasoningMessages=true and has a cache that was not written under the system-role remap.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in apps/mobile at ThreadFeed and the thread subscribe path, then trace the ActivityPayloadProjection compatibility remap and SQLite snapshots. Reproduce with a client missing reasoningMessages=true and inspect resumed data afterSequence. Done means Android renders remapped thinking as a distinct Thought disclosure while the actual answer remains a regular message.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, sqlite, typescript
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100