Background work bar: Stop re-enables as soon as the interrupt is accepted, so it flickers and invites repeat clicks#24993
What happened
A Claude thread started a Next.js dev server in the background, and the composer showed the bar "Running: Start Next.js dev server" with a Stop button. Clicking Stop makes the button text flicker between "Stop" and "Stopping...", and the bar stays up for a few seconds before it goes away. It flickers again on every click. It looks like Stop didn't work, so I clicked it again.
Diagnosis
On web/desktop, the bar's Stop handler clears its "stopping" state as soon as the server accepts the interrupt command, not when the background task ends:
apps/web/src/components/chat/useThreadLifecycle.tsx:392-404(handleStopBackgroundWork) setsstoppingBackgroundWorkKey, awaitsinterruptThreadTurn, then clears it. The comment there says "Acceptance does not confirm termination. Allow retry while the provider finishes stopping the tasks or reports a failure."useThreadLifecycle.tsx:532-533passes that state asstopDisabled/stopping, andThreadActivityBanners.tsx:155renders"Stopping..."vs"Stop"from it.
The accept round-trip is about 25 ms, but the provider takes about 2 s to stop. So the label shows "Stopping..." for one frame, returns to an enabled "Stop" while the task is still running, and the bar stays until the task is reported ended. Each click repeats this and sends another interrupt.
Server trace for the session (server.trace.ndjson), one burst of clicks:
| 1 | 01:54:56.069 ThreadCommands.interruptTurn 30 ms |
| 2 | 01:54:56.099 ProviderTurnControlService.interrupt 2002 ms (ClaudeAdapterV2.interruptTurn 2001 ms) |
| 3 | 01:54:57.159 ThreadCommands.interruptTurn 25 ms |
| 4 | 01:54:57.627 ThreadCommands.interruptTurn 25 ms |
| 5 | 01:54:57.835 ThreadCommands.interruptTurn 21 ms |
| 6 | 01:54:58.018 ThreadCommands.interruptTurn 26 ms |
| 7 | 01:54:58.109-123 ProviderTurnControlService.interrupt x4, ~0.4 ms each |
Five interrupt commands in about 2 s, because the button was re-enabled after each ~25 ms accept while the first ~2 s provider interrupt was still running.
Expected: the button stays "Stopping..." and disabled until the background task leaves the bar or the stop fails. Then it either disappears or re-enables with an error.
References are on main aa247c52. The two web files have no changes to this logic since 2026-10-22.
Steps to reproduce
- In a Claude thread, ask the agent to start a long-running background command, such as
npm run devfor a Next.js app. - After the turn ends, the composer shows "Running: Start Next.js dev server" with Stop.
- Click Stop once. The label flashes "Stopping..." and immediately returns to "Stop" while the bar stays for a few seconds.
- Click Stop again during that window. It flickers again and another interrupt is sent.
Version
T3 Code (Nightly) desktop 0.1.3-nightly.20261024.3630.
Environment
macOS 15.1, desktop app against its local server. Provider: Claude (claudeAgent), model claude-opus-5-5.
Impact
Minor bug. Stop does work eventually, but the UI says it didn't, which invites repeated clicks and sends extra interrupts.
Related
- #16419 fix(server): stopping a Claude turn no longer hangs without an acknowledgement (server-side stop latency, not this UI state)
- #24336 fix(web): keep background tasks visible during follow-up turns (same bar, different problem)
- #24385 fix(mobile): confirm before stopping background work from the floating capsule (mobile)
Investigated and drafted by Claude Code (claude-opus-5-5) on the reporter's machine, following .github/triage/PLAYBOOK.md.