Apply queued checkout switches on idle and announce them at pick time - #135
Merged
Conversation
A checkout switch used to wait for the user's next message, and while a subagent ran in the target worktree the only signal was a small chip by the composer. Two changes: - The reactor now applies a queued switch as soon as the session is safe to move: idle status, no active turn, no pending background tasks. Triggered from thread.meta-updated (pick while idle) and thread.session-set (background tasks settling, turn finishing), so the source control panel and diff surfaces follow without needing a new message. - Picking a branch that queues a switch behind a busy session now raises an info toast from both the composer branch picker and the source control panel, saying where the agent still is and when it will move.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
When a thread's session was busy (running turn, or a subagent working in a worktree), picking a different checkout only queued the switch, and the queue drained on the next user message. Until then every surface stayed pointed at the old checkout, and the only signal was the small chip next to the composer. In practice this read as "it did not let me switch".
What
Server: apply the queued switch as soon as it is safe, not on the next message.
ProviderCommandReactornow listens forthread.meta-updated(checkout picked while idle) andthread.session-set(turn finishing, background tasks settling). When the session is idle with no active turn and no pending background tasks, and its checkout differs from the thread's target, the session is cycled into the target via the existing resume-carrying restart path. Threads without a live runtime keep lazy semantics. The guard validates against the freshly projected thread and the projectedsession.checkoutCwd(rewritten on every bind), so it cannot loop or fight a turn dispatch: the per-thread sequential worker already serializes both.Web: say the queueing out loud. A new shared
queuedCheckoutSwitchToasthelper (built onresolvePendingCheckoutSwitch) raises an info toast when a pick leaves the live session behind: one wording when a background task defers the switch, another when only the running turn does. Wired into both the composer branch picker and the source control panel branch menu. No toast when the session is idle, since the switch now applies right away. Chip copy updated to match the new behavior ("moves as soon as the current turn ends" instead of "your next message starts it").Tests
ProviderCommandReactor.test.ts: two new tests — queued switch applies on background-task settle without a new message, and immediately on an idle pick; existing deferral tests still pass unchanged.BranchToolbar.logic.test.ts: coverage for the toast helper (deferred, mid-turn, idle → null, no-op pick → null).