cmux-tui: a workspace commit journals only the workspaces it changes (#32408)
Part of #32408. Fixes the biggest O(session) writer that the classic-import stall still had: each workspace create journaled every workspace.
Cause
WorkspaceCommitIntent::apply (the legacy workspace commit, used by create-workspace, rename, move and close) wrote its rows only when they changed. But its journal record came from normalized_workspace_resource_deltas, which restated every live workspace: an upsert of each one with its name, index, focus and name provenance. So one create journaled the whole workspace list, every session.events subscriber got it, and the record's WAL bytes grew with the session. This path does not use the public fold's pruning (prune_stated_topology_deltas) that the effect paths use.
Measured with the registry WAL frame counter (#34225) on aws-m4pro-7. The create command's own commit took 542 KB per create at 50 workspaces and 3434 KB at 500 (diag step 6f2a2544). In the 512 import, 931 session.events items restated every workspace (up to 1 MB each).
Fix
The commit reads each live workspace's current stored value (name, position, name source and revision) before it rewrites the rows. It then upserts only the workspaces whose value changes: a new one, a rename, an index move, or the focus moving to or from it. Deletes are as before. A create at the end states 1 or 2 workspaces.
This follows R-DURABILITY DESIGN rule C2 (one change, one record of the change's size) and R-DAEMON-STATE rule R5 (every op O(changed)). R-DAEMON-STATE asked me to fix this in my own PR (STATUS 9955) and reviews it for fit with its S3 writer plan.
Regression test
cmux-tui-core mux::tests::workspaces::a_workspace_create_journals_only_the_workspaces_it_changes: 40 workspaces, then one more. That create may journal at most 2 workspace upserts. The journal, replayed from revision 0, must state every workspace's name, index and focus exactly as the session snapshot shows them.
- Red at the test commit 3d16da23205 (Actions run 41703378498):
one create at 40 workspaces journaled 41 workspace upserts. - Green at the fix f816caa1f2e (run 41703378227).
- fmt green (run 41703603154). Full cmux-tui test suite and clippy at the fix: RESULT (runs 41714755892, 41714755377; the first runs were cancelled by Blacksmith during setup).
End-to-end (macOS, aws-m4pro-7): WindowStateBytesPerCreateTests measures all registry bytes per create, at 50 and 500 workspaces.
| 50 workspaces | 500 workspaces | ratio | |
|---|---|---|---|
| feat (step f80161b0) | 762 KB | 3798 KB | 4.99x |
| this fix + the counter (step 2b192246, branch wcd-e2e) | 418 KB | 577 KB | 1.38x |
The test still fails its 1.10x bound. The rest of the growth is the app's whole-document window-state save, which R-DURABILITY stage S-WS removes next. The end-to-end bound stays as it is.
Observed / Not run
- Observed: the runs and steps above.
- Not run: macOS
cargo testof cmux-tui-core (the Mac lane split). The 512-import before/after pair runs after S-WS, so the import sees both fixes.
Changelog
- Fixed: making a workspace in a large session no longer writes (and streams to every client) a record of every workspace.
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes workspace creation so it no longer journals every workspace in the session. Each create-workspace commit previously wrote an upsert for the entire workspace list, making the journal record and WAL bytes grow with the session size (542 KB per create at 50 workspaces, 3434 KB at 500). Now the commit reads each live workspace's stored value before rewriting rows and upserts only what changed: a new workspace, a rename, an index move, or the focus moving to or from one.
Adds a regression test that creates 40 workspaces then one more, asserting the create journals at most 2 workspace upserts and that folding the journal from revision 0 still reproduces every workspace's name, index, and focus as the session snapshot shows.