cmux NEXT Cloud: New Cloud Workspace shows the plan's machine sizes, with the hourly rate where the server gave one

TE
6d ago
+922-12

Leo, 2026-10-30: "How about more cmux-platform audits, improvements, features, fixes, optimizations, polish, plus the ui that gets people (or agents) using it". And 2026-10-29: "let's just get the cloud and its respective ui up to snuff".

New Cloud Workspace (behind New Cloud Machine…, the person's own create) named no size and no cost: the plan picked the machine silently, and the first a new account learned of its size or price was the sidebar row after it was made. The dialog now has a Machine size choice with the plan's sizes, smallest first and selected, and the pick is sent as the create's memoryMb.

  • Sizes and vCPUs come from GET /api/vm's limits (memoryOptionsMb, vcpusByMemoryMb), which CloudMachineLimits now reads. A malformed size field drops the sizes, never the machine cap.
  • A size shows an hourly rate (4 vCPU, 8 GB memory · $0.26/h) only when the server already priced a listed machine of exactly that size, and those rates agree. The app keeps no rate table and invents no price, so a first machine's sizes show size only. Showing a rate before any machine exists needs the server to send one per size (follow-up below).
  • A chat that sizes its own repository's machine (autosize) keeps that as the first, selected choice: "Automatic (sized for the chat's repository)".
  • The app-server link, whose create refuses a size for now, shows no size choice. The agent/CLI create paths are unchanged.

Risk: low-normal. The create body gains memoryMb only when the person picks a size the plan offers (memoryMb(answer:sizes:) refuses any other value); the server already accepts and validates it (resolveCreateMemory). Nothing is created by opening or dismissing the dialog.

Screenshots

Before/after from the gallery fixture route (cmux-ci capture at c600472d, debug.dialog fixtures, on cmux8s-mac-mini). The cloud-capture fixture route (HQ pr-capture-cloud.yml) was tried three times; each run was replaced in that workflow's single waiting slot by other lanes' captures before a job started (runs 41039746786, 41044431543). The head (9aafbac5) changes only limits decoding, nothing drawn.

BeforeAfter (A, shipped)
create size beforecreate size after A

The after dialog's size options as the app reports them (rpc-04.json): "2 vCPU, 4 GB memory" (selected), "4 vCPU, 8 GB memory · $0.26/h", "8 vCPU, 16 GB memory".

Variant BVariant C
create size variant Bcreate size variant C

Variants

Three layouts, gallery fixtures in debug.dialog (cloudWorkspaceSized, cloudWorkspaceSizeLine, cloudWorkspaceSizeFirst):

VariantAxisVerdict
A: size choice after the starting point (shipped)choice, read after "Start with"Picked: the starting point stays the first decision, and the size is still one click away.
B: a summary line, no choiceread-onlyShows the size but gives no way to change it before paying for it.
C: size choice firstchoice, before "Start with"Puts a money decision ahead of what the machine is for.

UI review (design/ui-review-checklist.md)

Verdict: ship it. Subtraction: one new field, no new button; the rate sits in the option label, so there is no separate cost line to keep in sync. Stable layout: the dialog's choice popup keeps its width across options. Every state: no limits or no sizes renders the dialog as before (no empty field); no rate renders size only. Worst case: 1.5 GB renders "1.5 GB memory"; locale-formatted numbers and USD to the cent, as the sidebar's cost line. Needs a human: none.

Localization

Five new keys in CmuxNextCloudUI/Resources/Cloud.xcstrings with all 21 locales the catalog carries; localization_catalog.py check --catalog on it reports only the base's existing setupScriptPlaceholder debt. vCPU counts are formatted numbers (vCPU has no plural form). Gallery fixtures are DEBUG-only.

Tests

CloudWorkspaceSetupSizeTests (new): limits decoding and its malformed-field fallback, rate matching by exact size and agreement, labels, the dialog field (smallest selected; automatic first), and the answer sent as memoryMb (stale or unknown values send nothing). Ran on the fleet with the neighbouring suites: All 6 suites passed at 8d9a05fa (fleet step beab965875f0, cmuxs-Mac-mini-4: CloudWorkspaceSetupSizeTests 10, CloudWorkspaceSetupWizardTests 14, CloudWorkspaceEnvironmentChoiceTests 7, CloudWorkspaceSetupLifecycleTests 19, CloudWorkPickTests 12, CloudMachineCostTests 13). At 9aafbac5 (lenient maxActiveVms/activeVmCount decoding, from the first review) all 5 suites passed again (fleet step ffe7ccdd722a: CloudWorkspaceSetupSizeTests 11, CloudWorkspaceSetupWizardTests 14, CloudWorkspaceSetupLifecycleTests 19, CloudMachineCostTests 13, CloudCreateWithoutAsking 7).

Follow-up

  • Server: GET /api/vm limits could carry hourlyRateUsdByMemoryMb (from vmHourlyRateUsd) and defaultMemoryMb, so a first machine's sizes show a rate too.
  • The app-server create (CloudMachineCreateFlow.named, fixed 2 vCPU / 4 GB / 16 GB) takes no size yet.

Agent session: agent session

🤖 Generated with Claude Code