feat(cmux-tui): set-layout-sizes commits a divider batch as one change or none (layout-sizes-batch-v1; daemon only, no app caller yet)
Summary
No change a person can see; the P0 resize rule does not apply. This PR adds a daemon command that no app path sends yet:
git grep -i -E 'set-layout-sizes|set_layout_sizes|layout-sizes-batch|setLayoutSizes'on feat-cmux-next d422b94b60d finds 0 hits inPackages,webviews,iosandweb.- The existing commands that a divider drag or a key resize sends today (
set-split-ratio,set-viewport-pane-width) are unchanged: the PR removes 0 lines in the daemon source files (mux.rs,mux/resource_topology.rs,mux/resource_topology/layout_ops.rs,server.rs,server/capabilities.rs,server/cmd_panes.rs,server/command_dispatch.rs,server/responses.rs). Its other files are new files, the spec, and generated bindings. - The identify capability list gains
layout-sizes-batch-v1. The app reads that list only throughcapabilities.contains(<name>)checks, and no app code names this capability.
The app change that sendsset-layout-sizes(KEYS owner) changes what a person sees. That PR carries the display evidence of the P0 rule (the resize-keyboard journey or a divider-drag display run on a gui-session-v1 worker, at its head).
A key resize or a drag release that ends with 2 or more divider values (a keyboard column trade's two widths, an Option or Command drag's widths and ratios: EndedValues, #34123) went to the daemon as one set-split-ratio or set-viewport-pane-width per value under one transaction. Each was its own commit, so a value the daemon refused after it applied another left that one applied; the app kept all-or-nothing only on its own side (EndedValues.swift: "Two commands are still two daemon commits"). CLAUDE.md: batch requests apply as one transaction. (R-DAEMON-STATE DESIGN 2.10.1, lead item 2026-11-04.)
New command set-layout-sizes {values: [{split, ratio} | {pane, width}, ...], transaction}, capability layout-sizes-batch-v1:
- One commit. One resource plan (
screen.layout_sizes.set) applies every value, in order, to one layout snapshot of the screen, with the checks of its single command (unknown split, row split, the ratio's implied column width computed from the batch's own earlier widths, width range, pane not resizable), then commits once: one durable screen, one layout revision, one undo entry keyed by the resize transaction (the values a drag sent while it moved, under the same transaction, stay in the same undo step), onelayout-changedevent with the transaction echo. - Or nothing. A refused value refuses the plan before anything is staged: the reply carries the single command's
error_code, and its message names the value's index (value 1: ...). Values on two screens, no value or more than 64 values are refused too. A batch that changes nothing commits nothing. - Spec (commands.md), SDK schema, inventory and api-compat surface updated; bindings regenerated (
bindings/codegen/generate.py --write).
The app side is next (KEYS owner, frame-owner-keys): EndedValues.send sends one set-layout-sizes when the daemon advertises layout-sizes-batch-v1, else the per-value commands as today.
Testing
Red first, focused command ./scripts/verify-cmux-tui-hosted.sh --filter layout_sizes (Linux test (linux)):
- Red at a8b19af30cd (
test/rds-batch-red-never-merge: the same method, applying one commit per value as the app does today), hosted run https://github.com/manaflow-ai/cmux/actions/runs/41778535469:a_refused_second_value_leaves_the_first_unappliedanda_batch_is_one_layout_change_and_one_undo_stepFAILED ("a refused second value left the first applied"); the drag-plus-release undo step and the count refusal passed (guards). - Green at 801ba628b9d, hosted run https://github.com/manaflow-ai/cmux/actions/runs/41778538160:
mux::tests::layout_sizes4 of 4. - Wire test at the head:
server::tests::layout_receipts_shutdown::set_layout_sizes_applies_a_trade_whole_or_refuses_it_whole(a refused trade answersviewport-width-out-of-rangewithvalue 1:and changes nothing; a malformed value is refused; a valid trade applies both widths). Hosted run https://github.com/manaflow-ai/cmux/actions/runs/41782294289 at ca012e6b6f5:test (linux)5 of 5 (the fourmux::tests::layout_sizestests and the wire test). python3 cmux-tui/scripts/check-sdk-schema.py: ok (290 commands).generate.py --check: ok.
Compile: fleet compile check job 4b6311261f683aade3e7f6db at ca012e6b6f5: CmuxNext (386 s), app (102 s) and cmux-tui (75 s) passed. lint (linux) at the head: no clippy error; the panic ratchet rows are base rows (cmux-tui-core arithmetic 1007 > 875, as on feat).
Observed / Not run
- Observed: the hosted runs above, the fleet compile, the schema check and the binding check.
- Not run: the app side (not in this PR); macOS execution of the focused tests (Linux only).
Changelog
none
Proof
No capture: daemon command only; the app does not send it yet.
![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
Introduces set-layout-sizes (layout-sizes-batch-v1), a daemon command that commits a resize gesture's final divider values as one change or not at all.
- Currently a key column trade or drag release ending with two or more divider values sends one
set-split-ratioorset-viewport-pane-widthper value under one transaction, each its own commit, so a value the daemon refuses after applying another still leaves that one applied. - The new command stages all values in order on one layout snapshot, running each single command's checks, then commits once: one layout revision, one undo entry keyed by the resize transaction, and one
layout-changedevent. A refused value refuses the whole batch before anything is staged, returning the single command's error code and the failing value's index; values across two screens, empty batches, batches over 64 values, and no-op batches are refused or commit nothing. - Spec, SDK schema, api-compat surface, and inventory updated, with all language bindings regenerated. The app-side caller that sends the new command is a follow-up.