Problem
Switching the agent pane from Copilot to Claude through Settings can leave the pane stuck at Starting agent... / unable to connect. The old ACP connection retires, but the replacement connection is never started because a valid preflight result is discarded as stale.
Observed on Intelligent Terminal Dev 0.8.0.2, Windows, with WTA built from 602220e7957c1e53d48d0fbab8bb775a1b2feac9. The implicated state handling is also present on main at efd10cfb20072d715d20d023cb141df17b8324d1.
Reproduction
- Open an agent pane and connect to Copilot.
- In Settings, change the agent to Claude while retaining the existing helper/pane.
- The old ACP connection closes and target preflight starts.
- The helper receives both
AgentTransportRetired and AgentReconnectReady for the rebind before preflight completes.
- The pane remains in its connecting state. Master receives no replacement Claude initialize request for this operation.
Expected: Rebind completion notifications should advance the latest pending target exactly once. A duplicate notification must not invalidate a running preflight; a successful result should start the Claude ACP connection.
Relevant logs
Captured on 2026-09-07 at 16:13:46 UTC+8. Timestamps below are UTC. Only relevant excerpts are included; unrelated conversation/configuration contents are omitted.
wta-main_helper-19484.log, lines 222-230 and 234
2026-09-07T08:13:46.490217Z DEBUG wta::helper::runtime: wt_event_rx: received event method=retire_agent_sessions
2026-09-07T08:13:46.499710Z DEBUG alive_mirror: alive session removed by master session_id=9ca0a442-c380-49e7-a98b-83399b0800d6
2026-09-07T08:13:46.683118Z DEBUG wta::helper::runtime: wt_event_rx: received event method=rebind_agent
2026-09-07T08:13:46.696668Z INFO helper: ending helper ACP connection for Agent rebind operation_id=56828-operation-1 generation=1 agent_id=claude
2026-09-07T08:13:46.697111Z DEBUG acp: ACP handle_io completed (over pipe) (t+129.788s)
2026-09-07T08:13:46.697175Z WARN helper: ACP I/O loop to master ended — pipe closed (master gone)
2026-09-07T08:13:46.697783Z INFO agent_rebind: old ACP transport retired; preflighting latest pending target operation_id=56828-operation-1 generation=1 agent_id=claude
2026-09-07T08:13:46.700751Z DEBUG agent_rebind: ignoring reconnect-ready event with no disconnect pending operation_id=56828-operation-1 generation=1
2026-09-07T08:13:46.711506Z DEBUG agent_rebind: ignoring stale target preflight result operation_id=56828-operation-1 generation=1 agent_id=claude
...
2026-09-07T08:13:53.921853Z DEBUG ui_trace: slow: state=Connecting("Starting agent...") turn=Discriminant(0) messages=0 completed_turns=0 input_chars=0 pending_chars=0 scroll=0 activity_frame=0 recommendations=0 permission=false timing_note=false scope="terminal_flush" elapsed_ms=107
The preflight result is for the same operation and generation, not an older user selection.
wta-main_master.log, lines 4451, 4456-4457
2026-09-07T08:13:46.493436Z INFO master: physically closed replaced ACP session step="helper→agent" op="close_replaced_session" helper_id=HelperId(1) old_session_id=9ca0a442-c380-49e7-a98b-83399b0800d6 outcome="closed" elapsed_ms=2
2026-09-07T08:13:46.697518Z INFO master: helper ownership retired; automatic recovery disabled helper_id=HelperId(1) sessions_owned=0 sessions_fallback_retired=0 intentional_close=true
2026-09-07T08:13:46.697613Z INFO master: helper connection task exited cleanly helper_id=HelperId(1) live_helpers=0
This is intentional retirement during rebind, not evidence that the master crashed. The helper warning's parenthetical master gone is misleading in this sequence.
Root cause indicated by the code and logs
App::begin_pending_agent_reconnect_preflight starts with:
let AgentReconnectState::Disconnecting(latest) =
std::mem::take(&mut self.agent_reconnect_state)
else {
return None;
};
mem::take resets the state to Idle before checking its variant. Both AgentTransportRetired and AgentReconnectReady call this helper:
- The first notification consumes
Disconnecting and starts Preflighting.
- The second calls the helper again, takes the
Preflighting value, and returns None, silently replacing it with Idle.
AgentReconnectPreflightComplete requires a matching Preflighting state, so it rejects the valid completion and never sets pending_acp_start.
Source references at the tested revision:
tools/wta/src/app.rs:3724-3747 - destructive state extraction.
tools/wta/src/app_events.rs:596-615 - reconnect-ready handler.
tools/wta/src/app_events.rs:1398-1413 - transport-retired handler.
tools/wta/src/app_events.rs:1972-2007 - preflight result matching.
The logic is provider-independent, so Claude is the observed target rather than the only potentially affected agent. The failure occurs before replacement ACP startup and is separate from hook-listener availability or its retry limit.
Proposed fix and regression coverage
- Only consume the reconnect state when it is
Disconnecting; preserve Preflighting and other states on a no-op call.
- Cover
AgentTransportRetired -> AgentReconnectReady -> PreflightComplete and the reverse notification ordering.
- Duplicate completion notifications must not clear a pending preflight or start a second one.
- Preserve latest-generation selection; genuinely stale results must remain ignored.
- A valid failed preflight must still show the appropriate setup/error UI rather than leave the pane connecting.
No code or user settings were changed as part of filing this issue.
Problem
Switching the agent pane from Copilot to Claude through Settings can leave the pane stuck at Starting agent... / unable to connect. The old ACP connection retires, but the replacement connection is never started because a valid preflight result is discarded as stale.
Observed on Intelligent Terminal Dev 0.8.0.2, Windows, with WTA built from
602220e7957c1e53d48d0fbab8bb775a1b2feac9. The implicated state handling is also present onmainatefd10cfb20072d715d20d023cb141df17b8324d1.Reproduction
AgentTransportRetiredandAgentReconnectReadyfor the rebind before preflight completes.Expected: Rebind completion notifications should advance the latest pending target exactly once. A duplicate notification must not invalidate a running preflight; a successful result should start the Claude ACP connection.
Relevant logs
Captured on 2026-09-07 at 16:13:46 UTC+8. Timestamps below are UTC. Only relevant excerpts are included; unrelated conversation/configuration contents are omitted.
wta-main_helper-19484.log, lines 222-230 and 234The preflight result is for the same operation and generation, not an older user selection.
wta-main_master.log, lines 4451, 4456-4457This is intentional retirement during rebind, not evidence that the master crashed. The helper warning's parenthetical
master goneis misleading in this sequence.Root cause indicated by the code and logs
App::begin_pending_agent_reconnect_preflightstarts with:mem::takeresets the state toIdlebefore checking its variant. BothAgentTransportRetiredandAgentReconnectReadycall this helper:Disconnectingand startsPreflighting.Preflightingvalue, and returnsNone, silently replacing it withIdle.AgentReconnectPreflightCompleterequires a matchingPreflightingstate, so it rejects the valid completion and never setspending_acp_start.Source references at the tested revision:
tools/wta/src/app.rs:3724-3747- destructive state extraction.tools/wta/src/app_events.rs:596-615- reconnect-ready handler.tools/wta/src/app_events.rs:1398-1413- transport-retired handler.tools/wta/src/app_events.rs:1972-2007- preflight result matching.The logic is provider-independent, so Claude is the observed target rather than the only potentially affected agent. The failure occurs before replacement ACP startup and is separate from hook-listener availability or its retry limit.
Proposed fix and regression coverage
Disconnecting; preservePreflightingand other states on a no-op call.AgentTransportRetired -> AgentReconnectReady -> PreflightCompleteand the reverse notification ordering.No code or user settings were changed as part of filing this issue.