cmux-tui: an identical frontend projection save writes nothing and wakes no one; a session registration reads only its row (idle CPU)

Merged
TE
2d ago
+168-31

The app's headless cmux-tui owner idles at 29-84% CPU on a nightly with nobody using the app. A full 10 s sample of it at 29% puts the time in app-driven writes and reads, not in a loop of the daemon's own:

  • one app connection (mux-control-dispatch): WorkspaceRegistry::put_session (SQLite) and Mux::personal_snapshot (list-personal), about 3100 of 5052 samples, most of the latter blocked on the registry mutex;
  • mux-launch-snapshot: Mux::personal_snapshot, about 1870 of 5052, rebuilding after each change event;
  • mux-conn threads: put_frontend_projection (SQLite execute_batch).

This PR makes the daemon side cheap whatever the app sends. A follow-up fixes the app's re-registration ping-pong (SessionRegistrar).

Changes

  • put_frontend_projection with an identical body is no change. The app saves its window and sidebar documents again on many events with nothing changed. Each save under a new mutation id bumped projection_revision, wrote the row and a ledger row, and emitted FrontendProjectionChanged. That woke every subscriber and the launch snapshot writer, which rebuilds the personal snapshot under the registry lock. Now the stored schema version and canonical payload are compared in SQL, without loading the row. If they match, the stored revision comes back with replayed: true: nothing is written and no event is emitted, the same way a repeated agent report already behaves. The expected-revision check still runs first, so a stale client still gets its conflict.
  • A session registration reads only its own row. read_session used to read and parse every session row, each with its capability list (several KB), to find one. It now does a keyed lookup. An identical put-session already wrote nothing and emitted nothing (#30457); it now costs one row read instead of O(sessions) JSON parses.

No new timers, threads or observers.

Tests

  • workspace_registry::tests::an_identical_frontend_projection_under_a_new_mutation_writes_nothing. It fails on base, where the second save gets revision 2 and replayed: false, and passes on head. It covers key order, the ledger row count, a matching and a stale expected revision, and that a new body or schema version is still a change.
  • The existing frontend projection, personal and put-session tests are unchanged.

Performance

metricbeforeafterdeltaharness or run link
writes per identical projection save2 rows (projection and ledger) plus 1 change event0-100%an_identical_frontend_projection_under_a_new_mutation_writes_nothing
rows parsed per identical put-sessionevery session (with capabilities)1O(sessions) to O(1)read_session
app headless owner idle CPU on an idle nightly29-84% (sample: put-session, list-personal, put-frontend-projection)to be read after the next nightlysample after the next nightly

Observed / Not run

  • Observed: the samples of the idle nightly owner (above). The new test fails on base and passes on head (run with cmux-ci; see the PR comments).
  • Not run: an end-to-end idle measurement. That waits for a nightly with this change.

Changelog

none

Agent session: agent session

šŸ¤– Generated with Claude Code