cmux NEXT: show nothing while acpmux starts; a lasting failure is one line

Merged
TE
7d ago
+388-435

While cmux NEXT starts and connects to acpmux (first launch, the relaunch after an update, a daemon restart), the agent pane now shows nothing: no error card, no "acpmux did not answer in time" above the composer, no Reconnecting spinner or Retrying… pill in the header, no Retry button. The composer stays usable. A failure is said only once it is one, as one line with one small action.

Leo, 2026-10-29:

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 F*** ARE YOU GUYS FU CKING DOING" AND WHY THE F*** ARE YOU STILL CLEARING MY FING COMPOSER DRAFT WHEN I TYPE BEFORE FING LOADING IM ASSUMING MY PAST DRAFTS MAY ALSO GET FED TOO GOD THESE FING RECOVERY STEPS ARE SO F***ING RETARDED

This PR is the "show nothing on load" part. Removing the old daemons and keeping drafts are separate PRs.

Cause

  • The app tagged only an update's handoff (daemonRestarting) as a recovery (details.reconnecting). Every other start failure (the handshake timing out while the daemon loads, a start that lost the lock race, a daemon whose executable an update replaced) reached the page as a refusal, and the page painted the red card at once, with "cmux tries again on its own. Your prompt stays here until acpmux connects." and Retry.
  • The recoveries it did know showed a neutral spinner with "Reconnecting…" and Retry (Retrying… once pressed), and the header said Reconnecting for any retrying handshake (connecting:).
  • The page's retry wait grew to 30 s, so it could see a ready daemon up to about 32 s late (#27283's replay of one Mac's log).

Why the earlier fixes didn't do it: #24795 retried only a missing socket for 3 s, #23692 kept only the too-old notice back until a real refusal, and #26115 made only the update handoff neutral and still drew a Reconnecting line. The other start failures still drew the card, and every path still showed something.

Fix

  • Swift. AgentPaneHostError.isTransient (new file): a daemon starting, restarting, handed off, still loading, losing the start's lock race, or stopped while the page keeps looking for it is a recovery, tagged details.reconnecting. These are said at once: acpmux missing, another build's daemon (holder or owner known), a daemon on another home, and a daemon that refused to start from where it is. The cause list builds on #27283's log analysis (credit to it; its branch is not used).
  • Page. hostErrorShows (hostRetry.ts): a recovery is said only after HOST_ERROR_DEADLINE_MS = 25 s of failed handshakes, with the retry loop still running; a refusal is said at once. I picked a deadline over "hard refusals only" because a lost lock race or a slow daemon looks the same as a broken one for the first seconds. In the logged update timeline the daemon answered 10 s after the first failed handshake, so 25 s is past any real restart and still well short of a minute. A connect clears everything.
  • One line. HostUnavailableLine replaces the card and the Reconnecting spinner: the host's message, cut to one line (the whole text is the tooltip), and one small button, Restart acpmux when another binary holds the socket, else Retry. The recovery copy is gone: host.retrying and host.restartHint are dropped from the strings. The folder-harness card (Enable / Trust) is unchanged.
  • Header. paneHeader no longer says Reconnecting for a retrying handshake, and a dropped socket the pane is already asking for again is quiet.
  • Fast retries. nextRetryDelay: while recovering, the next handshake comes within 2 s (as in #27283) instead of backing off to 30 s. A refusal still backs off to 30 s.

Timers: none new. The deadline is checked when a failed handshake comes back (at most every ~2 s plus the attempt), so no extra timer runs. The retry timer still stops on connect, on unmount and during a Restart acpmux.

Screenshots

Gallery renders (Chromium, the gallery-health job's own renders of these states; the change is webview-only on screen).

Before (base): header while acpmux restartsAfter: while acpmux recovers (within 25 s)
before: Reconnecting in the headerafter: nothing shown

After, a failure that lasts past 25 s: one line and one action.

after: one line with Retry

No capture: a native capture of a cold acpmux start shows an empty pane by design; the states above are the pane's own webview renders.

Performance

The numbers come from #27283's replay of the page's retry loop against the 2026-10-29 update timeline. A failed handshake takes 2.05 s, and the old cap was 30 s; this PR uses the same 2 s cap while recovering.

metricbeforeafterdeltaharness
chat connects after the daemon answers, worst case for a daemon ready anywhere in 0–60 s32.04 s4.05 s−27.99 s#27283 retry replay, 10 ms sweep
same, mean10.52 s1.99 s−8.53 ssame
time an error is on screen during a restart the daemon recovers from in < 25 sthe whole outage0 s−100%the code path (hostErrorShows)

Tests

  • hostRetry.test.ts: within the deadline a transient failure shows nothing; past it, it shows; a hard refusal shows at once; the 2 s and 30 s waits.
  • HostError.test.tsx: the late line is one <p> plus Retry, with no "tries again on its own" or "stays here until" and no spinner; a refusal's one action is Restart acpmux; the harness card is kept.
  • header/paneHeader.test.ts: a retrying handshake is quiet in the header; a dropped socket is quiet while the pane recovers.
  • AgentPaneHostErrorTransientTests (new) and AcpmuxRefusedRestartTests.onlyARecoveryIsAReconnectingHandshake: which failures are recoveries.

Observed / Not run

  • Observed: AcpmuxRefusedRestartTests and AgentPaneHostErrorTransientTests passed on a fleet Mac (cmux-ci package-test-lane.sh suite, aws-m4pro-8) at 557c1745; the Swift sources are unchanged since.
  • Observed: HostError.test.tsx, hostRetry.test.ts, header/paneHeader.test.ts (18 pass) and the webviews typecheck at 48a2331143 (after merging feat-cmux-next, which brought #27646's "a queued Retry holds Restart acpmux"; kept on the one-line action with its test).
  • Not run: the full Swift package suite (CI runs it).

Changelog

  • Fixed: the agent chat no longer shows an error card or Reconnecting while acpmux starts or restarts; a lasting failure is one line.

Agent session: agent session
🤖 Generated with Claude Code