cmux NEXT Cloud: offer Pause or Resume Machine by the machine's state
Leo, 2026-10-29, on the Cloud area: "a whole lot of this cloud stuff ought to get better...".
What was wrong
A Cloud machine's sidebar menu, and the command palette, offered both Pause Machine and Resume Machine whatever state the machine was in. So a paused machine offered Pause and a running one offered Resume. The agent pane's machine picker already allowed Resume only on a paused machine (AgentPaneCloudMachines+App.swift). The menu and the palette never checked.
Fix
CloudHandlers+Machine.swift now gives both actions a target visibility (ActionTargetVisibility.hide) and a target reason (AppActionContext.setTargetReason). Both come from one predicate, pauseResumeMismatch, which reads the session's effectiveStatus (the status the sidebar shows):
- Pause is left out of the menu, and refused from the palette, when the machine is paused ("This machine is already paused.").
- Resume is left out and refused when the machine is running or provisioning (
Status.isLive) ("This machine isn't paused."). - A failed or unknown machine keeps both, and the server decides.
- Only a person's run (menu, palette, key) is judged on the app's record. A CLI, MCP or script run (
cmux cloud pause-machine|resume-machine) still goes straight to the server. Both server routes are idempotent, and the app's record of a machine with no link can be stale after an idle auto-pause (CloudHandlers.attachedalready says so), so refusing a script on that record could block a real resume.
The two refusal strings are new in Cloud.xcstrings, translated into all 21 app locales.
Test
CloudPauseResumeAvailabilityTests (Packages/macOS/CmuxNext/Tests/CmuxNextAppTests) covers: the menu and target reason for a paused machine, for a running or provisioning one, for a failed or unknown one, the palette's run with no target, and CLI/MCP/script runs left to the server. It is committed alone first and should be red at that commit (3ff9dfdd473) and green at the head. CI will show both.
Observed / Not run
- Ran:
python3 scripts/cmux-next/prepush-gate.py, which reports no new violations (it gave app-size warnings for +16 App lines, and those never refuse). I also checked that the editedCloud.xcstringsstill parses as JSON and carries every locale of its neighbours. - Not run: the Swift build and the test suite. This was a Linux lane with no local Swift, so the red at the test commit and the green at the head come from CI. No tagged build or capture was made.
- Read only: the menu path (
ActionRegistry+ContextMenus.menuItemshonourstargetHidden), the palette path (RegistryPaletteProviderusescanPerformwithout a target, so the palette row still shows and its run is refused with the reason byActionTargetReasons.refusesinperformNow), and the server's pause and resume routes (both idempotent).
No capture: menu availability only; covered by the regression test.
Changelog
Fixed: a Cloud machine's menu offers Pause Machine only when it is running and Resume Machine only when it is paused.
risk:normal
Agent session: agent session
š¤ Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Makes a Cloud machine's sidebar menu and command palette offer Pause Machine and Resume Machine based on the machine's state, instead of showing both for every state. Pause is now hidden and refused, with a localized message, when the machine is already paused; Resume is hidden and refused when the machine is running or starting. A failed or unknown machine keeps both and lets the server decide.
- Only a person's run (menu, palette, key) is judged on the app's record ā CLI, MCP, and script runs still go straight to the server, whose pause and resume routes are idempotent.
- Refusal strings are translated into all 21 app locales.