You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Auto-settle on PR merge races the Claude wake turn after an awaited background task completes #26083
What happens
An agent runs a background Bash task (a merge wait), registers it with t3_background_wait, and ends its turn. The linked PR merges while the task runs. When the task exits, the thread is auto-settled before the Claude wake turn is registered, and settling closes the session, so the wake turn is lost. The agent never runs its post-merge steps.
Timeline from one real thread
.015 background task exits (exit 0); task list empties and triggers the settle check (ThreadSettlementService.ts:618)
.022 thread settled (auto-settle on merge; no active run yet)
Claude had started the wake turn (API request at .227); it never completed.
Why #24610 (fixing #17625) protects the thread while the awaited task runs. It does not cover the gap between task completion and the wake run attaching: ClaudeAdapterV2.ts:7632-7669 only registers the wake run once the reply streams, and its pending-work check (:10560) reads the already-empty task list.
Repro
Background sleep 120 + t3_background_wait on a thread whose linked PR has already merged, then end the turn. In a control run without a PR, the settle step fired at +8 ms after the task list emptied and the wake turn registered at +375 ms; with a merged PR the thread settles inside that gap.
Suggested fix
Keep the adapter's pending-work signal true from task completion until the wake run attaches, and have the settle check ask the live session (as stillReportsBackgroundWork already does). The ACP adapter already counts buffered continuations; doing it in the settle check covers every provider.
Another occurrence on 0.1.6 (Claude Code via the Agent SDK). This time the merged PR was a self-hosted GitLab MR.
The agent merged the MR, called t3_background_wait on a background Bash watcher, and ended its turn.
About 40 s later the watcher exited. The task_notification and the empty background_tasks_changed reached T3, and T3 sent query.close13 ms later. That was before the CLI's wake API request (~50 ms).
No run or notification item appeared. The user got no update for 2 hours, until they sent a message themselves.
Also, the "No response requested." that shows up in the Claude transcript after this is not the model ignoring the notification. On resume, the CLI writes it ("model": "<synthetic>") to close the dangling wake turn, so the model never saw the task finish.
#26108 matches the trace. It now conflicts with main, so it needs a rebase before it can land.
Another occurrence on 0.1.6 (Windows, desktop app, Claude Agent SDK, claude-opus-5-5).
The agent merged its PR (~20:48Z), started a ~5 min background test run, called t3_background_wait on it (matched), and ended its turn at 20:53:54Z.
20:59:13.496Z task_notification (completed) and an empty background_tasks_changed arrived.
20:59:13.514Z ThreadSettlementServiceV2.sweep dispatched thread.auto-settle; query.close was sent at .602Z, 106 ms after the notification. No wake run or item appeared.
The thread stayed silent until the user wrote ~10 min later. On resume the model saw the synthetic "No response requested." and wrongly told the user it had ended the turn itself.
sidebarAutoSettleOnMerge was at its default. +1 for landing #26108.
Prepared by Claude Opus 5.5 (claude-opus-5-5) via T3 Code triage.
Another occurrence on 0.1.7-nightly.20261101 (macOS, Claude Code 2.1.316 via the Agent SDK). The PR host was a self-hosted Forgejo, sidebarAutoSettleOnMerge was at its default, and after-days was off. I found 12 occurrences across 3 threads, all with merged PRs. About 270 idle wakes on other threads resumed fine.
task_notification (completed) and an empty background_tasks_changed arrived. About 100 ms later the CLI emitted system/status: requesting, its own wake turn.
7 ms after that: thread.auto-settle → ProviderSessionManager.detach → query.close.
One more detail: the detach queues a recovery continuation as a buffered provider wake (creationSource: "provider"). The follow-up run resumes the query but never sends prompt.offer, because Claude drains an empty wake buffer (the notification died with the closed query). The run finalizes about 10 ms later. So the fallback can't recover a wake that was cut off. Delivering that continuation as message_text when the session that buffered it is gone would let it recover, independent of fix(server): auto-settle on merge waits for a pending Claude wake turn #26108.
The thread stayed idle for 28 minutes (856 on another thread) until the user wrote. +1 for rebasing and landing #26108.
This comment was written by an AI model, Claude Opus 5.5 (claude-opus-5-5), running in Claude Code inside T3 Code, and posted with the user's approval.
Still reproduces on 0.1.7-nightly.20261102.4093 (macOS, Claude Agent SDK, claude-opus-5-5), and main at e68e19d60 has no change to this path.
Our trigger makes it deterministic rather than a race: agents merge their PR and then wait in the background for the deploy pipeline to promote the merge commit (15–40 min). The linked PR is therefore always merged when the awaited task exits, so every such wait is lost. Three threads, same shape each time:
system/task_notification (completed or failed) + empty background_tasks_changed, then outgoing query.close 12–45 ms later; no init, no wake run.
Earlier wakes in the same thread, before its PR merged, resumed normally (init → wake turn → result with origin.kind: "task-notification").
Side note: sidebarAutoSettleKeepPullRequests: true does not help here, since resolveAutoSettlementAt returns for a merged PR before it reads that setting. Disabling sidebarAutoSettleOnMerge is the only workaround we found.
Still reproduces on 0.1.8-nightly.20261104.4165 (macOS, Claude Code 2.1.316 and 2.1.318). We crossed the two conditions over 299 idle wake-ups from about two days of local logs:
Linked PR merged before the wake-up
No merged PR
t3_background_wait used
71 wake-ups, 23 missed
106, 0 missed
t3_background_wait not used
37, 0 missed
85, 0 missed
"Missed" means no model reply within 5 minutes of the task notification. In the misses we read, query.close follows the empty background_tasks_changed by 43 to 258 ms, as described above.
Two things this adds:
Every miss needed both conditions. A merged PR alone and t3_background_wait alone each lost nothing here.
Leaving out t3_background_wait is a second workaround, besides turning off sidebarAutoSettleOnMerge. The sample is small, and the thread no longer shows as working while it waits. The T3 system prompt tells Claude threads to call this tool, so the default instructions lead agents into this bug.
Measured and written by Claude (Opus 5.5) from local session and provider logs.
Measured data from two self-hosted machines, in case it helps prioritise #26108, #29820 or #31449. Versions were nightlies from 0.1.7-nightly.20261104.4151 to 0.1.8-nightly.20261105.4234, with Claude Code 2.1.318 through the Agent SDK. sidebarAutoSettleOnMerge was at its default.
I joined the provider protocol logs with thread.settled events. A "wake" below is a task_notification that arrived while the session was idle.
Wakes from 2026-10-31 to 2026-11-06
Count
query.close within 3 s
thread.settled within 1.31 s after the notification
13
13
No thread.settled nearby, session stayed open
310
0
No thread.settled nearby, session closed anyway
12
12
In the most recent 48 hours, 15 of 88 idle wakes were lost, or 17%. Auto-settle accounts for 13 of those 15.
The settle landed 10 ms to 1,310 ms after the notification, with a median of 28 ms.
Ten of the lost wakes were only noticed when the user asked what had happened. Those threads sat idle for 1 to 422 minutes, about 17 hours in total. Three others were never resumed.
Before each loss the agent had done everything right: it started the background command, t3_background_wait returned it as matched, and the turn ended normally.
Two details that may not be covered yet:
A close without a settle. The 12 closes in the last table row show the same shape, task_notification, then an empty background_tasks_changed, then query.close, but with no thread.settled event within 5 s. Ten predate the 48-hour window. I could not identify the caller, because the server traces rotate after about 90 minutes here. This suggests fixing only the settle check may leave a second path open. hasPendingBackgroundWork in ClaudeAdapterV2 still reads as false while the buffer holds only the task_notification, since it counts user, assistant and result frames.
A wake run that starts and dies. In 2 of the 15 cases T3 did create the run. The notification item appeared, query.open followed about 3 s later, query.close came within a second, and the run completed in 2 to 5 s with no assistant message or tool call. From the UI that looks like an agent that read the notification and decided to do nothing.
As a workaround I set sidebarAutoSettleOnMerge to false in settings.json on both machines today. The server picked it up without a restart. I'll report back here if wakes are still lost.
Another occurrence on 0.1.7-nightly.20261103.4119 (macOS, Claude Code 2.1.318 via the Agent SDK, claude-opus-5-5). sidebarAutoSettleOnMerge was at its default. Two wakes were lost in one thread, both on background commands awaited with t3_background_wait.
2026-11-05 21:53:58Z: task_notification (failed), linked PR merged 21:51:07Z, last user message 21:45:31Z. provider-session.detach ("Thread settled.") came 7 ms later. Idle for 2h34 until the user wrote.
2026-11-06 02:59:10Z: task_notification (completed), linked PR merged 02:33:39Z, last user message 02:01:01Z. Detach came 39 ms later. Idle for 6h02 until the user wrote.
The other 5 notifications in the session did not meet that condition, because no PR had merged since the last user message, and they all woke normally.
On resume the model again read the synthetic "No response requested." as its own reply and told the user it had failed, which hid the cause until we checked the raw transcript fields.
We set sidebarAutoSettleOnMerge to false as a workaround. +1 for landing #26108 or #29820.
Prepared by Claude Opus 5.5 (claude-opus-5-5) running in T3 Code, and posted with the user's approval.
Another occurrence on 0.1.7 (macOS, Claude Agent SDK, claude-opus-5-5), twice in one day, on a long-running orchestrator thread.
PRs linked to the thread merged, after the user's last message. The agent called t3_background_wait on a background Bash task and ended its turn.
07:49:32.632Z: task_notification and an empty background_tasks_changed. 07:49:32.651Z: query.close (19 ms).
11:20:17.916Z: the same pair. 11:20:17.955Z: query.close (39 ms). An earlier moment with one task still on the roster did not close.
The transcript shows the synthetic "No response requested." Each wake turn was lost.
The cost is larger for an orchestrator. This thread was directing other threads. Nothing but a new user message reopened it, so it stayed closed for 3 h 25 min and then 25 h 2 min, and the workers' finished reports went unread.
The settle event itself is gone from our rotated logs, so the link to the settle path is from the timing and the merged-PR condition. We have set sidebarAutoSettleOnMerge to false as the workaround. #26108 would be welcome.
I'm adding numbers from my setup. This has stopped being an occasional annoyance: it is now the most common way my agent work gets silently abandoned.
Setup: T3 Code 0.1.7 on a Linux server running as a service, Claude Agent (claude-opus-5-5), about 10 active projects. "Settle on merge" is at its default. In my workflow the agents merge their own PR and then do the post-merge steps: deploy, release tag, store upload, live check, final report.
What I measured (read-only pass over statev2.sqlite, Oct 16 – Nov 9):
369 PR merges made inside an agent run, across 280 threads.
42 of those runs started background work after the merge (deploy, CI watch, build, test run).
10 times I had to step in manually because the post-merge work had been cut off, across 9 different projects. Each time the thread settled when the agent's turn ended, the provider session was detached, and no wake run appeared. The next thing in the thread was me asking "what's the status?"
3 more threads were abandoned silently. The agent's last message promised a follow-up ("I'll verify 1.18.0 is live", "waiting for the release workflow to finish") that never happened, and nobody noticed at the time.
In 7 of the 10, the agent did exactly what T3 asks: it called t3_background_wait on the task before ending its turn. Once the linked PR has merged, the tool does not protect this path.
Those threads sat dead for anywhere from a few minutes to 4 hours before I noticed (two of them for about 2 h 10 m), roughly 9.7 hours in total.
A production deploy that died during the image build and left the site on the old version.
A TestFlight build that nobody checked.
A store resubmission waiting on a live check that never ran.
Final reports that were never written.
I wrote "finish everything and write the report before merging, because the merge settles the thread" into my agent instructions in five separate threads and in my shared agent notes. It didn't help. Some of the remaining work only makes sense after the merge (deploying the merge commit, pushing the release tag), and the agent has no way to keep its thread alive while it does that work.
Ask:#26108 looks like the right fix for the awaited case. Going by the data above, it would remove most of these incidents. Could it be prioritized?
Related variant (3 of my 10): the agent merges, starts the deploy with run_in_backgroundwithoutt3_background_wait, and ends its turn. Under #24610 unmarked commands never hold, so the thread settles about a second later, and the detach kills the deploy itself. Timeline from today:
06:29:29Z gh pr merge 90 --merge --delete-branch (linked PR)
06:30:03Z remote.sh deploy started with run_in_background
06:30:27Z agent ends its turn: "when the deploy finishes I'll check 7 pages and write the final report"
06:30:28Z thread.auto-settle -> provider-session.detach "Thread settled."
deploy log stops during the image build; the site stays on the old version
08:40Z I notice and ask what happened
I understand the unmarked rule is intentional because of dev servers, so I won't file this as a bug. Still, when a settle is triggered by a merge, background work started after mergedAt in the same run is almost always the post-merge work itself. If you're open to it, I can write this up as an Ideas discussion. One option is to hold merge-settle while a task started after the merge is still live. Another is to let agents opt their own thread out over MCP, as in #26110.
Workaround for anyone else hitting this: have the agent pin its thread (t3_thread_organize → pin) before merging, and call settle itself when the work is done. Pinned threads are skipped by isAutoSettlementCandidate, and settle applies at turn end and clears the pin. This keeps "Settle on merge" on for every thread that has nothing left to do after the merge.
Another reproduction on 0.1.8-nightly.20261105.4234 (desktop app, Linux x64, local server, Claude claude-opus-5-5). ThreadSettlementService.ts on main still has the same settle path, and #26108 / #29820 / #30837 / #31449 are unmerged.
Timeline (UTC). The thread has 4 linked GitLab MRs, all merged after the last user message:
18:10:57: the agent merges an MR, starts a background Bash loop polling a CI job, registers it with t3_background_wait (matched), and ends its turn at 18:11:03.
18:12:06.023: task_notification, the background command completed (exit 0).
18:12:06.024: background_tasks_changed, the task list is now empty.
18:12:06: thread.auto-settle is accepted, then ProviderSessionManager.detach ("Thread settled.").
18:12:06.053: query.close is sent to Claude. The turn reporting the task result never runs, and the agent only resumed when the user messaged about 70 minutes later.
Near miss in the same thread 11 minutes earlier: after another merge, an awaited task finished at 18:01:25 and the agent's next turn started the same second. The auto-settle sent at 18:01:26 was rejected by the snapshotAt check ("changed before automatic settlement"). So whether the thread settles depends only on whether the agent's turn starts before the settle check runs.
Written by Claude Opus 5.5 (Claude Code) via t3 triage.
Another occurrence on 0.1.7 (Windows x64, desktop app, Claude Code 2.1.321 through the Agent SDK, claude-opus-5-5). The times below are from the CLI's session transcript; we didn't have the server's log at hand.
20:44:37Z: the agent started a background Bash cleanup and called t3_background_wait on it (matched).
20:53:40Z: the agent merged the thread's linked PR, after a watch_pull_request wake for its checks.
20:57:08Z: the agent's last reply; it ended its turn with the cleanup still running.
21:10:54.927Z: the cleanup's task-notification (completed) was recorded.
21:10:58.282Z: the CLI sent the wake turn's API request. No assistant reply was recorded for it.
21:26:36.815Z: a synthetic "No response requested." ("model": "<synthetic>") was written, 3 s before the user's next message, which resumed the session 16 minutes after the wake was lost.
On resume the model read that synthetic message as its own reply, and told the user it had ignored the notification. We only found the cause in the transcript's model field.
Three earlier background waits in the same thread, before the PR merged (test runs of 1 to 10 minutes), and the watch_pull_request wake, all woke normally.
I tried to reproduce this on the current build and could not.
Commit 31a108c (origin/main). T3CODE_DEV_SEED=0 vp run dev (fresh DB, default settings, so auto-settle on merge is on). Created a scratch private GitLab project with one open MR, added the local clone as a project (t3 project add), then started real Claude threads with t3 thread start --provider claudeAgent --model <cheapest Claude model> and this prompt: (1) link_pull_request for the MR URL, (2) run glab mr reopen 1; sleep 3; glab mr close 1 in the foreground, (3) start sleep 40 && echo done-waiting with run_in_background, (4) t3_background_wait on that task, (5) end the turn and reply WOKE-AFTER-TASK when woken. Ran 6 threads (the first one used glab mr close only and sleep 90). In each, the MR snapshot was recorded as closed with closedAt after linkedAt and after the user message, so the auto-settle condition held: every thread was auto-settled (thread.settled, actor server:auto-settle) about 2.3 s after its wake turn finished. In all 6 the wake turn ran and replied WOKE-AFTER-TASK. Timings from the provider protocol log and server traces, measured from the empty background_tasks_changed: the CLI's wake init arrived at +30/+65/+75/+79/+88/+127 ms, the wake run was created about 5-20 ms after init, and the first ThreadSettlementServiceV2.sweep ran at +54/+91/+92/+107/+113/+190 ms, after the wake run had attached, so the settle check saw an active run. On this VM the settle sweep never landed inside the window (task_notification to wake init). Reporters' traces show it landing 7-45 ms after the notification, inside that window. The code gap is confirmed by reading: isAutoSettlementCandidate checks only the projected pendingBackgroundTasks (empty by then); thread.settle detaches every live session unconditionally (Orchestrator.ts ~4866); and ClaudeAdapterV2's hasPendingBackgroundWork returns false while the wake buffer holds only the task_notification, which the existing test 'buffers wake output and requests a single continuation run' asserts.
Closing as not planned. If it still happens for you, reply with the exact steps and the version you are on and it will be looked at again.
🤖 Claude Opus 5.5 responding on behalf of sc-frederick
Seeing this on 0.1.10-nightly.20261111.4544 (desktop app with its local server, Arch Linux x64, Claude Code 2.1.322 through the Agent SDK provider), and on an earlier nightly from 2026-11-10 and 2026-11-11.
Steps: link a PR to a Claude thread and merge it. Have the agent start a 2-minute command with run_in_background, call t3_background_wait on its task id, and end the turn.
On this machine 5 of 75 awaited background tasks never woke the agent, and each of those 5 threads had a merged PR. In each, after the CLI's task_notification and an empty background_tasks_changed, a provider-session.detach from server:auto-settle:<thread> was created 14 to 28 ms later, and query.close followed 21 to 44 ms after the notification. Claude's wake turn normally starts 24 to 52 ms after the notification, so it lost the race. settledAt was backdated to the last run's completion.
Latest case: task_notification at 04:26:54.013Z; provider thread stored with pendingBackgroundTasks: [] at .014; auto-settle detach at .027; query.close at .037.
That gap may be why repros failed: it is 10 to 30 ms wide, and on a slower machine the first settle check can land after the wake has attached. #34568 or #32223 may close it; I haven't tested a build with either.
Still reproduces on 0.1.9 (self-hosted Linux server, Claude Agent SDK, claude-opus-5-5, GitLab MRs, sidebarAutoSettleOnMerge at default). Twice today on one long-running lead thread, after the agent merged an MR, started a background Bash watcher for the target pipeline, registered it with t3_background_wait (matched), and ended its turn:
Background task exit (output written)
thread.auto-settle dispatched
Gap
16:17:48.521Z
16:17:48.539Z
18 ms
19:03:05.879Z
19:03:05.905Z
26 ms
No wake run either time; the thread stayed idle until the user wrote (2 h 24 min the first time). Timings are consistent with the 10–30 ms window described above, which may explain why it did not reproduce on a slower VM. Could this be reopened? #32223 looks like the right fix; happy to test a build with it.
Still reproduces on 0.1.9 (self-hosted Linux arm64 server, 4 cores, Claude Code 2.1.325 via the Agent SDK, claude-opus-5-5, sidebarAutoSettleOnMerge at default). From orchestration_events, the effect outbox and the agent's task output file:
Time (UTC, 2026-11-15)
What happened
04:25:19
Agent merges the thread's linked PR
04:25:58
Agent starts a bounded background Bash watcher for the merge's main run
04:26:00
t3_background_wait {"taskIds":["…"]} → matched
04:26:02
Turn ends
04:41:35.956
Watcher exits 0 (task output written)
04:41:35.983
thread.settled, command server:auto-settle:…, plus provider-session.detached "Thread settled."
04:54:24
Next run, started only because the user asked why nothing came back
Gap from task exit to auto-settle: 27 ms. No wake run was created.
About the repro that closed this: in it, the settle sweep ran 54–190 ms after the empty background_tasks_changed, after the wake run had attached. Here it ran inside that window. The race looks timing-dependent, so a fast dev VM may never hit it. Running the repro under CPU load, or adding an artificial delay before the wake run attaches, should make it show up reliably. #30837 and #31449 both still look needed; could this be reopened?
Drafted with Claude (Opus 5.5) in T3 Code from this install's event store.
🤖 Claude Opus 5.5 (Claude Code, following the t3 triage playbook) posting on behalf of the user
@juliusmarminge, flagging this since you closed it: the race only shows up on threads whose linked PR has already merged, which may be why it didn't reproduce for you (see the repro at the end).
Still reproduces on 0.1.9 (self-hosted Linux x64 server in a small k3s pod, used from the web app; Claude Code 2.1.323 through the Agent SDK; claude-opus-5-5; sidebarAutoSettleOnMerge at default). It happened twice today on the same long-running thread. Both times the agent merged the thread's linked PR, started a background gh run watch on the deploy, registered it with t3_background_wait (matched), and ended its turn.
Second occurrence (UTC 2026-11-15, from the provider event log and server.trace.ndjson):
Time
What happened
07:11:20
Agent merges the linked PR and triggers the deploy
cleanUpSettledThread, then ProviderSessionManager.detach / closeScopeWithin
07:12:02.083
query.close. No system init for the wake turn ever arrives
07:12:09
User sends "?" by hand. That is the only reason the thread continued
First occurrence: at 05:32:27.556 task_notification, then at .649 query.close, with the same server:auto-settle command. The thread then sat idle for 66 minutes until the user wrote.
On threads with no merged PR on this server, idle task notifications woke the agent every time. In those runs the SDK's system init/status: requesting for the wake turn came 70–80 ms after the empty background_tasks_changed. The settle here took about 50 ms, so it wins whenever the thread's linked PRs are all merged.
Why, from the v0.1.9 source:
ThreadSettlementService.tsprocessEvent: provider-thread.updated with an empty pendingBackgroundTasks calls enqueueThread right away.
isAutoSettlementCandidate checks activityRunStatus, backgroundWorkHoldsCompletion(pendingBackgroundTasks) and threadHasQueuedTurnStart. All three are clear in that gap: the run is finished, the roster is empty, and the queued-turn grace only covers user messages.
ClaudeAdapterV2 already tracks wakeEligibleBackgroundTasksByNativeThread, which outlives the empty background_tasks_changed level. Nothing exposes it to the settlement check, so pullRequestSettles (merge at or after the last user-authored message) settles the thread, and the settle detaches the session.
No commit on main since v0.1.9 that touches ThreadSettlementService.ts or orchestrationV2PendingBackgroundWork.ts seems to address this. #30837 (open, conflicting) looks like the fix. Could this be reopened? The duplicate #33041 was also closed as not reproducible, but it needs a merged PR on the thread to reproduce: background sleep 60 plus t3_background_wait on a thread whose linked PR has already merged, then end the turn.
Workaround for now: turn off "Auto-settle on merge".
Produced by Claude Opus 5.5 (claude-opus-5-5) in Claude Code, following the T3 Code triage playbook.
Activity
danddanddand commentedon Oct 31, 2026
Another occurrence on 0.1.6 (Claude Code via the Agent SDK). This time the merged PR was a self-hosted GitLab MR.
t3_background_waiton a background Bash watcher, and ended its turn.task_notificationand the emptybackground_tasks_changedreached T3, and T3 sentquery.close13 ms later. That was before the CLI's wake API request (~50 ms).Also, the "No response requested." that shows up in the Claude transcript after this is not the model ignoring the notification. On resume, the CLI writes it (
"model": "<synthetic>") to close the dangling wake turn, so the model never saw the task finish.#26108 matches the trace. It now conflicts with
main, so it needs a rebase before it can land.altintasutku commentedon Nov 1, 2026
Another occurrence on 0.1.6 (Windows, desktop app, Claude Agent SDK, claude-opus-5-5).
t3_background_waiton it (matched), and ended its turn at 20:53:54Z.task_notification(completed) and an emptybackground_tasks_changedarrived.ThreadSettlementServiceV2.sweepdispatchedthread.auto-settle;query.closewas sent at .602Z, 106 ms after the notification. No wake run or item appeared.sidebarAutoSettleOnMergewas at its default. +1 for landing #26108.Prepared by Claude Opus 5.5 (claude-opus-5-5) via T3 Code triage.
aport73 commentedon Nov 2, 2026
Another occurrence on 0.1.7-nightly.20261101 (macOS, Claude Code 2.1.316 via the Agent SDK). The PR host was a self-hosted Forgejo,
sidebarAutoSettleOnMergewas at its default, and after-days was off. I found 12 occurrences across 3 threads, all with merged PRs. About 270 idle wakes on other threads resumed fine.task_notification(completed) and an emptybackground_tasks_changedarrived. About 100 ms later the CLI emittedsystem/status: requesting, its own wake turn.thread.auto-settle→ProviderSessionManager.detach→query.close.creationSource: "provider"). The follow-up run resumes the query but never sendsprompt.offer, because Claude drains an empty wake buffer (the notification died with the closed query). The run finalizes about 10 ms later. So the fallback can't recover a wake that was cut off. Delivering that continuation asmessage_textwhen the session that buffered it is gone would let it recover, independent of fix(server): auto-settle on merge waits for a pending Claude wake turn #26108.The thread stayed idle for 28 minutes (856 on another thread) until the user wrote. +1 for rebasing and landing #26108.
This comment was written by an AI model, Claude Opus 5.5 (
claude-opus-5-5), running in Claude Code inside T3 Code, and posted with the user's approval.ethan-huo commentedon Nov 4, 2026
Still reproduces on 0.1.7-nightly.20261102.4093 (macOS, Claude Agent SDK, claude-opus-5-5), and
mainate68e19d60has no change to this path.Our trigger makes it deterministic rather than a race: agents merge their PR and then wait in the background for the deploy pipeline to promote the merge commit (15–40 min). The linked PR is therefore always merged when the awaited task exits, so every such wait is lost. Three threads, same shape each time:
system/task_notification(completed or failed) + emptybackground_tasks_changed, then outgoingquery.close12–45 ms later; noinit, no wake run.init→ wake turn →resultwithorigin.kind: "task-notification").Side note:
sidebarAutoSettleKeepPullRequests: truedoes not help here, sinceresolveAutoSettlementAtreturns for a merged PR before it reads that setting. DisablingsidebarAutoSettleOnMergeis the only workaround we found.enzotrp974 commentedon Nov 5, 2026
Still reproduces on 0.1.8-nightly.20261104.4165 (macOS, Claude Code 2.1.316 and 2.1.318). We crossed the two conditions over 299 idle wake-ups from about two days of local logs:
t3_background_waitusedt3_background_waitnot used"Missed" means no model reply within 5 minutes of the task notification. In the misses we read,
query.closefollows the emptybackground_tasks_changedby 43 to 258 ms, as described above.Two things this adds:
t3_background_waitalone each lost nothing here.t3_background_waitis a second workaround, besides turning offsidebarAutoSettleOnMerge. The sample is small, and the thread no longer shows as working while it waits. The T3 system prompt tells Claude threads to call this tool, so the default instructions lead agents into this bug.Measured and written by Claude (Opus 5.5) from local session and provider logs.
lukasbxy commentedon Nov 6, 2026
Measured data from two self-hosted machines, in case it helps prioritise #26108, #29820 or #31449. Versions were nightlies from
0.1.7-nightly.20261104.4151to0.1.8-nightly.20261105.4234, with Claude Code 2.1.318 through the Agent SDK.sidebarAutoSettleOnMergewas at its default.I joined the provider protocol logs with
thread.settledevents. A "wake" below is atask_notificationthat arrived while the session was idle.query.closewithin 3 sthread.settledwithin 1.31 s after the notificationthread.settlednearby, session stayed openthread.settlednearby, session closed anywayt3_background_waitreturned it asmatched, and the turn ended normally.Two details that may not be covered yet:
task_notification, then an emptybackground_tasks_changed, thenquery.close, but with nothread.settledevent within 5 s. Ten predate the 48-hour window. I could not identify the caller, because the server traces rotate after about 90 minutes here. This suggests fixing only the settle check may leave a second path open.hasPendingBackgroundWorkinClaudeAdapterV2still reads as false while the buffer holds only thetask_notification, since it countsuser,assistantandresultframes.query.openfollowed about 3 s later,query.closecame within a second, and the run completed in 2 to 5 s with no assistant message or tool call. From the UI that looks like an agent that read the notification and decided to do nothing.As a workaround I set
sidebarAutoSettleOnMergetofalseinsettings.jsonon both machines today. The server picked it up without a restart. I'll report back here if wakes are still lost.Prepared by Claude Opus 5.5 from local logs.
lucasbastianik commentedon Nov 6, 2026
Another occurrence on 0.1.7-nightly.20261103.4119 (macOS, Claude Code 2.1.318 via the Agent SDK, claude-opus-5-5).
sidebarAutoSettleOnMergewas at its default. Two wakes were lost in one thread, both on background commands awaited witht3_background_wait.task_notification(failed), linked PR merged 21:51:07Z, last user message 21:45:31Z.provider-session.detach("Thread settled.") came 7 ms later. Idle for 2h34 until the user wrote.task_notification(completed), linked PR merged 02:33:39Z, last user message 02:01:01Z. Detach came 39 ms later. Idle for 6h02 until the user wrote.We set
sidebarAutoSettleOnMergetofalseas a workaround. +1 for landing #26108 or #29820.Prepared by Claude Opus 5.5 (
claude-opus-5-5) running in T3 Code, and posted with the user's approval.beausee commentedon Nov 8, 2026
Another occurrence on 0.1.7 (macOS, Claude Agent SDK, claude-opus-5-5), twice in one day, on a long-running orchestrator thread.
t3_background_waiton a background Bash task and ended its turn.task_notificationand an emptybackground_tasks_changed. 07:49:32.651Z:query.close(19 ms).query.close(39 ms). An earlier moment with one task still on the roster did not close.The cost is larger for an orchestrator. This thread was directing other threads. Nothing but a new user message reopened it, so it stayed closed for 3 h 25 min and then 25 h 2 min, and the workers' finished reports went unread.
The settle event itself is gone from our rotated logs, so the link to the settle path is from the timing and the merged-PR condition. We have set
sidebarAutoSettleOnMergeto false as the workaround. #26108 would be welcome.mahirozdin commentedon Nov 9, 2026
I'm adding numbers from my setup. This has stopped being an occasional annoyance: it is now the most common way my agent work gets silently abandoned.
Setup: T3 Code 0.1.7 on a Linux server running as a service, Claude Agent (claude-opus-5-5), about 10 active projects. "Settle on merge" is at its default. In my workflow the agents merge their own PR and then do the post-merge steps: deploy, release tag, store upload, live check, final report.
What I measured (read-only pass over
statev2.sqlite, Oct 16 – Nov 9):t3_background_waiton the task before ending its turn. Once the linked PR has merged, the tool does not protect this path.What it costs in practice:
I wrote "finish everything and write the report before merging, because the merge settles the thread" into my agent instructions in five separate threads and in my shared agent notes. It didn't help. Some of the remaining work only makes sense after the merge (deploying the merge commit, pushing the release tag), and the agent has no way to keep its thread alive while it does that work.
Ask: #26108 looks like the right fix for the awaited case. Going by the data above, it would remove most of these incidents. Could it be prioritized?
Related variant (3 of my 10): the agent merges, starts the deploy with
run_in_backgroundwithoutt3_background_wait, and ends its turn. Under #24610 unmarked commands never hold, so the thread settles about a second later, and the detach kills the deploy itself. Timeline from today:I understand the unmarked rule is intentional because of dev servers, so I won't file this as a bug. Still, when a settle is triggered by a merge, background work started after
mergedAtin the same run is almost always the post-merge work itself. If you're open to it, I can write this up as an Ideas discussion. One option is to hold merge-settle while a task started after the merge is still live. Another is to let agents opt their own thread out over MCP, as in #26110.Workaround for anyone else hitting this: have the agent pin its thread (
t3_thread_organize→pin) before merging, and callsettleitself when the work is done. Pinned threads are skipped byisAutoSettlementCandidate, andsettleapplies at turn end and clears the pin. This keeps "Settle on merge" on for every thread that has nothing left to do after the merge.spereirag commentedon Nov 9, 2026
Another reproduction on 0.1.8-nightly.20261105.4234 (desktop app, Linux x64, local server, Claude claude-opus-5-5).
ThreadSettlementService.tsonmainstill has the same settle path, and #26108 / #29820 / #30837 / #31449 are unmerged.Timeline (UTC). The thread has 4 linked GitLab MRs, all merged after the last user message:
t3_background_wait(matched), and ends its turn at 18:11:03.task_notification, the background command completed (exit 0).background_tasks_changed, the task list is now empty.thread.auto-settleis accepted, thenProviderSessionManager.detach("Thread settled.").query.closeis sent to Claude. The turn reporting the task result never runs, and the agent only resumed when the user messaged about 70 minutes later.Near miss in the same thread 11 minutes earlier: after another merge, an awaited task finished at 18:01:25 and the agent's next turn started the same second. The auto-settle sent at 18:01:26 was rejected by the
snapshotAtcheck ("changed before automatic settlement"). So whether the thread settles depends only on whether the agent's turn starts before the settle check runs.Written by Claude Opus 5.5 (Claude Code) via
t3 triage.MARCROCK22 commentedon Nov 9, 2026
Another occurrence on 0.1.7 (Windows x64, desktop app, Claude Code 2.1.321 through the Agent SDK, claude-opus-5-5). The times below are from the CLI's session transcript; we didn't have the server's log at hand.
t3_background_waiton it (matched).watch_pull_requestwake for its checks.task-notification(completed) was recorded."model": "<synthetic>") was written, 3 s before the user's next message, which resumed the session 16 minutes after the wake was lost.On resume the model read that synthetic message as its own reply, and told the user it had ignored the notification. We only found the cause in the transcript's
modelfield.Three earlier background waits in the same thread, before the PR merged (test runs of 1 to 10 minutes), and the
watch_pull_requestwake, all woke normally.juliusmarminge commentedon Nov 12, 2026
I tried to reproduce this on the current build and could not.
Commit 31a108c (origin/main).
T3CODE_DEV_SEED=0 vp run dev(fresh DB, default settings, so auto-settle on merge is on). Created a scratch private GitLab project with one open MR, added the local clone as a project (t3 project add), then started real Claude threads witht3 thread start --provider claudeAgent --model <cheapest Claude model>and this prompt: (1) link_pull_request for the MR URL, (2) runglab mr reopen 1; sleep 3; glab mr close 1in the foreground, (3) startsleep 40 && echo done-waitingwith run_in_background, (4) t3_background_wait on that task, (5) end the turn and reply WOKE-AFTER-TASK when woken. Ran 6 threads (the first one usedglab mr closeonly and sleep 90). In each, the MR snapshot was recorded as closed with closedAt after linkedAt and after the user message, so the auto-settle condition held: every thread was auto-settled (thread.settled, actor server:auto-settle) about 2.3 s after its wake turn finished. In all 6 the wake turn ran and replied WOKE-AFTER-TASK. Timings from the provider protocol log and server traces, measured from the empty background_tasks_changed: the CLI's wakeinitarrived at +30/+65/+75/+79/+88/+127 ms, the wake run was created about 5-20 ms after init, and the first ThreadSettlementServiceV2.sweep ran at +54/+91/+92/+107/+113/+190 ms, after the wake run had attached, so the settle check saw an active run. On this VM the settle sweep never landed inside the window (task_notification to wake init). Reporters' traces show it landing 7-45 ms after the notification, inside that window. The code gap is confirmed by reading: isAutoSettlementCandidate checks only the projected pendingBackgroundTasks (empty by then); thread.settle detaches every live session unconditionally (Orchestrator.ts ~4866); and ClaudeAdapterV2's hasPendingBackgroundWork returns false while the wake buffer holds only the task_notification, which the existing test 'buffers wake output and requests a single continuation run' asserts.Closing as not planned. If it still happens for you, reply with the exact steps and the version you are on and it will be looked at again.
sc-frederick commentedon Nov 12, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of sc-frederick
Seeing this on 0.1.10-nightly.20261111.4544 (desktop app with its local server, Arch Linux x64, Claude Code 2.1.322 through the Agent SDK provider), and on an earlier nightly from 2026-11-10 and 2026-11-11.
Steps: link a PR to a Claude thread and merge it. Have the agent start a 2-minute command with
run_in_background, callt3_background_waiton its task id, and end the turn.On this machine 5 of 75 awaited background tasks never woke the agent, and each of those 5 threads had a merged PR. In each, after the CLI's
task_notificationand an emptybackground_tasks_changed, aprovider-session.detachfromserver:auto-settle:<thread>was created 14 to 28 ms later, andquery.closefollowed 21 to 44 ms after the notification. Claude's wake turn normally starts 24 to 52 ms after the notification, so it lost the race.settledAtwas backdated to the last run's completion.Latest case:
task_notificationat 04:26:54.013Z; provider thread stored withpendingBackgroundTasks: []at .014; auto-settle detach at .027;query.closeat .037.That gap may be why repros failed: it is 10 to 30 ms wide, and on a slower machine the first settle check can land after the wake has attached. #34568 or #32223 may close it; I haven't tested a build with either.
dev-valshin commentedon Nov 12, 2026
Still reproduces on 0.1.9 (self-hosted Linux server, Claude Agent SDK, claude-opus-5-5, GitLab MRs,
sidebarAutoSettleOnMergeat default). Twice today on one long-running lead thread, after the agent merged an MR, started a background Bash watcher for the target pipeline, registered it witht3_background_wait(matched), and ended its turn:thread.auto-settledispatchedNo wake run either time; the thread stayed idle until the user wrote (2 h 24 min the first time). Timings are consistent with the 10–30 ms window described above, which may explain why it did not reproduce on a slower VM. Could this be reopened? #32223 looks like the right fix; happy to test a build with it.
dilbagh commentedon Nov 15, 2026
Note
Claude Opus 5.5 posting on behalf of @dilbagh
Still reproduces on 0.1.9 (self-hosted Linux arm64 server, 4 cores, Claude Code 2.1.325 via the Agent SDK, claude-opus-5-5,
sidebarAutoSettleOnMergeat default). Fromorchestration_events, the effect outbox and the agent's task output file:mainrunt3_background_wait {"taskIds":["…"]}→matchedthread.settled, commandserver:auto-settle:…, plusprovider-session.detached"Thread settled."Gap from task exit to auto-settle: 27 ms. No wake run was created.
About the repro that closed this: in it, the settle sweep ran 54–190 ms after the empty
background_tasks_changed, after the wake run had attached. Here it ran inside that window. The race looks timing-dependent, so a fast dev VM may never hit it. Running the repro under CPU load, or adding an artificial delay before the wake run attaches, should make it show up reliably. #30837 and #31449 both still look needed; could this be reopened?Drafted with Claude (Opus 5.5) in T3 Code from this install's event store.
sweepies commentedon Nov 15, 2026
Note
🤖 Claude Opus 5.5 (Claude Code, following the
t3 triageplaybook) posting on behalf of the user@juliusmarminge, flagging this since you closed it: the race only shows up on threads whose linked PR has already merged, which may be why it didn't reproduce for you (see the repro at the end).
Still reproduces on 0.1.9 (self-hosted Linux x64 server in a small k3s pod, used from the web app; Claude Code 2.1.323 through the Agent SDK; claude-opus-5-5;
sidebarAutoSettleOnMergeat default). It happened twice today on the same long-running thread. Both times the agent merged the thread's linked PR, started a backgroundgh run watchon the deploy, registered it witht3_background_wait(matched), and ended its turn.Second occurrence (UTC 2026-11-15, from the provider event log and
server.trace.ndjson):gh run watchstarts (local_bash)t3_background_wait→{"matched":["<task>"],"unmatched":[]}result success,end_turn)task_updatedend_time)task_notificationcompleted, thenbackground_tasks_changed tasks=[]ThreadSettlementServiceV2.sweepstartsthread.auto-settledispatched (server:auto-settle:<thread>:…)cleanUpSettledThread, thenProviderSessionManager.detach/closeScopeWithinquery.close. Nosystem initfor the wake turn ever arrivesFirst occurrence: at 05:32:27.556
task_notification, then at .649query.close, with the sameserver:auto-settlecommand. The thread then sat idle for 66 minutes until the user wrote.On threads with no merged PR on this server, idle task notifications woke the agent every time. In those runs the SDK's
system init/status: requestingfor the wake turn came 70–80 ms after the emptybackground_tasks_changed. The settle here took about 50 ms, so it wins whenever the thread's linked PRs are all merged.Why, from the v0.1.9 source:
ThreadSettlementService.tsprocessEvent:provider-thread.updatedwith an emptypendingBackgroundTaskscallsenqueueThreadright away.isAutoSettlementCandidatechecksactivityRunStatus,backgroundWorkHoldsCompletion(pendingBackgroundTasks)andthreadHasQueuedTurnStart. All three are clear in that gap: the run is finished, the roster is empty, and the queued-turn grace only covers user messages.ClaudeAdapterV2already trackswakeEligibleBackgroundTasksByNativeThread, which outlives the emptybackground_tasks_changedlevel. Nothing exposes it to the settlement check, sopullRequestSettles(merge at or after the last user-authored message) settles the thread, and the settle detaches the session.No commit on
mainsince v0.1.9 that touchesThreadSettlementService.tsororchestrationV2PendingBackgroundWork.tsseems to address this. #30837 (open, conflicting) looks like the fix. Could this be reopened? The duplicate #33041 was also closed as not reproducible, but it needs a merged PR on the thread to reproduce: backgroundsleep 60plust3_background_waiton a thread whose linked PR has already merged, then end the turn.Workaround for now: turn off "Auto-settle on merge".
Produced by Claude Opus 5.5 (
claude-opus-5-5) in Claude Code, following the T3 Code triage playbook.