Repository navigation
cmux NEXT: terminals stay quiet while a handoff adopts their shell - #27825
Conversation
After every update handoff the new daemon marks each tab adopting until it takes the shell's host back, and every visible terminal said "Reconnecting to the shell…" at once; a dropped view said "Reconnecting…" at once too. Both now fade in only after a 5 s grace (a slow host's adopt: 1 s connect retries + 2 s handshake after up to 2 s for the owner to say ready) and never show if the state clears within it. "Restoring…" keeps its 0.5 s; the lasting "Disconnected · Click or type to reconnect" and stopped states still show at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-ecef4926772c95ead301
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
web and size checks: green |
|
Review (codex gpt-6-astra high fast, subrouter): SHIP at ed68f6f No P0-P2 findings. The reviewer read the diff against merge-base a51021c and checked:
CI at this head:
|
…doff capture A Debug-only control route sets live terminal tabs' terminal_state (adopting, reconnecting, running, ...) as the daemon's tab push does, so a capture can hold the moment of an update handoff without restarting the daemon. tour:terminal-handoff shoots 1 s into an adopt (base: "Reconnecting to the shell…"; this branch: nothing), 6 s in (both: the label), and after it clears. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-ecef4926772c95ead301
|
Found 3 test failures on Blacksmith runners: Failures
|
|
cmux-next daemon tests (check run 123892225380, live-daemon suites), compared by test name with the newest completed base run of the same suite (a0174db, job 123887275939, 18 red): Also red on base (17): AgentCLIIntentLedgerTests.aForgedRecordAcknowledgementCannotSettleAnotherIntent, AgentCLIIntentLedgerTests.recordsOriginalIdentityBeforeAttachingOnlyItsPublicReceipt, AgentCLIIntentLedgerTests.retiredLedgerCannotReconnectOrDispatchAnUpdate, AgentCLIIntentLedgerTests.settlingSendsOnlyTheKeysAndRejectsALedgerThatKeptThem, AgentCLIJobCarrierTests.retiredObservationTransportCannotReattach, AgentCLIJobCarrierTests.retiredTransportCannotRedirectAnIntent, AgentInventoryWatchLifecycleTests.cancellationRecoversNoLeaseRefusalWithoutSendingUnfencedCleanup, AgentInventoryWatchLifecycleTests.cancellationRetainsTheWithheldReceiptThenConditionallyDisables, AgentInventoryWatchLifecycleTests.capturesShareOneManagerAndRetirementPrecedesReadmission, AgentInventoryWatchLifecycleTests.foreignAdmissionAndReceiptCannotBecomeLive, AgentInventoryWatchLifecycleTests.refusedCleanupRemainsFailedAndCannotReadmit, AgentInventoryWatchLifecycleTests.sameOwnerReconnectNeverRedirectsOldCleanup, CreateTerminalLateStartTests.aRefusedCreateIsNotAskedAgain, CreateTerminalLateStartTests.aTerminalThatStartsLateIsWaitedForAndMadeOnce, DaemonSideEventEnvelopeTests.realConnectionsStampNativeEventsAndSeparateEqualSerials, FrontendBrowserTabLateReplyTests.aDaemonWithoutKeysIsNotAskedTwice, FrontendBrowserTabLateReplyTests.aLateCreateReplyStillOpensTheOneTab. Not red on that base run (4), all in No crash or signal in this job. |
…al-quiet-during-handoff
|
Review (the owner lane's independent review, at its land request): SHIP at cbadece Recorded by lane-gh-broker from this PR's owner lane, which asked to land this exact head after its independent review. next-autoland lands it once every red is the base's; a BLOCK at this head, a HOLD line or a hold label still stops it. |
|
cmux-next daemon tests at cbadece (check 123992395966), compared by name with base f3ae1f8's daemon tests (run 41037828764, job 124004649928, 6 red). No crash or signal in either. Same on base (4): BusyRefusalReconnectTests.aDaemonBusyPastTheBudgetIsStillReached, BusyRefusalReconnectTests.aRefusedConnectionSaysTheDaemonIsBusy, SplitLateStartTests.aDaemonWithoutClientKeysIsNotAskedAgain, SplitLateStartTests.aRefusedSplitIsNotAskedAgain. Not on base (1): BranchStateResourcesTests.ephemeralStatusScreenAndTabStateReachTheRecords (BranchStateResourcesTests.swift:80). It drives workspace status, progress, screen pin/colour and tab zoom through a live daemon, with 20 s waits. This PR changes the terminal stall banner's timing, a DEBUG-only |
|
Independent review (gpt-6.1-sol high) at 60c16c6: BLOCK.
|
|
Owned by an agent session · session |
…al-quiet-during-handoff # Conflicts: # Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/TerminalShellStallTests.swift
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
…xtLayout (app_lines) (#29514) The tip's arch-ratchet app_lines row is red over 30806df: three commits add CmuxNextApp lines with no exception entry, each refused alone by `arch-ratchet.py --base <c>^ --head <c>`: - 0703de4 (#28049, Move Space to This Window / Merge All Windows): +99 - 088b1bc (#27919, a link from another app never reshapes Chief's Home): +16 - 7a12ed3 (#27825, terminals stay quiet while a handoff adopts their shell): +12 (the last two came in through the direct-push merge 8dd8bdc; #28799's +75 is #29478's). Their own code is wired into WindowManager, ExternalOpenController and AppControl, so it cannot leave the App by itself. Instead, the App's pure window membership model moves out whole: WindowRegistry (struct, Window, Changes, every transition) and its repairedSelection extension go unchanged to CmuxNextLayout/Windows with `public` access; its persistence extension (WindowRegistry+Records) and the observable store stay in the App. WindowRegistry used only CoreGraphics and Foundation (and Design's FocusAfterClose, which CmuxNextLayout already depends on). App and test files that name the type import CmuxNextLayout. CmuxNextApp -426 against the tip; the watcher's range from 30806df is app_lines -226 with this alone. No behavior change.
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo: "HOW THE F*** IS THIS STILL NOT FING CHANGED IT'S LITERALLY "STOP SHOWING FING NONSENSE ON LOAD" + "JUST FING REMOVE THE FING OLD DAMEMONS WHAT THE…""
What was wrong
After every update handoff the new cmux-tui marks each tab's
terminal_stateadoptinguntil it takes the shell's host back.TerminalLinkWatch.shellStallturnsadopting/reconnectingintoTerminalShellStall.reconnecting, andTerminalStatusBannershowed its text, "Reconnecting to the shell…", at once, so every visible terminal flashed it on each update. A view whose own attach dropped (TerminalAttachMachine.reconnect,.disconnected(_, reconnecting: true)) showed "Reconnecting…" at once too. Only "Restoring…" waited (0.5 s).The fix
TerminalStatusBanner.revealDelay(for:stall:)is the one rule for when a label may show:.reconnecting) and "Reconnecting…" (link reconnecting) waitreconnectingRevealDelay= 5 s, then fade in, and never show if the state clears within it.Why 5 s: a normal adopt finishes in well under a second for a live host. The worst normal case is a slow host, which costs up to 1 s of connect retries plus 2 s of handshake (
HOST_CONNECT_RETRY_WINDOW,HOST_HANDSHAKE_TIMEOUTincmux-tui-core/src/terminal_host_runtime.rs), after up to 2 s for the owner to say ready (STARTUP_READY_WAIT,mux/terminal_adoption.rs). 5 s covers that.While a hidden banner is still waiting, a change keeps the longer wait from the same start. So "Restoring…" turning into a reconnecting stall does not show at 0.5 s. A banner already on screen stays on screen: no flicker back to hidden.
Nothing is removed. The banner, its strings and the lasting "Disconnected" state are unchanged, and no strings are added. It reuses the existing hold/scheduled-fade animation, which is removed whenever the banner hides or shows a lasting state.
Tests
TerminalShellStallTests.aHandoffAdoptionStaysQuietUnlessItLastscovers:adoptingpush holds the label for about 5 s;runningwithin the grace hides it and leaves no animation;failedand "Disconnected" show at once;On base it fails: there is no hold for the stall, and
revealDelaydoesn't exist. Swift compiles in this PR's CI.Risk: low. One view's reveal timing, with a lasting failure still shown at once.
Screenshots
Before / after: a terminal during an update handoff. At 1 s into the handoff, BEFORE shows 'Reconnecting to the shell…' at the bottom of the terminal and HEAD shows the shell output with no banner. At 6 s, both show 'Reconnecting to the shell…', so a handoff that lasts still says so. Captured on the fleet, each still checked by eye. BEFORE is the merge-base plus only the Debug-only debug.terminal_state route (no fix).
BEFORE, run 1s (91f00ab, merge-base + debug.terminal_state only, 1 s into the handoff)
HEAD, run 1s (96030b7, 1 s into the handoff)
BEFORE, run 6s (91f00ab, 6 s into the handoff)
HEAD, run 6s (96030b7, 6 s into the handoff)
Captured at
96030b74; the later commit (cbadece, a base merge) changes no terminal UI.Agent session: agent session
🤖 Generated with Claude Code