Skip to content

cmux-tui + app: hidden terminals' scroll events no longer wake the app's event pump (SWITCH FRAMES SF-5 B) - #32380

Merged
lawrencecchen merged 7 commits into
feat-cmux-nextfrom
fix/switch-subscribe-scroll-on-attach
Nov 2, 2026
Merged

lawrencecchen merged 7 commits into
feat-cmux-nextfrom
fix/switch-subscribe-scroll-on-attach

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

SWITCH FRAMES SF-5 (part B): a hidden terminal that prints made the daemon send scroll-changed to the Mac app's event subscription for every frame of its output. With 8 hidden busy terminals the app's subscription got 604 events in 5 s; with 24, 1910 (about 15 per terminal per second; 2x display, cmux24, round sw-sf5-events). The app's store applies nothing for this event: it reads a terminal's scroll position on that terminal's attach stream. Each event still woke the store's event pump and asked for a main-thread drain frame: the wakeup ledger showed DaemonStore.drain rising from about 0 to 57.8 frames/s, and 4106 display links made, with 24 busy hidden terminals.

Fix (an additive capability, both sides in this PR):

  • cmux-tui: a connection that echoes subscribe-scroll-on-attach-v1 in set-client-info gets no scroll-changed on its subscribe stream. Every other client keeps it. identify lists the capability. Spec: cmux-tui/spec/commands.md, subscribe.
  • App: DaemonCapability.subscribeScrollOnAttach (optional tier) is advertised, so the app's connection says it; plans/cmux-next/daemon-capabilities.json is updated.
  • Compatibility: an older daemon ignores the capability (no change); an older app does not send it (no change). Attach streams still carry scroll-changed.

This is the daemon event feed, not key routing or output bytes, so it does not overlap typing-fix-2's area.

Testing

  • cmux-tui-core server::tests::subscribe_scroll_on_attach (2 tests): red at 9d222a2 on Linux (step 153c8b00): the app connection got 2 scroll-changed events, and identify did not list the capability. Green at 1e68b01 (step 96deee45): 2 of 2 passed. (The other connection gets at least one scroll event, not 20: the subscriber mailbox keeps the newest scroll per surface.)
  • After the merge with feat, 8c485a1: Swift compile (CmuxNext, app, cmux-tui) passed. The head 839b72a changes comments and docs only (review follow-up).
  • App-level gate: the busy hidden terminal A/B on one mini (cmux24, same feat base, bench builds), with part A (terminal: a hidden terminal's output costs the launch-frame keeper no wakes (SWITCH FRAMES SF-5 A) #32384): base vs fix, CPU, wakeups and daemon events at 0 / 8 / 24 busy hidden terminals. Result: see the A/B table below.

Busy-terminal A/B (lead's gate), one mini

cmux24, 2x virtual display at 120 Hz, bench builds of the same feat base 507438d: base test/sf5-ab-base-bench (build bcd1b411) vs fix = #32384 (at f2fa334) + #32380 (build 9b6ddda3). Hidden terminals each print 20 lines/s; a quiet workspace is shown; 20 s windows.

busy hidden terminals app CPU base -> fix app main-thread wakes (debug.wakeups ledger, /s) base -> fix DaemonStore.drain frames/s launch-frame keeper wakes/s process wakeups/s (proc_pid_rusage) base -> fix
0 2.70 -> 2.34 % 13.8 -> 17.5 (CEF message pump) 0 -> 0 0.1 -> 0 4.0 -> 4.2
8 5.78 -> 3.54 % 75.5 -> 15.9 23.4 -> 1.6 14.5 -> 0 221.1 -> 134.8
24 3.81 -> 2.01 % 72.7 -> 7.9 55.1 -> 4.8 14.9 -> 0 311.3 -> 114.4
  • App CPU with 24 busy hidden terminals is within 1 point of the 0-terminal baseline (target met).
  • The app's main-thread wakes are back near baseline (the CEF message pump is the baseline's own).
  • Process-level wakeups stay at about 115/s with 8 or 24 busy hidden terminals: they come from the 8 warm hidden terminals' output being parsed off the main thread (the model update itself, about 14 wakeups/s per warm terminal). That is the next SF-5 cause; it is not in this PR.
  • The daemon-event column of the probe (not shown) still counts scroll-changed, because the probe's own connection does not opt in; the app's DaemonStore.drain column shows its own subscription no longer gets them.

Observed / Not run

  • Observed: the wakeup ledger and daemon event counts above (rounds sw-sf5-busy-ledger, sw-sf5-events on build 0ccecd0c); the Rust red and green; the prepush gate.
  • Not run: the full cmux-tui suite (hosted --full); the A/B is below.

Known, not fixed here

  • The daemon's own event thread still wakes for each scroll event before it drops it for this client (a daemon cost, not the app's).

Changelog

  • Fixed: terminals that print while their workspace is hidden no longer wake cmux many times a second.

Proof

No capture: no visible change.

…ll-changed on subscribe, and identify lists it (SWITCH FRAMES SF-5, expected red)

Busy gate on a 2x display (2026-11-02): with 24 hidden terminals printing, the app's event subscription got 1910
scroll-changed events in 5 s; its store applies nothing for them, but each woke its event pump and asked for a
main-thread frame. The capability constant exists; the daemon does not honor or list it yet.
…scroll-changed on subscribe; the Mac app says so (SWITCH FRAMES SF-5)

The daemon accepts subscribe-scroll-on-attach-v1 in set-client-info, lists it in identify, and skips scroll-changed on
that client's subscribe stream; every other client keeps it. The Mac app advertises it (DaemonCapability, optional
tier; export updated): its store applies nothing for scroll-changed and reads scroll on each terminal's attach stream,
so 24 hidden busy terminals no longer send it about 380 events a second, each a pump wake and a main-thread drain
frame request. Older daemons ignore the capability (no change); older apps do not send it (no change).
… (the subscriber mailbox keeps the newest per surface; the red run saw 2 of 20)
@lawrencecchen lawrencecchen added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 839b72a: 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=pr32380 -f ref=fix/switch-subscribe-scroll-on-attach -f platform=macos -f backend_mode=local

Shipped in:

…ason (the app applies no scroll-changed) and is listed with identify's capabilities (review of #32380)
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Review (subagent, correctness first): SHIP at 839b72a

An independent review agent read the full diff (read only; it built and ran nothing), in two rounds.

  • Round 1 at 8c485a1: SHIP, no must-fix. Checked: no app connection applies scroll-changed from subscribe (store, stage, coalesce key, mobile compat); attach streams keep the event; the phone lane forwards the phone's own set-client-info; set-client-info runs before subscribe on one connection, in order; the Rust continue sits with the other filters and skips no overflow or incognito bookkeeping; capabilities only add, so a late set-client-info is harmless; the capability registration is complete (no CI file missing); the Rust tests compile and assert what the PR says.
  • Round 2 at 839b72a: comments and docs only (the stated reason was wrong: the app applies no scroll-changed; the identify capability paragraph now lists the name). The earlier review stands.
  • Known: the daemon's event thread still wakes per scroll event before it drops it.

Proofs: Rust red 153c8b00 (both tests fail on asserts), Rust green 96deee45 (2/2), Swift compile at 8c485a1 passed. ready-to-land waits for the busy-terminal A/B on one mini (lead's gate).

@lawrencecchen lawrencecchen added the ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds label Nov 2, 2026
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 2, 2026 15:30 Inactive
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 2, 2026 15:30 Inactive
@lawrencecchen
lawrencecchen merged commit 414917e into feat-cmux-next Nov 2, 2026
150 of 155 checks passed
@lawrencecchen
lawrencecchen deleted the fix/switch-subscribe-scroll-on-attach branch November 2, 2026 15:33
cmux-bot Bot pushed a commit that referenced this pull request Nov 2, 2026
…E_SCROLL_ON_ATTACH_CAPABILITY in capabilities.rs (from #32380) (#32442)

capabilities.rs already has use super::*, so the super:: path is redundant and clippy -D warnings refuses it; every
cmux-tui PR's clippy was red on the feat tip 414917e.

This branch was previously deployed

3 inactive deployments
artifacts — 7e863037 Deployed Nov 2, 2026 by cmux-bot[bot] via publish tree-addressed cmux-tui #101859
sentry-dsyms — 7e863037 Deployed Nov 2, 2026 by cmux-bot[bot] via upload cmux-tui dSYMs to Sentry #101859
ci-routing-agent — 7e863037 Deployed Nov 2, 2026 by cmux-bot[bot] via cmux-tui tree preflight #101859
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant