Skip to content

fix(mobile): stop background work after the turn settles - #14846

Open
tris203 wants to merge 1 commit into
pingdotgg:mainfrom
tris203:t3code/mobile-stop-background-work-1
Open

tris203 wants to merge 1 commit into
pingdotgg:mainfrom
tris203:t3code/mobile-stop-background-work-1

Conversation

@tris203

@tris203 tris203 commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

After a turn completes, background tasks can keep running while mobile has no way to stop them. Add a separate Stop action to the existing “Waiting on…” pill, preserving the composer draft.

Fixes #14655. Replaces #14661 with the requested interaction evidence. Rebased onto main at 61b9790816.

V2 implementation

Use the existing pendingBackgroundTasks / presentPendingBackgroundWork model and its task labels. The thread-only shared interrupt command resolves the settled run with pending work and sends V2 run.interrupt; no server, contract, or provider changes are needed.

Stop is one more segment of the existing pill, built like the agents and queue segments beside it; the status label itself is unchanged from main. It is hidden when the client lacks the orchestration operate scope.

Pending behavior now matches V2 web: Stop is disabled as Stopping… while the interrupt request is pending. It becomes available after acceptance or failure if tasks remain visible; acceptance is not proof of termination. The pill disappears when the pending-task roster clears. This deliberately replaces the previous V1 implementation's “disabled until liveness clears” behavior.

Request state is scoped to the environment/thread and the individual request, so an older completion cannot clear a newer request. Only the separate Stop button interrupts work. Existing status priority, queue/agent/device controls, and foreground-turn Stop behavior are preserved. Long task labels truncate while Stop remains visible.

Approval and scope

Three mobile files. Web/desktop already provide this path. Shared iOS/Android code; native verification is Android/Claude only. Other providers retain their existing interrupt semantics. iOS and relay/tunnel were not device-tested.

Evidence

Current layout

Android 16 Pixel emulator against an isolated worktree-local server, main at 611132c171 plus this change. A real Claude Sonnet 4.6 session started sleep 3600 with run_in_background=true and completed its turn. Tapping Stop ended the sleep, cleared the pill, and left the draft in place.

Stop segment beside the task label, draft in the composer After Stop: pill cleared, draft retained
Stop segment in the background work pill with a draft Pill cleared after Stop with the draft retained

12-second recording of the tap:

stop-interaction.mp4

The pairing query from #10298 fails on a local server (it binds a JS boolean), so this run used an uncommitted one-line local workaround for pairing only. It is not part of this PR.

Pending and failure behavior (earlier layout)

The material below was recorded before Stop became a pill segment. The Stop text sat in a separate label component then; the request handling it exercises is unchanged. The pending, retry, and failure states have not been re-recorded on the current layout.

Android 16 Pixel emulator, compatible current development client, V2 main plus this change. A fresh worktree-local .t3 and manufactured project/thread were created through server commands; no live user data was copied. A real Claude Sonnet 4.6 session started sleep 3600 with run_in_background=true, completed its turn, and left a command in pendingBackgroundTasks. No separate runtime prerequisite patch was needed.

Before: V2 waiting pill without Stop After: Stop beside the same task label and draft
Before: waiting on a background command with no Stop After: separate Stop action with the draft retained

22-second continuous recording, normal speed, with explanatory captions:

verification-v2.mp4

Original recording without captions.

A test-only WebSocket proxy rejects the first interrupt with OrchestrationV2DispatchCommandError. On retry it forwards the real interrupt, holds the successful response and subscription updates separately, then releases each. This makes the transient states observable without modifying production code or fabricating task completion.

Recording Observed result
0–3s: failed request Stop becomes available for retry; task and draft remain.
3–13s: retry pending Stopping… is disabled (enabled: false in the native tree). The extra tap sends no new interrupt: count remains 2 total, including the rejected attempt.
13–20s: acceptance delivered Stop is enabled again (enabled: true) while the task roster is still held. This is the V2 web behavior.
20–22s: task update delivered The pill disappears. The server projection confirms an empty pending-task roster and the run remains completed.
Throughout Keep this draft while stopping background work. stays unchanged.

Checks

  • Mobile typecheck passed.
  • 82 focused tests passed: mobile floating status, client-runtime thread execution, and shared V2 pending background work.
  • Targeted lint: no errors; the same 30 warnings as main, compared ignoring line-number shifts.
  • Formatting and git diff --check passed.

Implemented and verified with GPT-6-Astra through the Codex harness in T3 Code. Rebased, reworked to reuse the pill's segment UI, and re-verified with Opus 5.5 through the Claude Code harness in T3 Code.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Oct 2, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 2, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This adds a new mobile Stop control that can interrupt real background tasks after a turn settles, including new request-state and retry behavior. The change is compact and reuses existing infrastructure, but the new native interaction and wiring are not directly covered by an identifiable mobile control test, so the runtime behavior merits human review.

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

@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 2, 2026 — with ChatGPT Codex Connector
@macroscopeapp
macroscopeapp Bot dismissed their stale review October 2, 2026 17:25

Dismissing prior approval to re-evaluate d8abb52

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 2, 2026
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f4cfb05f-f89a-4e80-9146-081096331bb4
📥 Commits

Reviewing files that changed from the base of the PR and between 4ddc3e3 and 98c205b.

📒 Files selected for processing (3)
  • apps/mobile/src/features/threads/ThreadDetailScreen.tsx
  • apps/mobile/src/features/threads/ThreadRouteScreen.tsx
  • apps/mobile/src/features/threads/floating-working-control.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The mobile thread screen now offers a Stop control for background work. The route sends an interrupt for the selected thread and tracks the request state until the call settles.

Changes

Mobile background-work stopping

Layer / File(s) Summary
Background-stop request and state propagation
apps/mobile/src/features/threads/ThreadRouteScreen.tsx, apps/mobile/src/features/threads/ThreadDetailScreen.tsx
The route tracks stop requests for the selected thread, sends an interrupt without a run ID, and clears the matching request when the call settles. The thread screen passes the stop state and callback to the floating control when the thread can be operated.
Floating background status control
apps/mobile/src/features/threads/floating-working-control.tsx
The background status displays a Stop control when a stop action is available. The control displays “Stopping” and is disabled while stopping. Its width is measured and deducted from the status-label space.

Priority: ➖ Normal

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

Change: Feature · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant FloatingWorkingControl
  participant ThreadRouteContent
  participant ThreadInterrupt
  User->>FloatingWorkingControl: Press Stop
  FloatingWorkingControl->>ThreadRouteContent: Invoke stop callback
  ThreadRouteContent->>ThreadInterrupt: Interrupt selected thread without run ID
  ThreadRouteContent->>ThreadRouteContent: Clear matching request when call settles
Loading

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 98c20

This adds a Stop control for background work in mobile threads. No concrete merge-blocking defect was established. One open question remains: whether a rejected interrupt call could leave the button showing "Stopping" until the user switches threads. The author could confirm this cheaply.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 98c20

The new action preserves existing operation permissions and selected-thread targeting. Stop is thread-wide, not limited to one displayed task, and request acceptance does not guarantee termination. Downstream isolation and termination behavior remain only partially verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The observed action scope is thread-wide rather than task-specific: it holds queued work, can interrupt pending work across multiple provider threads in the selected thread's projection, and can disable its visible pull-request watches. These outcomes do not demonstrate isolation between authenticated environments or tenants.

Trust Boundaries and Controls

  • observed — The server authenticates the WebSocket upgrade, installs authorization using that session's scopes, and requires orchestration:operate before dispatchCommand executes. The operation is therefore not protected solely by a mobile UI permission check.
  • observed — The server loads the command thread's projection, validates its run and provider-thread references, and derives provider effects from that projection. This establishes target routing, not independent authenticated thread ownership or provider-specific authorization; those portions of the supplied proof gaps remain unresolved.

Resilience and Maintainability Implications

  • observed — Commands serialize per environment/thread, limiting concurrent client transitions in the same scope. Pull-request-watch updates are nevertheless sequential rather than an atomic group, so partial acceptance can leave some watches unchanged. The UI permits retry after settlement, but backend duplicate effects and eventual provider termination were not established.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements the mobile Stop action and sends a thread-only interrupt for settled background work [#14655]. It preserves the draft and permits retry after a failed request. However, #14655 requir… Keep Stop disabled as “Stopping…” until the pending background-task roster or equivalent background liveness clears. Clear the state after request failure only when retry must be available.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The reported changes are limited to four mobile files. They add the background Stop control, thread interrupt handling, state propagation, and layout support for the objective in #14655. No unrelated …
Title check ✅ Passed The title clearly and concisely describes the main mobile change: stopping background work after the turn settles.
Description check ✅ Passed The description covers the problem, implementation, scope approval, verification steps, observed results, limitations, screenshots, recordings, and agent attribution. It meets the required template.
Full details: Linked Issues check

Explanation

The PR implements the mobile Stop action and sends a thread-only interrupt for settled background work [#14655]. It preserves the draft and permits retry after a failed request. However, #14655 requires Stop to remain disabled as “Stopping…” until background liveness clears. The implementation clears the stopping state when the interrupt request settles, so it re-enables Stop while pending work remains visible.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

@tris203
tris203 force-pushed the t3code/mobile-stop-background-work-1 branch from d8abb52 to 2e31bf9 Compare October 2, 2026 21:37
@macroscopeapp
macroscopeapp Bot dismissed their stale review October 2, 2026 21:38

Dismissing prior approval to re-evaluate 2e31bf9

@tris203
tris203 force-pushed the t3code/mobile-stop-background-work-1 branch 2 times, most recently from f149308 to 4ddc3e3 Compare October 7, 2026 13:51
@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Oct 7, 2026
@tris203
tris203 force-pushed the t3code/mobile-stop-background-work-1 branch from 4ddc3e3 to 98c205b Compare October 8, 2026 18:14

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

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:M 30-99 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.

[Bug]: Mobile has no way to stop background work after the turn settles

2 participants