cmux NEXT Cloud: offer Pause or Resume Machine by the machine's state

Closed
TE
6d ago
+386-0

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.attached already 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 edited Cloud.xcstrings still 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.menuItems honours targetHidden), the palette path (RegistryPaletteProvider uses canPerform without a target, so the palette row still shows and its run is refused with the reason by ActionTargetReasons.refuses in performNow), 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 Autofix with [code]smith
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.