Skip to content

[Bug]: Mobile Stop does nothing during 'Setting up worktree…' (preparing phase) #12187

Closed

Description

@ImBIOS

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate.

Area

apps/mobile • packages/client-runtime

Description

Mobile Stop does nothing during Setting up worktree… (thread-creation preparing phase).

Follow-up to #8618 (remote Stop gives no feedback / stuck in Thinking, fixed by #8619). That issue/PR covers the Thinking/Working phase where session.status is running/starting. This report covers the earlier window: a new task with workspaceMode: "worktree" shows Setting up worktree… while the creation is still queued or the server is still checking out the worktree — and Stop is either hidden or a silent no-op.

Reproduced against remote server myrehat-dev (Tailscale/relay) in a thread like "Chatwoot and Human Takeover": tap Stop while the pill reads Setting up worktree… → nothing happens immediately, no Cancelling…/Stopping… feedback, worktree setup continues.

Steps to reproduce

  1. On mobile, connected to a remote environment, create a new task with worktree mode (new branch).
  2. While the floating pill reads Setting up worktree… (creationState.kind === "preparing", preparingWorktree === true), tap the Stop control.
  3. Observe: no immediate feedback; the creation continues.

Actual

  • ThreadRouteScreen.handleStopThread early-returns unless selectedThread.session?.status is running/starting (ThreadRouteScreen.tsx:501-508). During preparing, the stand-in shell from pendingThreadCreationShell has session: null (pending-thread-creation.ts:159), so Stop is a no-op.
  • ThreadComposer.showStopAction is also gated on session running/starting (ThreadComposer.tsx:308-311), so the stop affordance may not even render during the queued window.
  • Even when a server shell has arrived with starting, there is no active turn yet (latestTurn === null, empty detail during checkout), so thread.turn.interrupt has nothing to act on server-side (ProviderCommandReactor.processTurnInterruptRequested → "No active provider session").
  • There is no path to cancel the queued creation from the thread screen (remove the outbox entry + clear the pending-creation outcome); the drain keeps it and delivers it.

Expected

  • Stop during preparing gives feedback within one frame (Cancelling…/Stopping…, disabled) on every surface that renders the pill/composer.
  • If the creation is still queued locally, Stop cancels it: remove the outbox message (compare-and-set, respecting editingQueuedMessageIdsAtom), clear pendingThreadCreationOutcomesAtom for the thread key, and return to a sane screen (draft restored or back).
  • If the creation was already delivered (server thread exists, worktree checking out, no turn yet), Stop still sends the best-effort interrupt/stop AND surfaces that the setup cannot be cancelled mid-checkout if that is the decided behavior — never a silent no-op.
  • Relay/offline failures surface as a toast instead of silence.

Evidence & analysis

Investigated on main (cdfeb1d):

  • apps/mobile/src/features/threads/ThreadRouteScreen.tsx:501 handleStopThread guard; :785-800 creationState preparing derivation.
  • apps/mobile/src/features/threads/ThreadComposer.tsx:308 showStopAction guard; :805,896 stop buttons.
  • apps/mobile/src/state/pending-thread-creation.ts:132-165 stand-in shell session: null; :28-68 resolvePendingThreadCreation retention.
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:1586-1600 interrupt with no session → failure activity only.
  • fix(remote): stop button gives immediate feedback and prevents stuck thinking on remote server #8619 adds isStoppingThread optimistic UI but keeps the same running/starting gate, so preparing is still uncovered.

Environment

  • Mobile client (iOS/Android) → remote T3 server myrehat-dev over Tailscale/relay.
  • New task with worktree checkout (Setting up worktree…).

Suggested fix (non-negotiables)

  1. Mobile handleStopThread: branch on creationState.kind === "preparing" — cancel queued creation locally (outbox removal + clear outcome) with immediate optimistic state; if already delivered, fall through to interrupt with isStoppingThread feedback.
  2. Render a stop affordance during preparing (not just running/starting), disabled + Cancelling… while the cancel settles.
  3. Guard the outbox race: compare-and-set removal, respect in-edit messages (defer, don't drop user edits).
  4. Tests: pending-thread-creation + outbox-removal unit coverage for cancel-during-preparing; no repo-wide checks.

Activity

  1. juliusmarminge commented on Sep 17, 2026

    @juliusmarminge
    Member

    Triage

    Confirmed on current main (901db8966). This is a real mobile Stop bug during the thread-creation preparing window, not a remote-Thinking hang and not a missing setup-progress card. Not already fixed. Not a duplicate of #8618.

    What you reported. On mobile, connected to a remote environment, a new worktree task shows Setting up worktree…. Tapping Stop does nothing: no Cancelling… / Stopping…, and setup continues. You traced it to handleStopThread and showStopAction requiring session.status running/starting, while the pending-creation stand-in has session: null and there is no thread-screen path to cancel a still-queued creation.

    That matches the code.

    What the code does

    1. Composer Stop is still gated on a live session.

    ThreadComposer.showStopAction (apps/mobile/src/features/threads/ThreadComposer.tsx:304-307) is session.status === "running" | "starting" and an empty draft. During preparing, pendingThreadCreationShell sets session: null (apps/mobile/src/state/pending-thread-creation.ts:159), so the Stop affordance does not render. The floating pill is display-only (floating-working-control.tsx: only the reconnect label is tappable).

    2. handleStopThread is a silent no-op in that window.

    ThreadRouteScreen.handleStopThread (ThreadRouteScreen.tsx:542-548) returns unless the selected shell is running/starting. There is no isStoppingThread on mobile (or web). #8619 is still open and does not land that optimistic UI.

    3. A queued creation cannot be cancelled from the thread screen.

    The stand-in is kept by resolvePendingThreadCreation until the prompt is in detail. Home can delete a pending task (usePendingTaskListActions → removeThreadOutboxMessage). The thread screen has no equivalent: it does not remove the outbox row or clear pendingThreadCreationOutcomesAtom. The drain keeps delivering.

    4. #12177 added a different cancel surface; it does not fix this.

    feat(mobile): port worktree setup progress and agent handoff (4abb07eac, merged today) hides the preparing pill once a worktree-setup snapshot exists and puts Cancel setup / Work locally in the Details sheet (worktree-setup-card.tsx:210-264). It also remaps onStopThread to handleCancelWorktreeSetup while awaitingBootstrapTurn (ThreadRouteScreen.tsx:837-846, 1053). That remapping is dead for the composer: showStopAction is still false, so Stop is never shown to invoke it. handleCancelWorktreeSetup only RPCs vcs.worktreeSetupCancel after a live/recorded snapshot — it does not cancel a still-queued outbox creation. handleCancelWorktreeSetup is void with no optimistic disabled/Cancelling… state.

    5. Interrupt is the wrong tool before a turn exists.

    Once a server shell exists with no session (or starting and latestTurn === null), thread.turn.interrupt hits ProviderCommandReactor.processTurnInterruptRequested (ProviderCommandReactor.ts:1514-1522) and records provider.turn.interrupt.failed / “No active provider session is bound to this thread.” Server cancel settlement for an in-flight checkout is already #12176 (worktreeSetupCancel).

    Related, not duplicates

    Work Why this is still open
    #8618 (open) / #8619 (open) Remote Stop during Thinking/Working (session running/starting). Different window.
    #12177 (merged) Mobile setup progress + Details Cancel setup. Composer Stop and queued-creation cancel unchanged.
    #12176 (merged) Server: settle worktreeSetupCancel before rollback. Needed by the delivered/checkout path; does not show Stop.
    Home pending-task delete Outbox removal exists off the thread screen only.

    Suggested smallest scope

    Stay on mobile thread create. Do not wait for #8619. Do not change web. Do not add OpenCode sanitizer work.

    • Show Stop while creationState.kind === "preparing" (composer, and any other surface that owns the preparing pill). Immediate optimistic Cancelling… / Stopping…, disabled until settle.
    • Still queued (outcome null, not the in-flight dispatchingMessageId): removeThreadOutboxMessage compare-and-set, respect editingQueuedMessageIdsAtom, clear pendingThreadCreationOutcomesAtom, leave a sane screen (restored draft or back).
    • Already delivered and setup phase === "running": call the existing cancelWorktreeSetup path (fix(server): settle cancelled worktree setup before rollback #12176/feat(mobile): port worktree setup progress and agent handoff #12177), not thread.turn.interrupt. If product decides mid-checkout cannot be cancelled, say so in the UI — never a silent no-op.
    • Surface relay/offline cancel failures as an alert/toast.
    • Unit-test the preparing Stop decision (pending-thread-creation + outbox removal). No repo-wide checks.

    Open PR

    #12188 (fix(mobile): make Stop work during preparing worktree phase) is the same author and Fixes #12187. The mobile slice is the right idea (resolvePreparingStopAction, composer Stop during preparing, outbox cancel, isStoppingThread).

    Do not review that PR as-is. It is size:L / vouch:unvouched and mixes an unrelated OpenCode OSC/ANSI sanitizer (stripTerminalEscapes.ts, OpenCodeAdapter / OpenCodeProvider / opencodeRuntime, packages/shared/src/model.ts). CONTRIBUTING: do not mix unrelated fixes. Same problem as #12186.

    The helper also returns noop for a delivered creation with session: null, and the interrupt branch does not use cancelWorktreeSetup for an in-flight checkout.

    Workaround

    • Before delivery: Home → delete the pending task (outbox removal).
    • After a setup snapshot exists: thread feed → Details → Cancel setup (or Work locally).
    • There is no composer Stop during the queued Setting up worktree… pill.

    Next step

    Accepted. Keep this issue open until a mobile-only PR lands. Split the OpenCode sanitizer out of #12188 (or open a fresh PR with just preparing Stop) before review.

  2. added
    bugSomething is broken or behaving incorrectly.
    acceptedfeature request accepted
    via-triageFiled through npx t3 triage
    on Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions