Skip to content

Auto-settle stops the agent when the background task it awaited finishes #30790

Open

Description

@relferreira

What happened

An agent (Claude, claude-opus-5-5) merged the thread's linked PR, started a background Bash command to poll the deploy's check runs, registered it with t3_background_wait, and ended its turn saying it would continue when the build finished. When the command exited, the thread auto-settled 7 ms later. Settling detached the provider session, so the agent never received the task-completion notification and never resumed. The user had to send a message to get it going again.

Diagnosis

The thread was eligible for auto-settle on merge (sidebarAutoSettleOnMerge, default true) because the PR merged after the latest user message. isAutoSettlementCandidate correctly held it while the awaited task was pending (backgroundWorkHoldsCompletion). When the task finished, provider-thread.updated emptied pendingBackgroundTasks, ThreadSettlementService.processEvent enqueued a sweep, and thread.auto-settle was sent at once. Settling detaches live provider sessions (Orchestrator.ts, "Archive and settle stop idle background work"), which kills the follow-up turn the provider was about to start for the task notification. threadHasQueuedTurnStart only gives a grace period to user messages, not to provider-started follow-up turns. The same race should apply to inactivity-based auto-settle.

Possible fixes: give a short grace period after awaited background work ends (like QUEUED_TURN_START_GRACE_MS) before a thread can auto-settle, or skip auto-settle until the provider has handled the task's completion notification.

Steps to reproduce

  1. Keep "Auto-settle on merge" enabled (the default).
  2. In a Claude thread, have the agent open a PR and link it to the thread.
  3. Ask the agent to merge the PR and then wait for CI/deploy, e.g. run_in_background a loop that polls check runs for ~1-2 minutes, then call t3_background_wait on that task and end the turn.
  4. When the background command exits, the thread auto-settles immediately and the agent never resumes.

Version

0.1.7-nightly.20261103.4138 (desktop app, local server, macOS arm64). Commit 115739c.

Environment

macOS 27.0 arm64, Node 24.22.1, T3 Code Desktop against its local server, provider claudeAgent (claude-opus-5-5).

Evidence

- Turn ended 20:51:19.05Z right after `t3_background_wait` returned `{"matched":["bs00argh7"],"unmatched":[]}`.
- PR snapshot: `state: merged`, `mergedAt: 20:51:03Z`; latest user message 20:49:16Z.
- Server trace: `orchestrationV2.EventSink.writeIfProviderThreadOwner` at 20:53:01.391Z, then `orchestrationV2.dispatch.once` `thread.auto-settle` (command id `server:auto-settle:<thread>:...`) at 20:53:01.398Z.
- Next activity was the user's message at 20:56:19Z, which unsettled the thread and started a new provider session.

Related issues

#11788 (snoozed threads auto-settled), #11711 (pinned threads auto-settling). Neither covers this.

Fix applied or workaround

Turn off "Auto-settle on merge" in Settings, or pin threads that are waiting on post-merge work.

Filed by

Triaged and written by Claude Opus 5.5 (claude-opus-5-5) via Claude Code inside T3 Code

Activity

  1. addisonk commented on Nov 5, 2026

    @addisonk

    Another occurrence today, on T3 Code 0.1.6 (macOS desktop, Claude provider, claude-opus-5-5, full-access thread launched by another thread with t3_thread_launch). Same outcome: the agent never resumed and the user had to send a message. The timing differs from the report above, though: the thread settled when the turn ended, while the awaited task was still running, not when the task exited.

    Timeline (UTC, from t3_thread_read):

    • 23:02:43: the thread's linked PR merges.
    • 23:08:06: the agent starts a background Bash deploy (task bk3zmhrho).
    • 23:08:07: t3_background_wait with that ID returns {"matched":["bk3zmhrho"],"unmatched":[]}.
    • 23:08:13.153: the agent's last message: "deploy is running in the background now … when the deploy finishes I'll confirm".
    • 23:08:13.190: the run completes, and the thread's settledAt is the same timestamp.
    • 23:09:47: the deploy finishes. The thread's updatedAt moves to 23:09:47.820, but no run starts.

    So the awaited task held the thread for under a second: settlement fired at turn completion, about 94 seconds before the task ended. The thread was merge-settle eligible, since the PR merged after its latest user message. This may be the merge-sweep path in #29899, or the awaited task not yet counting as pending when the turn closed. I haven't read the code to tell which.

    A t3_thread_send (mode auto) from another thread restarted it.

    Written by Claude Opus 5.5 in Claude Code, run from T3 Code.

  2. mahirozdin commented on Nov 9, 2026

    @mahirozdin

    Same failure here on 0.1.7 (Linux server, Claude Agent). I posted numbers from about three weeks of use in #26083 (#26083 (comment)): 10 manual interventions after merge-then-deploy flows, and in 7 of them t3_background_wait had already been called.

  3. jelsco commented on Nov 10, 2026

    @jelsco

    Four more occurrences of the turn-end variant described in the comment above, on 0.1.7 (Windows x64 desktop app, Claude provider, claude-opus-5-5). In each one the wait matched, and the thread settled at the exact moment the run completed, while the awaited task was still running. Settling detached the session, so the task's completion never woke the agent.

    Both threads were merge-settle eligible: each had a linked PR that merged after the latest user-authored message (thread A's PR at 2026-11-09 00:16:36Z, thread B's at 2026-11-09 15:33:52Z).

    Times are UTC, read from the thread's turn items and settledAt:

    Thread t3_background_wait result Run completed settledAt
    A 2026-11-09 19:31:27.964, matched: ["b3nc8i6z9"] 19:31:33.094 19:31:33.094
    A 2026-11-09 22:06:44.551, matched: ["brh008qal"] 22:06:49.448 22:06:49.448
    B 2026-11-09 15:55:04.318, matched: ["bz5c28s40"] 15:55:17.261 15:55:17.261
    B 2026-11-10 00:56:31.615, matched: ["bxlmvd07e"] 00:56:38.417 00:56:38.417

    The tasks were a pre-push check run of roughly 10 to 15 minutes, so each was still running when the thread settled. Each thread sat idle for 2 to 9 hours until a message from another thread restarted it.

    As far as I can tell, the open fixes (#26108, #29820, #30837) cover the gap between the awaited task finishing and the wake turn attaching. #31449 covers explicit settles. None of them seems to cover this case: here isAutoSettlementCandidate lets the merge sweep settle at run completion, as if pendingBackgroundTasks were empty at that moment even though the wait had matched seconds earlier. I haven't traced why the pending list is empty at that point.

    What we're running locally in the meantime, in case it helps anyone: in resolveAutoSettlementAt, the merge/close path returns null until the thread has been quiet for a window. Quiet means no run activity and no sweep that saw backgroundWorkHoldsCompletion true. That covers both variants. It is a behaviour change (merged threads linger for up to that window), so I'm not proposing it as the fix.

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

    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