Skip to content

cmux NEXT: terminals stay quiet while a handoff adopts their shell - #27825

Merged
cmux-bot[bot] merged 7 commits into
feat-cmux-nextfrom
lane/terminal-quiet-during-handoff
Oct 31, 2026
Merged

cmux-bot[bot] merged 7 commits into
feat-cmux-nextfrom
lane/terminal-quiet-during-handoff

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

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_state adopting until it takes the shell's host back. TerminalLinkWatch.shellStall turns adopting/reconnecting into TerminalShellStall.reconnecting, and TerminalStatusBanner showed 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 to the shell…" (stall .reconnecting) and "Reconnecting…" (link reconnecting) wait reconnectingRevealDelay = 5 s, then fade in, and never show if the state clears within it.
  • "Restoring…" keeps its 0.5 s.
  • Every lasting state shows at once, as before: "Disconnected: … · Click or type to reconnect" after the quiet retries run out, "Terminal stopped: …", "Process exited"/"Terminal lost", "Turned off by your organization".

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_TIMEOUT in cmux-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.aHandoffAdoptionStaysQuietUnlessItLasts covers:

  • the delay rule for each state;
  • an adopting push holds the label for about 5 s;
  • running within the grace hides it and leaves no animation;
  • failed and "Disconnected" show at once;
  • "Restoring…" turning into a stall keeps the 5 s wait.

On base it fails: there is no hold for the stall, and revealDelay doesn'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)

Light Dark
BEFORE 1s light BEFORE 1s dark

HEAD, run 1s (96030b7, 1 s into the handoff)

Light Dark
HEAD 1s light HEAD 1s dark

BEFORE, run 6s (91f00ab, 6 s into the handoff)

Light Dark
BEFORE 6s light BEFORE 6s dark

HEAD, run 6s (96030b7, 6 s into the handoff)

Light Dark
HEAD 6s light HEAD 6s dark

Captured at 96030b74; the later commit (cbadece, a base merge) changes no terminal UI.

Agent session: agent session

🤖 Generated with Claude Code

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
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 41b3d30: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr27825 -f ref=lane/terminal-quiet-during-handoff -f platform=macos -f backend_mode=local

web and size checks: green

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 30, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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:

  • the reveal bookkeeping: clearing the status removes both animations and the pending wait, and lasting states drop the hold at once;
  • that the 5 s grace covers the adoption timings in cmux-tui: the 2 s STARTUP_READY_WAIT, the 1 s connect retry window and the 2 s handshake timeout. Adoptions queued behind the 8 slots can take longer; they then show the label, which is intended.

CI at this head:

  • cmux-next fleet Release compile is green.
  • These reds fail the same way on base runs at 609814a, 0c57509 and 1816f75:
    • cmux-next checks: Script tests swift-test-summary.test.sh, and Sidebar-Look "3 commit(s)";
    • nightly-guards release-notary;
    • Guard status.
  • pr-capture-check is red because a fleet Mac capture is still pending.

…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
@blacksmith-sh

blacksmith-sh Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Found 3 test failures on Blacksmith runners:

Failures

Test View Logs
test_a_picked_signing_label_has_a_rerun_alternative (main.RunsOnFallbackTests.test_
a_picked_signing_label_has_a_rerun_alternative)/
test_a_picked_signing_label_has_a_rerun_alternative (main.RunsOnFallbackTests.test_
a_picked_signing_label_has_a_rerun_alternative)
View Logs
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)/
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)
View Logs
test_no_runs_on_chain_ends_on_the_reserved_side_label (main.RunsOnFallbackTests.tes
t_no_runs_on_chain_ends_on_the_reserved_side_label)/
test_no_runs_on_chain_ends_on_the_reserved_side_label (main.RunsOnFallbackTests.tes
t_no_runs_on_chain_ends_on_the_reserved_side_label)
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol high fast, subrouter; Sol because Astra through SR2 hung fleet-wide): SHIP at 96030b7

No P0-P2 defects found. The earlier head ed68f6f also got SHIP (Astra). This head adds only the Debug-only capture route debug.terminal_state and tour:terminal-handoff.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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 ClassicUpgradeSimulationTests: ClassicUpgradeSimulationTests.aDowngradeAndReUpgradeMergesOnlyNewWork, ClassicUpgradeSimulationTests.anOffSSHHostDoesNotHoldBackLocalWorkspaces, ClassicUpgradeSimulationTests.bothDamagedFallsBackToTheNewestArchive, ClassicUpgradeSimulationTests.onlyThePreviousCopyLeftStillMigrates. This PR changes only the terminal banner, a Debug route and a tour, nothing in the Classic migration. The same suite fails on unrelated #28059 (aDowngradeAndReUpgradeMergesOnlyNewWork, onlyThePreviousCopyLeftStillMigrates and more), so it reads as a base red newer than a0174db. Not proven against base: the next base run (d62d4d7) is still queued.

No crash or signal in this job.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Merged feat-cmux-next (7050ae3) into this branch, at cbadece. The merge is clean and the PR's own diff is unchanged (same content as the SHIP head 96030b7), so that SHIP carries to cbadece. It picks up base's BusyRefusalReconnect and SplitLateStart fixes so the daemon tests rerun on a fixed base.

@teamleaderleo

teamleaderleo commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

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.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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 debug.terminal_state route and a tour, and none of that is on this test's path. Not yet shown to be a flake: a rerun of this job decides it.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (independent Codex gpt-6.1-sol, high, via the review router; full diff from merge base 7050ae3 to this head): SHIP at cbadece.

No P1/P2 findings.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Independent review (gpt-6.1-sol high) at 60c16c6: BLOCK.

AppControl.swift:583 — Invalid surface values are not rejected: negative integers trap during UInt64 conversion, while non-integers silently become nil and update every live terminal. Validate and return an error.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by an agent session · session

…al-quiet-during-handoff

# Conflicts:
#	Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/TerminalShellStallTests.swift
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex, router-review): SHIP at 41b3d30

SHIP at 41b3d30

Visual verdict: after screenshots match the intended behavior; no layout issues.

No correctness findings.

Follow-ups: none

@teamleaderleo teamleaderleo removed the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@cursor

cursor Bot commented Oct 31, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot 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.

@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent October 31, 2026 15:17 Inactive
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent October 31, 2026 15:17 Inactive
@cmux-bot
cmux-bot Bot merged commit 7a12ed3 into feat-cmux-next Oct 31, 2026
157 of 160 checks passed
@cmux-bot
cmux-bot Bot deleted the lane/terminal-quiet-during-handoff branch October 31, 2026 15:31
lawrencecchen added a commit that referenced this pull request Oct 31, 2026
…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.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 110f685d Deployed Oct 31, 2026 by cmux-bot[bot] via cmux-tui tree preflight #90358
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant