Cloud first send: recover inline from a paid-plan refusal (#32045)
Purpose — HQ #6236 / brief 02; cmux #32045
A free-plan Cloud first send currently reaches the native Cloud-machine cap dialog, but after the person closes it the inline chat reports only a generic failure. The draft stays in the composer; the card lacks a durable way to upgrade or run here. This patch adds that recovery path without provisioning a VM or inventing another payment flow.
Changes
- Native:
CloudStartChat.replyclassifies the existingCloudMachineCreation.proRefusalcases ascloud_plan_required. Uncertain connection, copy, and other errors retaincloud_start_failed;run_on_this_macstays untouched. The native toast and cap dialog remain responsible for the immediate refusal. - Page: the start answer retains the typed refusal in
CloudStartProgress; the already-toasted refusal avoids a duplicate composer notice. On this code only,CloudStartViewkeeps the failed machine step and displays Upgrade, Run on This Mac, Not Now. - Actions: Upgrade uses the existing
https://cmux.com/pricingroute; Run Here takes the samerunHereSend→ persistent-mode → send-once path as the native cap-card choice. Not Now dismisses the inline card while preserving the composer draft. Both New Tab and fresh chat render paths are connected. - Language: Upgrade/Run Here are the 21 existing localized labels from
CmuxNextCloudUI/Resources/Cloud.xcstrings, mirrored into a small web module. The pane's 7.4 MB catalog needs synchronization later; no new English-only labels are introduced in the UI.
Regressions added
- Swift: free-plan refusal gets
cloud_plan_required, timeout keeps generic code. - Bun: native plan refusal is treated as already toasted; start progress retains the plan code without changing generic errors; React markup includes localized Upgrade/Run Here/Not Now only for plan refusal; locale fallback and several languages are covered.
- Existing key-scoped completion behavior is retained: a late native response clears only its matching start card. Unknown-create retries still use the same durable start key. The native Run Here answer and the new inline Run Here click both retire that key through
cloudStartKeys.ended(..., run_on_this_mac)before switching to This Mac; the failed-plan → local-choice regression covers this.
Validation / limits
Reviewed source on feat-cmux-next at 7ab967bf517776c02bfbf36d479de7cf074a0a21, plus earlier HQ receipts and owning PRs. 12 files in the focused branch; GitHub compare reports 0 base conflicts at branch start. Static review and file-encoding checks were performed through the repository connector. No Mac runtime, authenticated free-plan fixture, or full Swift/Bun test run was available in this session; CI and a DEV capture should verify the end-to-end dialog → card → local send / upgrade journey before merge. This is intentionally a draft PR.
Prior work / ownership
- #32309 already fixed the signed-out first-send message; left untouched here.
- #32244 owns durable machine/create/pin identity; #33879 owns web key retention, with later completion/cache review follow-ups; this patch leaves those algorithms untouched.
- #33881 and #33906 concern provider login transfer, outside this slice.
- #32045 is the original no-machine/free-plan report; this PR targets that precise gap, while other brief-02 failures remain to be audited separately.
HQ: https://github.com/manaflow-ai/cmuxterm-hq/issues/6236 (brief 02)
![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
Gives free-plan Cloud first sends a durable recovery path after the native machine-cap dialog closes; previously the inline chat only showed a generic failure with no way to upgrade or run locally.
- Plan refusals are now classified as
cloud_plan_required; other failures keepcloud_start_failed. - The inline card shows localized Upgrade, Run on This Mac, and Not Now actions; Run Here reuses the native cap-card path, preserves the composer draft, and ends the retry key exactly as the native answer does.
- Already-toasted refusals no longer raise a duplicate composer notice.