Repository navigation
[Bug]: Mobile has no way to stop background work after the turn settles #14655
Description
Activity
Note
Grok responding on behalf of Julius.
Triage
Thanks for tracing this through both clients, @tris203. Your source links held up exactly. I confirmed it on current
main(094fb230), the commit you cited. Once the parent turn settles, web still offers a Stop control for leftover background work, but mobile doesn't. That applies to both iOS and Android, since the gate lives in shared React Native code.Why web can stop it
Web shows the Monitoring banner only after the turn stops working, because by then the composer's Stop button is gone. Pressing its Stop sends
thread.turn.interruptwith onlythreadIdwhen the session isn'trunning(buildThreadTurnInterruptInputinapps/web/src/components/ChatView.logic.ts). The server accepts that command without a running turn (apps/server/src/orchestration/decider.ts), andProviderCommandReactorforwards it by session unless the session is missing orstopped. Claude'sinterruptTurnignores the turn id and closes the session.Why mobile can't
apps/mobilenever readsbackgroundLiveness, even though the thread shell the app receives already has it (EnvironmentThreadShellextendsOrchestrationThreadShell).- The composer's Stop button only appears when there's no draft and
session.statusisrunningorstarting(ThreadComposer.tsx). handleStopThreadreturns right away for any other status (ThreadRouteScreen.tsx), so showing a button wouldn't be enough on its own.
After a turn settles, the session is
readywhilebackgroundLivenessisworkingormonitoring. In that state the composer only offers Send and dictation, and nothing else on the thread screen calls the interrupt. I checked this in source rather than on a device, but none of these gates depend on the platform.Not a duplicate
- [Bug]: iOS does not show Monitoring status displayed on desktop and web #10372 and [Bug]: Mobile looks idle while background subagents are still working #4962 (open issues) cover the missing Monitoring and Working label in this same state. Neither asks for a stop control.
- fix(mobile): show Working and Monitoring when background work outlives the turn #13803 (open PR) adds that label but doesn't touch
ThreadComposerorhandleStopThread, so after it merges the phone would show Monitoring and still couldn't stop the work. - [Feature]: Mobile: sub-agent visibility parity — spawn indicator, Agents surface, and background liveness #6402 (closed) was a broader parity request that only mentioned stopping in passing.
Fix direction
On the open thread, when
backgroundLivenessis non-null and no turn is running, show a Stop control that sends the same thread-only interrupt web sends. Unlike the running-turn button, which is hidden whenever the composer has text, it should stay visible when there's a draft. Showing "Stopping…" until the liveness clears is a good idea, since the command returning only means the request was accepted.- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 1, 2026
Before submitting
Area
apps/mobile
Steps to reproduce
Expected behavior
Mobile offers a Stop control for background work that outlives the turn, the same as the web/desktop banner. Pressing it stops the background tasks and the thread returns to idle.
Actual behavior
Mobile has no stop control in this state. The composer shows the normal send/dictation actions, and nothing else on the thread screen can stop the work. The only way to stop it is to switch to web or desktop.
This is separate from the missing status label tracked in #10372 and #4962. #13803 fixes those by adding the Working/Monitoring label to the mobile list and the open-thread pill, but its diff has no stop or interrupt changes, so after it merges mobile will show Monitoring and still not be able to stop it.
Impact
Major degradation or frequent failure
It happens every time background work outlives a turn, and the phone is often the only client at hand when a user wants to stop a runaway watch loop or subagent fleet.
Version or commit
main@ 094fb23Environment
Source-level finding against
main; not reproduced on a device for this report. The code involved is shared React Native code, so it applies to both iOS and Android. The device reports on #10372 and #4962 (iOS, Pixel 9, Galaxy S25 Ultra) describe the same thread state.Source evidence
Web deliberately gives this state its own stop control, and says why:
ChatView.tsx#L6237-L6243: "once it settles, the composer stop button is gone, so this banner is the only visible stop affordance. Stop routes through the stop-everything interrupt ... and works by session, so no active turn is needed."ChatView.tsx#L6278-L6313: the banner and its Stop button.ChatView.logic.ts#L530-L539: the interrupt input is built withthreadIdonly when no turn is running.Mobile blocks the same action in two places:
ThreadComposer.tsx#L301-L304:showStopActionis true only whilesession.statusisrunningorstarting, so the "Stop agent" button is hidden once the turn settles.ThreadRouteScreen.tsx#L632-L649:handleStopThreadreturns early unless the session isrunningorstarting. Showing a button is therefore not enough; the handler also has to allow a settled session with background work.The server side already supports it.
backgroundLivenessis on the thread shell mobile receives (orchestration.ts#L928), and the Claude adapter's interrupt stops the whole session regardless of turn (ClaudeAdapter.ts#L5363-L5371). No contract or server change should be needed.apps/mobilehas no references tobackgroundLivenessonmain.Suggested fix
When
backgroundLivenessis non-null and no turn is running, show a Stop control on the mobile thread screen and send the interrupt withthreadIdonly, asbuildThreadTurnInterruptInputdoes on web. Hold a "Stopping..." state until the liveness clears, since the command returning only means the request was accepted.Workaround
Open the thread on web or desktop and press Stop on the Monitoring banner.
Related
Investigated from source with Claude Opus 5.5 in T3 Code.