fix(cloud): guard Cloud Ports actions while pending
Cloud Ports repeated activation could issue duplicate Forward, Open in Browser, and Close requests while the host call or reconciliation was unresolved. Leo's ask: “Cloud Ports has no pending guard.”
This adds per-machine, per-port pending ownership through the full host lifecycle. The originating controls are disabled with a localized busy status, conflicting actions are suppressed, and stale completions cannot update a newly selected machine. Forward now reports acceptance so a refused request keeps its draft for retry; invalid input uses native disabled semantics. Close remains pending through its ports refresh, and browser remains pending through route and tab opening.
Deferred regression coverage exercises duplicate activation, independent ports, all refusal and unsupported paths, retry keys, A→B→A completion races, session reset, refresh reconciliation, and unavailable/unselected actions. The gallery includes a deterministic ports-pending variant for visual review.
Validation: cmux-ci test -- bun test webviews/src/pages/cloud/ports.test.ts
Visual review: before = settled Cloud Ports controls; after = ports-pending gallery with the originating action disabled and busy status. Reviewer must record a Visual verdict against design/ui-review-checklist.md (Every state renders, Stable layout, focus/disabled states) with named file:line findings.
Closes manaflow-ai/cmuxterm-hq#3992
Agent session: agent session
![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
Guards Cloud Ports actions while their host call or port reconciliation is unresolved, so activating a control repeatedly can no longer issue duplicate Forward, Open in Browser, or Close requests. Each machine and port owns its pending action for the full lifecycle; the originating control is disabled with a busy status, conflicting actions on the same port are suppressed, and a completion from an earlier machine selection can no longer update the newly selected machine.
- A Forward that the host declines reports the refusal so its draft stays in the field for retry instead of being cleared; invalid input uses the native disabled semantics.
- Close stays pending through its ports refresh, and Open in Browser stays pending through route discovery and the native tab call.
- Adds regression coverage for duplicate activation, independent ports, refusal and unsupported paths, A→B→A completion races, session resets, and failed reconciliation, plus a
ports-pendinggallery variant. - Refactors
PushRegistrationto inject its register closure and covers preference reversals while a write is pending.
Closes manaflow-ai/cmuxterm-hq#3992.