cmux NEXT: a chat opens on its attach, not on acpmux's paced session list (30 s → <1 s on a busy Mac)

Merged
TE
6d ago
+206-5

Why

Leo, 2026-10-30: "WHY DID IT TAKE 30 F***ING SECONDS TO LOAD YOUR CHAT". When he opened an existing chat in cmux NEXT, the pane showed "Waiting for the agent service (0:32)" with Restart agent service for about 30 s, and then the chat loaded.

Cause

The daemon wasn't stuck, and it wasn't restarting. The chat's open was waiting on acpmux's paced full session list.

  • Opening a chat sends _acpmux/status, the first page of the list (_acpmux/watch {limit: 200}) and the chat's own attach together (#27755). Before it draws, open() then awaits the list.
  • The list goes through the hub's list guard (hub/list_guard.rs, #26479). It builds the list once per window, and the window is 32 times the last build's cost, up to 15 s. It reuses a build only while nothing changed. On a daemon with dozens of agents streaming, something always changed, so each new open waits for the next window. A request may wait up to MAX_WAIT (30 s).
  • On the affected Mac's daemon (429 sessions, 31 live agents, 6131 peer rows), a bench client sent the same requests the pane sends:
requesttime
initialize1 ms
_acpmux/status28 ms
_acpmux/attach (the chat itself, 315 KB)622 ms
_acpmux/watch first page, 8 opens in a row1460, 5630, 4291, 301, 18272, 10607, 5444, 56 ms
_acpmux/watch first page, cold19.8 s

So the chat's attach had answered in about 0.6 s, and the pane went on waiting up to about 20 s, sometimes more, for a list it does not need in order to draw the chat. The daemon log for the window (04:45-05:05 UTC) shows no restart and no stuck session. It shows only peer reconnects and session/new admitted past the lane cap: this Mac has 66 live sessions, which is the load that keeps the list stale.

What changes (direct.ts)

  • When the attach sent with the list (askEarly) answers first, for this chat on this machine (its reply's session row has this sessionId and no peer), the open draws the chat from it (attachedBeforeList). It doesn't wait for the list.
  • The list still arrives. It settles after the open, through the same path a reread after a lag uses (refreshSessions, now able to take the read already in flight). listedSessions stays unset until that happens, so nothing says "this chat isn't listed" too early.
  • Every other case still waits for the list and decides as before:
    • the list came first;
    • the attach failed (a missing chat: the list still says why, sessionMustExist);
    • the reply named another chat or machine;
    • a remote wire, whose machine names the tab's chat in the list (tab).

The daemon's pacing is unchanged. It protects the daemon's CPU (#26479), and the chat no longer waits on it.

Risk: until the list lands, the pane's session list holds just the open chat (and whatever the page kept). Pickers that read the list fill in a moment later. Nothing new runs on a timer.

Performance

metricbeforeafterdeltaharness or run link
chat open (connect) with the session list held 3000 ms3004 ms1 ms-3003 ms (-99.9%)chatOpenPacedList.test.ts "with a list that takes 3 s", same harness; before run 41176364697 at the red commit, after run 41178642875
live daemon, time the open waits on _acpmux/watch (first page)0.06-19.8 s0 s-100%bench client against the affected Mac's daemon, above
live daemon, _acpmux/attach of the chat (what the open now waits on)0.62 s0.62 s0same bench

Testing

Commit order: the red tests come first, then the fix.

  • New webviews/src/agent-session/acpmux/chatOpenPacedList.test.ts:

    • a chat opens and draws while every list reply is held, and the list settles after it;
    • a chat its attach cannot find still waits for the list to call it missing;
    • attachedLocalRow only takes this chat's row on this machine;
    • the 3 s timing case above.

    At the red commit 3 of these 4 fail (run 41176364697). With the fix all 4 pass.

  • These suites pass with the fix (run 41178642875): chatOpenTrips, agent-pane-cached-chat-open, agent-pane-new-tab-cached-chat, agent-pane-peer-copy, agent-pane-peer-copy-reply, chat-open-long, agent-pane-turn-opener, agent-pane-session-change-coalesce.

  • Review fix (c40553e6): an attach that acpmux forwarded from a peer names the peer at the reply's top level (peer_forward.rs), so it never opens as this Mac's chat. A test covers it. At that head: typecheck clean (3412 files); chatOpenPacedList, chatOpenTrips and agent-pane-peer-copy pass (62 pass, 0 fail; cmux-ci linux-pool job 62f2db42).

  • bun run typecheck: no type errors (3391 files). vp lint on the changed files: 0 warnings, 0 errors (run 41176469348; that run's catch-all bun test glob hit the 30-min cap, so the suites were rerun one by one above).

No capture: no pixel changes. The chat draws sooner, and the "Waiting for the agent service" line, which only shows after its deadline, no longer comes up for this cause.

Review: router-review BLOCK at 44510dbf (peer attach taken as local; fixed in c40553e6), then SHIP at c40553e6 (gpt-6-astra high), and SHIP at 6e53d256 (formatting only since; gpt-6.1-sol xhigh) with one follow-up: [P1] a session moved to a peer while the list is held keeps the attach's local row (edge case of the new path; filed as an issue, fixed forward).

Changelog

  • Fixed: opening an existing chat on a busy Mac no longer waits on the full chat list (up to 30 s behind "Waiting for the agent service").
    Agent session: agent session

šŸ¤– Generated with Claude Code