Repository navigation
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
Deterministic with the adapter's wake test harness. The test below fails on main at ca140c435f:
- A Claude user turn ends with its
result. No T3 turn is active. - Claude opens a native turn on its own (a wake) and emits a root
system/init.bufferWakeMessagebuffers theinitand offers a continuation, so T3 queues a continuation run. - A user message that was queued earlier (here, a PR watch notification) starts its run before the continuation does and sends its prompt.
- Claude folds that prompt into the native turn it already opened. One native turn and one
resultserve both. The user run takes theresultand completes. - The continuation run starts. It replays the stale buffered
init, marks the wake as started, and waits for aresultthat already went to the user run.
Failing test (drop into the "ClaudeAdapterV2 background wake turns" describe in ClaudeAdapterV2.test.ts)
it.effect("settles a wake run whose native turn a user turn already answered", () =>
Effect.scoped(
Effect.gen(function* () {
const harness = yield* makeWakeHarness;
const now = yield* DateTime.now;
const startTurn = (ordinal: number, text: string, wake = false) =>
harness.runtime.startTurn(
makeClaudeTestTurnInput({
threadId: harness.threadId,
providerThread: harness.providerThread,
now,
attemptId: RunAttemptId.make(`attempt-claude-wake-answered-${ordinal}`),
text,
attachments: [],
providerTurnOrdinal: ordinal,
...(wake
? { messageCreatedBy: "agent" as const, messageCreationSource: "provider" as const }
: {}),
}),
);
const lifecycle = (promptIndex: number, state: string, uuid: string) =>
claudeSdkFrame({
type: "command_lifecycle",
command_uuid: harness.promptUuid(promptIndex),
state,
uuid,
session_id: WAKE_NATIVE_SESSION,
});
yield* startTurn(1, "Open the PR.");
yield* harness.offerAndWait(
claudeSdkFrame({
...makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000a01", result: "Opened." }),
user_message_uuid: harness.promptUuid(0),
}),
);
assert.equal((yield* Queue.take(harness.terminalReceipts)).status, "completed");
// Claude opens a native wake turn on its own while T3 is idle.
yield* harness.offerAndWait(wakeTurnInit);
yield* Queue.take(harness.continuationReceipts);
// A queued user message runs before the continuation. Claude folds it
// into the wake turn it already opened and ends both with one result.
yield* startTurn(2, "A check failed on the PR.");
for (const frame of [
lifecycle(1, "queued", "00000000-0000-4000-8000-000000000a02"),
lifecycle(1, "started", "00000000-0000-4000-8000-000000000a03"),
makeAssistantTextFrame({
uuid: "00000000-0000-4000-8000-000000000a04",
text: "Fixed the check.",
}),
lifecycle(1, "completed", "00000000-0000-4000-8000-000000000a05"),
claudeSdkFrame({
...makeResultFrame({
uuid: "00000000-0000-4000-8000-000000000a06",
result: "Fixed the check.",
}),
user_message_uuid: harness.promptUuid(1),
}),
]) {
yield* harness.offerAndWait(frame);
}
assert.equal((yield* Queue.take(harness.terminalReceipts)).status, "completed");
// The continuation has nothing left to answer, and Claude is idle.
yield* startTurn(3, "Background task completed.", true);
yield* TestClock.adjust("2 minutes");
assert.lengthOf(harness.terminalEvents(), 3);
assert.equal((yield* Queue.take(harness.terminalReceipts)).status, "completed");
assert.lengthOf(harness.offeredMessages, 2);
}).pipe(
Effect.provide(Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer)),
),
),
);It fails with expected [ …(2) ] to have a length of 3 but got 2: the continuation never reaches a terminal state, even after the 2-minute deadline.
Expected behavior
The continuation run settles as completed. The user run already answered the native turn it was opened for, and Claude is idle.
Actual behavior
The continuation run stays running forever, and every run queued behind it waits. In my case that was 5 hours: a review result, two delegated-task completions and two PR watch updates sat queued while the thread showed Working and the Claude CLI sat idle. It only moved when another thread sent a message, which T3 delivered into the stuck run.
The cause is in ClaudeAdapterV2.ts. The user-turn path in startTurn leaves the buffered root init and the requestedContinuations entry in place. When the continuation drains the buffer, replaying that init through handleSdkMessage sets context.nativeWakeStarted = true. No buffered result follows, so nothing finalizes. The #19196 deadline only fires while nativeWakeStarted is false, so it never fires here.
Pi, Muse and OpenCode hit the same race and handle it. When a user turn starts while a wake is held, the user turn takes the wake over and the later continuation finishes at once (OpenCode takeRunningWake, Pi's adoptedWake, Muse's if (continuation && !held) return finish(...)). I checked the other adapters: Codex, Grok, ACP-registry and Antigravity can't hit it, and Cursor doesn't request continuations.
Related, but different: #22033 has the same root weakness (a user turn leaves an old buffered start in the wake buffer), but there the symptom is a subagent stuck Running. #20430 is a continuation run that never starts because it's held in the queue. Here the run starts and never ends. #22049 and #20318 don't fix this; the test above still fails with each applied.
Impact
Blocks work completely
Version or commit
0.1.1-nightly.20261020.3411; still fails on main @ ca140c4
Environment
Linux (WSL2), T3 server as a background service, Claude Code CLI 2.1.304, claude-opus-5-5, full access
Logs or stack traces
# provider event log, one Claude CLI process (UTC)
13:56:02.563 incoming result success # run 9 ends
13:56:02.616 incoming command_lifecycle started 0b35bb54 # no T3 prompt: Claude starts a wake
13:56:02.636 incoming system/init # adapter buffers it, logs claude-wake-turn-detected, run 11 queued
13:56:03.671 outgoing prompt.offer # run 10 (queued earlier) sends its prompt
13:56:03.673 incoming command_lifecycle queued 511fd19c
13:56:07.090 incoming command_lifecycle started 511fd19c
14:00:36.918 incoming command_lifecycle completed 511fd19c
14:00:36.921 incoming result success # one result for both; run 10 completes
14:00:36.929 incoming command_lifecycle completed 0b35bb54
(no events for 5 hours; run 11 started 14:00:37.707 and stays running)Workaround
Send any message to the stuck thread. It's delivered into the stuck run, Claude starts a turn, and its result finally ends the run. I didn't try Stop.
Activity
juliusmarminge commentedon Oct 20, 2026
Note
Grok responding on behalf of Julius.
Thanks for the failing test, it maps cleanly onto current main. When the continuation run starts, it replays every buffered frame except
resultthroughhandleSdkMessage(apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts:9520-9545). A replayed rootinitsetsnativeWakeStarted(lines 8559-8566). The two-minute fallback that settles a wake Claude never opens only runs whilenativeWakeStartedis false (lines 9546-9575). So when a queued user run has already taken theresultfor the turn Claude opened, the staleinitmakes the continuation look started, and nothing is left to end it.This isn't covered by the existing reports. #20430 needs a held queued run blocking the continuation gate, #17099 is a wake continuing on another provider, and #17809 is about subagent rows after a completion wake. #21239 drops obsolete pull request watch notifications before they start, which would avoid this particular trigger only when the watch is stale, not the race itself.
Accepting this as a bug.
fix(server): a Claude wake run settles when a queued message already …
fix(server): a Claude wake run settles when a queued message already …
kkolodziej7 commentedon Oct 23, 2026
Confirming this on the released desktop builds 0.1.1 and 0.1.2 (macOS,
claudeAgent, Claude Code CLI 2.1.308 and 2.1.309,claude-opus-5-5). It hit one long-lived thread three times in about 13 hours on Oct 22–23. Each time the sequence matched this report:createdBy=agent,creationSource=provider).queue_after_active) starts first. The native turn ends with one successfulresult, and the user run takes it and completes. In the case we inspected, thatresulthad task-notification origin,num_turns=7andqueued_turn_count=0.running, and everything queued behind it waits.resulttaken by the user run (UTC)During the waits nothing else was pending: no runtime request, open tool, hook or compaction, and the CLI had no open TCP connection. The two-minute wake deadline did not fire in any of the three cases, which fits the replayed
initsettingnativeWakeStarted. To recover, the stuck run had to be interrupted and the 7 messages queued behind it cancelled by hand, then the thread restarted.0.1.2 still has this. Could #22513 make the next release?
fix(server): a Claude wake run settles when a queued message already …
BWBama85 commentedon Oct 29, 2026
Possible related occurrence on T3 Code desktop 0.1.5 (
6d450a4284852af137b2f3407fddd89eb76e7644), macOS / Apple Silicon, Claude Code 2.1.312,claudeAgent,claude-opus-5-5.The user asked Claude to pause. Claude acknowledged the pause, its Stop hooks finished successfully, and the CLI emitted
result/success, but the thread continued showing Thinking with an increasing working timer. This is a possible match for this issue, not an independently reproduced confirmation of the exact race.Timeline on 2026-10-29, all times UTC:
prompt.offerfor the pause messagecommand_lifecycle: queued, commandfddef959-f78b-4d8c-919c-719dbe14af5bstartedstop_reason: end_turncompletedRedacted/selected fields from the final native result:
{ "type": "result", "subtype": "success", "is_error": false, "num_turns": 3, "stop_reason": "end_turn", "terminal_reason": "completed", "user_message_uuid": "fddef959-f78b-4d8c-919c-719dbe14af5b", "user_message_uuids": ["fddef959-f78b-4d8c-919c-719dbe14af5b"], "origin": { "kind": "task-notification", "producer": "session-task" }, "queued_turn_count": 0 }A successful read-only database query during the investigation found:
There were no later native provider events at inspection time. The approximately three-minute delay after the assistant reply is explained by Stop quality gates; the stale running state after successful completion is the problem.
The installed ClaudeAdapterV2.ts still replays non-result wake frames through
handleSdkMessage, and only schedules the wake fallback whilenativeWakeStartedis false. The root-frame handler can set that flag during replay, consistent with the mechanism described here.Limit: We did not establish ordinal 6's creation reason from its payload or reproduce the sequence in the adapter test harness. The mixed task-notification origin and matching user-prompt UUID make this a candidate for this issue, but do not prove identical causality. No claim is made that Stop or restart was tested as a recovery.
Cross-linking #15197 for the completed-provider/stuck-running symptom. Does the proposed fix in #22513 also cover this case, where a task-notification-origin result explicitly acknowledges the user's pause prompt?
achat-z commentedon Oct 31, 2026
Confirming on stable 0.1.6 (desktop, Windows 10 x64,
claudeAgent,claude-opus-5-5), with a trigger variant: the wake comes from a background native subagent (Agentwithrun_in_background, relaunched withSendMessage), not a background shell command. One long-lived, busy thread (many queued messages) hit it twice in one evening. Each time the T3 run stayed Working with no items and no output, everything queued behind it waited, andt3_thread_interruptcleared it.Incident 1 (2026-10-31, UTC, from the provider event log)
SendMessage(task_started)task_notificationstatus=completed,background_tasks_changed→ []resultsuccess / end_turn (ends the user turn)system/init: Claude opens a wake turn by itselfprompt.offer,command_lifecyclequeuedstarted: Claude folds it into the wake turn it just openedcompleted+ one rootresult, taken by the user runquery.interrupt/query.close(thread interrupt), then the queue drains normallyThis is the exact sequence in the issue body: root
initbuffered → queued user prompt absorbed into that native turn → its singleresultgoes to the user run → the continuation replays the staleinitand waits forever.Incident 2 (same thread, UTC)
result(tool_use), rootinit, rootresultend_turn at 20:41:31task_notificationcompleted for the background subagent while that user turn is open;background_tasks_changed→ []resultfor that native turn, taken by the user runquery.interrupt/query.close, then normalHere the notification landed inside a user turn rather than right before one. The end state is the same: a provider continuation with nothing left to answer it. I did not pin the exact start time of the stuck run from the server trace.
In neither case did the two-minute
claude-wake-turn-never-openedfallback fire. Checked against thev0.1.6source:ClaudeAdapterV2.tsstill setsnativeWakeStartedfrom a replayed root frame (around line 9481), and the fallback is gated on!context.nativeWakeStarted(around lines 10650–10662). Both are unchanged from the analysis above. Neither #22513 nor #28010 is inv0.1.6orv0.1.7-nightly.20261031.3991, so this still reproduces on current releases.Workaround in use: interrupt the thread; avoid background native subagents on busy threads.
Triage by Claude Opus 5.5 (Claude Code, inside T3 Code), from the user's local provider event log.
TylerJNewman commentedon Nov 2, 2026
Note
Written by Claude Opus 5.5 in Claude Code on behalf of Tyler.
Three more occurrences on T3 Code 0.1.5, all
claudeAgent, on 2026-11-02: two macOS machines and one Linux server. The continuation runs were stuck for 31 minutes, 65 minutes and 15.9 hours, each with queued messages behind it, until someone pressed Stop. Each matches this report: a background task'stask_notificationand the wakeinitarrive while no run is active, the next queued T3 prompt is folded into that native turn (transcriptqueue-operationremovewithreason: "absorbed_mid_turn"), its one result settles the user run, and the continuation that starts within a second never settles. The Claude transcript has no record after that until Stop.We checked both open fixes against our sequence on current
main(71646e031a), using an adapter test in the wake harness: a background task, its notification and the wakeinitwhile idle, then a user turn answered by an assistant frame and a result that carry the prompt's uuid inuser_message_uuids, then the continuation. Onmainthe continuation never settles (the case times out at 120 s). With either #22513 or #27416 applied onmain, it settles as completed, and the fullClaudeAdapterV2.test.tspasses (291/291 for each).juliusmarminge commentedon Nov 9, 2026
Thanks for the detailed report and regression test. #28010, merged in 692dd9ce6f, settles a wake absorbed by a user turn while preserving the buffered frames.