Cloud cap card: Pause frees a slot (CloudDO pause on the app-server link; classic Pause only where it frees room)

Merged
TE
2d ago
+96-13

Leo, on a Cloud create past the plan's machine cap: "you try to create a new workspace/machine past the cap, what do we wanna do? reuse exisitng or ask upgrade or". The card #32679 shipped (Pause one / Upgrade / Run on This Mac) has two gaps; this fixes both. Fixes #32708, fixes #32710.

#32708, app-server link. The cap card's Pause, and the sidebar's Pause and Resume, called the classic /api/vm/{id}/pause, which cannot pause a CloudDO machine. On the app-server link CloudService.pauseMachine / resumeMachine now send cloud.machine.pause / cloud.machine.start through the app server (CloudMachinePower, CmuxNextCloud). CloudDO already answers a person's pause only once the provider call landed (CloudCore submitAs, its 25 s wait), so the slot is free when the card's create goes again. A pause longer than that wait answers mutation.indeterminate; it is asked again with the same key (it joins the call in flight), at most 3 times. No backend change is needed: the Mac relay's install may pause without an approval (cmux-platform domain/Cloud.ts authorize), and the Rust app server already declares mutation.indeterminate for pause.

#32710, classic link. The classic allowance counts paused machines (web/services/vms/repository.ts activeLimitExceededInTx, LIVE_VM_RESOURCE_STATUSES), so a Pause there frees nothing. The classic card now offers Pause only for the shared vCPU/memory pool refusal (vm_resource_pool_exceeded, activePoolUsage counts provisioning and running only), mapped to a new CloudUpgradeReason.pool; the allowance refusal and the local plan gate keep Upgrade and Run on This Mac. Classic's backend is untouched.

CmuxNextApp lines do not rise (in-place edits only).

Tests: CloudMachineCapTests (the classic card offers Pause only where pausing frees room; the app-server pause sends cloud.machine.pause with one key and origin user, retries indeterminate up to 3 calls, stops at once on any other error), CloudCreateWithoutAskingTests (a 402 vm_resource_pool_exceeded throws .pool).

Design: leo-workspace design/cloud.md, "At the machine cap".

Agent session: agent session

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes two gaps in the Cloud cap card's Pause so that pausing actually frees a slot before the create retries (cmux#32708, cmux#32710).

Bug Fixes

  • On the app-server link, Pause and Resume now send cloud.machine.pause / cloud.machine.start through the app server, since the classic /api/vm cannot pause a CloudDO machine.
  • CloudDO answers once the provider call lands, so the slot is free on retry; a pause longer than CloudDO's wait answers mutation.indeterminate and is retried with the same key, up to 3 times.
  • On the classic link, Pause is now offered only for the vCPU/memory pool refusal (vm_resource_pool_exceeded), which counts running machines alone; the allowance counts paused machines, so its refusal keeps Upgrade and Run on This Mac.
  • No backend changes are needed.