Repository navigation
cloud sidebar polish: header refresh, tab switch, empty states, errors and upgrade - #17074
Conversation
…ecting row up with new workspace, hover on the ports action - while a machine connects, ports, terminals and displays have nothing current, so the tab row keeps only resources (fleet telemetry) - the connecting spinner takes new workspace's chevron column - the ports status action (refresh, set up vpn) is a quiet rounded chip with the tree's fills; its title brightens and fill deepens on hover and press
…text does nothing
the live cloud tab lists machines under the cloud machines section, and dropping one there is refused today. this fails until the fix.
…he section headers machine drags under the cloud machines section were refused because the drop only looked at top-level rows; drops and menu moves now find the machines wherever they sit. the real machine row is the drag visual: open machines close for the drag, peers spring aside against frozen geometry, and the drop lands every row from where it is on screen. mode tabs drag the same way and hand off to a native drag when pulled out of the bar, so they still open as panes. switching tabs slides one selection highlight, and an icon-only tab centers its icon. refresh moves from the bottom of the panel to the cloud machines and my devices headers.
…up pr keeps this pr to machine and tab reorder. the refresh icons in the cloud machines and my devices headers, the sliding tab highlight and the centered tab icons come back in the stacked cloud sidebar polish pr.
…light, centered tab icons refresh moves from the bottom of the cloud panel to an icon left of the cloud machines + and the my devices menu, with the same hover and a spinner that stays on screen while it runs. switching tabs slides one selection highlight while the widths re-share, and an icon-only tab centers its icon.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Important Review skippedWe couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis pull request updates Cloud tree refresh and machine reordering, right-sidebar mode-bar interactions and sizing, machine-creation upgrade actions, device terminology, and macOS CI build-root resolution. It also changes two public utility types from enums to non-instantiable structs. ChangesCanonical CI root resolution
Cloud tree refresh and machine ordering
Machine-limit upgrade action
Right sidebar mode bar
Device discoverability terminology
Static-only public API types
Priority: ➖ Normal Estimated code review effort: 5 (Critical) | ~90 minutes Change: Feature Sequence Diagram(s)sequenceDiagram
participant MachinesPanelView
participant CloudTreeOutlineView
participant CloudTreeSectionRefreshHeader
participant CloudTreeNodeActions
MachinesPanelView->>CloudTreeOutlineView: Pass cloud-machine and device refresh state
CloudTreeOutlineView->>CloudTreeSectionRefreshHeader: Render section refresh state and action
CloudTreeSectionRefreshHeader->>CloudTreeNodeActions: Invoke refresh action on click
sequenceDiagram
participant CloudTreeOutlineView
participant CloudTreeMachineReorderLift
participant CloudMachineReorderScope
participant CloudMachineReorderDrop
CloudTreeOutlineView->>CloudTreeMachineReorderLift: Begin lift for eligible machine drag
CloudTreeMachineReorderLift->>CloudMachineReorderScope: Resolve reorderable siblings
CloudTreeMachineReorderLift->>CloudTreeOutlineView: Report pointer-derived slot
CloudTreeOutlineView->>CloudMachineReorderDrop: Build drop from source and slot
CloudTreeOutlineView->>CloudTreeMachineReorderLift: Finish lift with optional commit
sequenceDiagram
participant RightSidebarModeBarTabDrag
participant RightSidebarModeBarDragController
participant RightSidebarModePaneDragSource
RightSidebarModeBarTabDrag->>RightSidebarModeBarDragController: Update drag position and target slot
RightSidebarModeBarDragController->>RightSidebarModeBarDragController: Commit reordered modes on in-bar release
RightSidebarModeBarDragController->>RightSidebarModePaneDragSource: Start native drag when a pane-capable mode leaves the bar
RightSidebarModePaneDragSource->>RightSidebarModePaneDragSource: End registration when native drag finishes
Suggested reviewers: Merge Risk: 🟡 Moderate · up to Dragging right-sidebar tabs can save them in the wrong order when the visible tabs differ from the saved list. In narrow windows, the sidebar can also grow past its configured maximum and squeeze the terminal. Fix the reorder before merging. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to Build-directory selection now uses a validated runner-identity fallback before cleanup. The checked controls limit accepted paths, but filesystem ownership and concurrent-slot isolation on persistent machines remain unconfirmed. No newly exploitable security path was demonstrated. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (5 errors, 1 inconclusive)
✅ Passed checks (19 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 44.03% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 134 functions across 49 files. (26 skipped: 3 unsupported, 1 too large, 22 over the file limit.) Full details: Cmux Swift Blocking RuntimeExplanation The PR adds a production polling loop in Resolution Remove the Full details: Cmux Algorithmic ComplexityExplanation The new drag layout filters the scalable machine list on every display-link frame. Resolution Precompute the stable peer indices and row-to-block mapping when Full details: Cmux Swift ConcurrencyExplanation The diff adds two Resolution Replace the newly added Combine state with Observation-based state. Make the affected models Full details: Cmux Swift Package BoundariesExplanation The diff adds Resolution Move the active-machine-limit error classification into the Full details: Cmux Swiftui State LayoutExplanation The diff adds new Resolution Move the new SwiftUI-owned state to ✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
CI failure attributionCI failed on
Matched log linesNot re-run automatically: Written by |
Dogfood tours of
|
review findings on the lift: - closing open machines above the source moved its row up, and that shift counted as pointer travel, so a small nudge could move the machine several slots. only the pointer's travel picks the slot now; the held row glides from under the hand into its closed slot - a drag whose end is never reported cancels once the button has been up for a moment, instead of following the pointer until the next click - the release lays out before landing, so the rows glide instead of snapping - mode bar: a late gesture change after the release no longer starts a phantom drag, and tab frames are measured without the drag offset - tests: the lifted drop gets a proposal that disagrees with the lift, and a new test opens machines above the held one
…l tab names, smoother switching and resize, upgrade at the limit - ports tab: the status text and its button start on the ports tab's edge - an empty fleet keeps a "No cloud machines yet" line, so cloud machines keeps its chevron; my devices says "No other devices yet" - the open-as-pane button stays in the bar in every tab; a tab that can't open as a pane says so in its tooltip - the right sidebar's minimum (and opening) width follows the mode tabs shown, so every tab name fits with the trailing buttons - switching tabs moves one highlight with no crossfade, and row selection is a layer so it no longer flickers while the sidebar resizes - a failed create keeps its warning icon on the name's line - a create refused at the plan's machine limit offers Upgrade Plan when a bigger plan exists - the cloud machines refresh icon spins only for a refresh someone asked for
…cons; ports layout back as it was the minimum and opening width now leave room for one full tab name (the widest, so it doesn't change as the selection moves) with every other tab at its icon, instead of every name in full. the ports tab's status alignment goes back to main's.
…reate keeps its status readable the cloud machines and my devices refresh icons move from the header's trailing buttons to right after the title and count, small and in the count's grey. the header passes clicks through to the row everywhere but the icon, so clicking the header still opens and closes the section. in a narrow sidebar a failed create's name gives way before its status.
…er other devices" - the section headers' refresh icons take the same hover fill as + and ⋯; their resting color and size are unchanged - switching tabs no longer moves a highlight across the bar. the widths ease on one short curve with no overshoot, the old tab's fill fades as it narrows and the new one's as it opens, labels are uncovered by their slot (with a soft edge) instead of re-truncating every frame, and an icon-only tab's icon glides to its leading spot - my devices and settings say "Discover other devices" (and "Stop discovering other devices"), in all 9 locales
…olish brings the ports status fix (only its button acts) and its hover chip into this build. connectingLeading moves to the machine detail tabs extension with the helpers #17070 moved there.
the row drops the "New Machine" name once the create has failed and shows only the warning and "Couldn't create machine"; the name and the reason stay in its tooltip and on the create's page.
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
…toggling the section each reload of a section header cleared the icon's clickable spot, and SwiftUI reports the icon's frame only when it moves, so after the first reload the click fell through to the row and opened or closed the section. a header now keeps the spot across reloads; it clears only when the cell is reused for another kind of row.
takes #17070's review fixes and its main merge. conflicts: ports status keeps the chip title and hides an empty message; node actions keep both newWorkspaceOnResolvedMachine and the section refreshes; the lift card fill keeps the hover tint on the struct style; the panel keeps the header refresh with main's reveal; the project file is #17070's with this branch's files wired and the deleted refresh button removed.
both header icons use the panel's one refresh (fleet and devices) instead of two new single-section actions; each icon still spins only for its own section. shorter ports status comment and denser panel arguments.
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
|
The PR advanced to
The tagged app is launched in an isolated window. Current CI still reports failures in routed checks and changed-suite app-host tests; the PR remains mergeable. — unregistered |
|
Fixed the failed-create row UI at Failed creates now render only the warning icon and Focused checks pass: Swift syntax and test wiring. Exact-head fleet build completed:
The tagged app is launched in an isolated window. — unregistered |
…074-cloud-sidebar-polish
|
Merge receipt for
Labeled |
7997c55 fix(cloud): update machine rename optimistically (manaflow-ai#17324) c9bdbd6 Fix missing Terminals tab while Cloud machine connects (manaflow-ai#17326) b047fa3 Fix Agent Hibernation never selecting live Claude Code sessions (manaflow-ai#17306) 9aefea4 cloud sidebar polish: header refresh, tab switch, empty states, errors and upgrade (manaflow-ai#17074) # Conflicts: # .github/workflows/ci-guards.yml # .github/workflows/ci.yml
* test(cloud): display health checks must not black-list websockify viewers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): stop Xvnc black-listing every viewer of additional displays Xvnc black-lists a peer after five unauthenticated connections, for a timeout that doubles each time. Every viewer of an additional display is websockify on 127.0.0.1, and the helper's 30s health check read the RFB greeting and hung up, so displays soon answered every connection with "Too many security failures". Restored panes and quiet retries then failed for minutes. Start additional displays with -UseBlacklist=0 (SecurityTypes None behind the private network gives it nothing to protect; Xvnc refuses runtime changes), complete the None handshake in the health check so it clears marks instead of adding them, and replace recovery's pgrep patterns, which pgrep's regex dialect rejected, with exact argv matching. Each helper request also runs in a plain shell now: the login shell cost about 0.7s per call and only the long-lived service needs its environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): New Display hands over a running standby display Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(cloud): keep one standby display running per desktop VM Each New Display started Xvnc, a desktop session and websockify on the guest (about 0.7s) while the user waited. The guest helper now keeps one display running outside the catalog, hands it over on create (about 0.1s) and starts the next in the background. The standby is invisible to list, does not count against capacity, and a restarted helper adopts it instead of leaking it. Opening a machine's Displays tab, or restoring it selected, runs guest discovery once so the standby is warm before the first click. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): failed or launching standby displays are never misassigned Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): serialize standby handover; retire failed standbys; rediscover after wake Concurrent creates could record a launching standby's number as a normal display; take the standby under one lock and reserve running numbers. A standby whose start failed is stopped and replaced instead of handed over or retried forever. Displays-tab discovery runs once per open and retries when a sleeping machine wakes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): align two sidebar tests with merged behavior A connecting machine keeps its Terminals tab (#17326), and every visible machine row requests a cached port scan once (#17074). Both tests still asserted the earlier behavior and failed on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): standby never takes a recorded number; failed discovery retries Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): address review: standby race and Displays discovery retry ensure_standby's locked check now also rejects a number a concurrent create recorded after the unlocked probe, so a display can never be both a catalog display and the standby. Displays-tab discovery reports completion; a failed discovery clears the request and retries, bounded to three attempts per shown machine, instead of treating task launch as done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): connecting machines keep only the Resources tab (#17139) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a stale Displays discovery cannot retry after the tab reopens Drive discovery completions explicitly instead of yielding a fixed number of times. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): bind each Displays discovery completion to its request Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#17327) * test(cloud): display health checks must not black-list websockify viewers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): stop Xvnc black-listing every viewer of additional displays Xvnc black-lists a peer after five unauthenticated connections, for a timeout that doubles each time. Every viewer of an additional display is websockify on 127.0.0.1, and the helper's 30s health check read the RFB greeting and hung up, so displays soon answered every connection with "Too many security failures". Restored panes and quiet retries then failed for minutes. Start additional displays with -UseBlacklist=0 (SecurityTypes None behind the private network gives it nothing to protect; Xvnc refuses runtime changes), complete the None handshake in the health check so it clears marks instead of adding them, and replace recovery's pgrep patterns, which pgrep's regex dialect rejected, with exact argv matching. Each helper request also runs in a plain shell now: the login shell cost about 0.7s per call and only the long-lived service needs its environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): New Display hands over a running standby display Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(cloud): keep one standby display running per desktop VM Each New Display started Xvnc, a desktop session and websockify on the guest (about 0.7s) while the user waited. The guest helper now keeps one display running outside the catalog, hands it over on create (about 0.1s) and starts the next in the background. The standby is invisible to list, does not count against capacity, and a restarted helper adopts it instead of leaking it. Opening a machine's Displays tab, or restoring it selected, runs guest discovery once so the standby is warm before the first click. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): failed or launching standby displays are never misassigned Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): serialize standby handover; retire failed standbys; rediscover after wake Concurrent creates could record a launching standby's number as a normal display; take the standby under one lock and reserve running numbers. A standby whose start failed is stopped and replaced instead of handed over or retried forever. Displays-tab discovery runs once per open and retries when a sleeping machine wakes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a closed display view is not rebuilt from a stale graph Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): load every workspace display and keep closed displays closed Displays beyond the first in a Cloud workspace layout appeared late: the catalog only learns display:N from guest discovery and drops memberships for displays it does not know, and nothing on open or restore ran discovery. Publishing a graph whose memberships name unknown displays now runs discovery once per lifecycle generation. Closed display panes came back: the membership removal is queued while reconciliation still reads the graph holding the token, so it rebuilt the pane under a new panel id and left an orphaned token that resurrected it after every close. Closing now fences the view until a fetched graph drops its token, retries a removal that has not landed, and a pane rebuilt from an orphaned token removes that token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): discover member displays on deltas; bound and scope token cleanup Membership rows arrive as projection deltas, so discovery also runs from publishDelta. Orphan cleanup skips other Macs' tokens (a no-op write that still cost a guest snapshot), removal retries stop after three attempts, and the client id is read once per close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): prune this Mac's orphaned display memberships in open workspaces A membership token whose pane is gone (an old close whose removal never landed, a crash, a pane rebuilt under a new id) showed as a duplicate display row and resurrected the display after it was closed; the reconciler treats one live pane as satisfying every token for that display, so nothing removed it. After reconciling an open Cloud workspace, remove this Mac's tokens with no live display pane, once the machine's restore has settled. Tokens of workspaces not open here, and of other Macs, are kept as their layout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): keep display discovery results across machine-list polls refreshDisplays dropped its result whenever refreshGeneration changed, and every machine-list poll bumps it, so a ~2s guest discovery that overlapped a poll was discarded: workspace displays loaded only after a later lucky discovery. Check the lifecycle generation only; the coordinator already invalidates on identity or image changes. Member-display discovery also re-checks after each attempt (bounded to three per lifecycle) because a launch-time refresh can cancel one, verified live: the first attempt's exec was cancelled after 1ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): member display listed once; silent desktop probe is not unreachable Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): list a workspace display once; stop repairing healthy desktops The sidebar listed a workspace's display twice: cloudWorkspaceResources appends a copy of a member display carrying its membership view, and localWorkspaceMembers deduped against the first copy (the pool resource, without the view), so the local pane added the display again. Display 1 sat on "Loading Cloud page" for 20s+: the desktop probe's 2s deadline never cancelled its connection, so a busy carrier held it to the proxy's 10s header timeout, and the timeout read as unreachable and ran the control plane's desktop repair (a guest exec, ~12s) on a healthy desktop. The deadline now cancels the connection, and repair runs only when the proxy or service answered with an error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a New Display pane and its membership are one sidebar row Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): build workspace rows and local-pane rows from one resource list The sidebar built workspace rows from cloudWorkspaceResources (which holds the membership copy of a member display) but deduped local panes against the pool resources alone, so a New Display pane listed its display twice. Both passes now read the same list. Adds debug.cloudtree.rows (DEBUG only), returning the Cloud sidebar's rows from the same builder, so dogfood can assert listed rows; duplicate rows were invisible to cloud tree --json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): display names parse from the daemon graph Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cloud): named displays, display tab titles, remove a display from a workspace Display panes showed noVNC's page title, and renaming a workspace display sent a synthetic view id to the daemon's tab rename, which cannot work. Displays now have one shared name per display, stored as a frontend projection beside the workspace memberships so every client follows a rename live. Rows and pane tabs show it ("Display N" until renamed); renaming the tab or the row renames the display; clearing restores the number. Workspace display rows get a hover X and a "Remove from Workspace" menu item, which close the display's pane on this Mac (the fenced membership removal) or remove this Mac's membership when no pane is open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ci): list debug.cloudtree.rows as a debug method; fold display names into the membership type The socket capability guard requires debug methods to be listed as intentionally unadvertised, and the package conventions lint rejects an all-static enum, so the display-name constants move onto CloudVMDisplayMembership. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a display row's Rename names the display The menu now routes display renames to renameDisplay (one name per display, shared by every row and pane) instead of a view's tab rename. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): align two sidebar tests with merged behavior A connecting machine keeps its Terminals tab (#17326), and every visible machine row requests a cached port scan once (#17074). Both tests still asserted the earlier behavior and failed on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): the desktop placeholder is titled Display 1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): title display panes from the click, never Desktop first The display 1 placeholder (shown before guest discovery) was titled "Desktop" and renamed "Display 1" seconds later; it now uses the same numbered title as discovery. A new display's pane is titled "Starting display…" from the click instead of "New tab", then takes the display's name when it materializes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): standby never takes a recorded number; failed discovery retries Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): address review: standby race and Displays discovery retry ensure_standby's locked check now also rejects a number a concurrent create recorded after the unlocked probe, so a display can never be both a catalog display and the standby. Displays-tab discovery reports completion; a failed discovery clears the request and retries, bounded to three attempts per shown machine, instead of treating task launch as done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): connecting machines keep only the Resources tab (#17139) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a stale Displays discovery cannot retry after the tab reopens Drive discovery completions explicitly instead of yielding a fixed number of times. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): bind each Displays discovery completion to its request Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): closing a display pane removes it for every client Drives the real pane-close path. Another client's token in the same workspace must not rebuild the pane, including when the close lands before this pane's own token reaches the graph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): closed displays stay closed across clients and instances - Closing a display pane or clicking X removes the display from that Cloud workspace for every client. The fence is keyed by machine, workspace and display, set at close from the pane's own workspace, and lifted when this Mac opens the display there again. - Pruning only touches view IDs this process recorded: stable and DEV builds on one Mac share a client ID. - A sleeping machine or an undiscovered display does not spend a removal attempt. - Tab renames run in order, and afterwards every pane shows the display's real name, so a cleared or failed rename reverts the tab. - A failed reserved pane drops its "Starting display…" title. - A reset or refused connection after the tunnel opens reads as unreachable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a desktop that resets the opened tunnel is unreachable Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): read a reset after the desktop tunnel opens as unreachable The probe's errors arrive as NWConnection.StreamError, so the previous NWError cast never matched. Only the HEAD stage maps a reset or refusal; before the tunnel opens the same error belongs to the local proxy and stays unknown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a queued close cannot undo a reopen; another client's re-add shows again Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): order display removals against reopens; release on another client's re-add - A reopen bumps the display's generation, so a close or removal retry that started before it leaves the reopened display alone. - Removal retries run on the machine's lane, ordered with the reopen's attach; the lane's enqueue is split into a resource-keyed core. - A removal records the tokens it deleted. A later graph showing any other token means another client put the display back, so the fence lifts. - Attempts are counted per closed display and only while it is fenced. - A tab rename re-applies the display's name only when it changed nothing or failed, so a successful rename no longer flashes the old name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): type the lane failure handler explicitly The ternary of closures crashed the type checker (failed to produce diagnostic). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a no-op display removal still fences older graphs; proxy reset is unknown Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): release a closed display by graph revision, not token sets A removal now reports the cursor of the graph it was computed from. A graph no newer than that predates the removal and stays fenced, including when the removal found nothing to delete. A newer graph that still shows the display means another client put it back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): fence a closed display up to its removal write's own cursor A graph published between the removal's snapshot read and its write could be newer than the snapshot yet still hold the removed tokens, releasing the fence early. The removal now reports the write receipt's cursor (the snapshot's only for a no-op), and a reply without one keeps the fence until the display is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* ci: name the failing test and whose it is in the CI attribution comment The attribution comment said "code" per job; #17074, #17232 and #17233 merged red on 10-05 with no comment naming their own failing tests, and #17232 kept a stale "CI passes" from an older head. - classify_failures.py extracts concrete failures from each failed job's failed steps (Swift Testing and XCTest file:line, compile errors, crashes, warning-budget overages, CLI help contract), dropping system and cache error: noise, and marks each yours (a file the PR changes or tests), also red on main (main's latest red full suite), or new in this PR. The comment's first line is that verdict. - The comment records its head; a newer head's CI start (workflow_run requested) or an older head's completion rewrites it to pending. A PR merged before CI finished still gets the comment. A green run whose macOS jobs never ran says so instead of "passes". - main_full_suite.py lists the red run's concrete failures in the issue with a data marker the PR attribution reads. - main_regression_attribution.py @-mentions each suspect's merger once. - guard_attribution.py's green comment says it covers only the fast guards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: harden failure attribution per review - Trust only the bot's issue body and comments for main's failure data, so a commented marker cannot turn a PR's own failure into "Not yours". - An unreadable files list or main issue falls back to none and still writes the comment. - `requested` events get their own concurrency group, so a start never replaces a pending completed report and its machine re-run. - "macOS jobs did not run" only when ci.yml's macos prerequisites (changes, Fast static checks) did not succeed, not when routing reused an admitted compile; a test pins the names to ci.yml. - Compiler paths match the PR's file by path; a bare Swift Testing file name says when several changed files share it. - No "fix forward" on a merged PR whose failures are all machine. - URL-encode the head branch in PR lookups; job names go through code(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: keep failure reports quiet when nothing new happened A main that stays red with the same failures edits its last report on the tracking issue instead of posting a new comment, so subscribers and pinged mergers are notified only when the failure set changes. A PR whose failures are all the machine's and are being re-run gets no new comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A project file merge in #17074 dropped the browser-repl resource folder from the app target, so every `cmux browser repl` call fails with "browser REPL runtime manifest is missing". Restore the folder reference and its Resources build file.
A project file merge in #17074 dropped the browser-repl resource folder from the app target, so every `cmux browser repl` call fails with "browser REPL runtime manifest is missing". Restore the folder reference and its Resources build file. Co-authored-by: Austin Wang <38676809+austinywang@users.noreply.github.com>





stacked on #17070 (base
cloud-machine-reorder), so this diff is only the polish on top of the reorder. it also merges #16812 (cloud-sidebar-connecting: only the ports status button acts, its hover chip, link tabs hidden while connecting), so those lines show here until #16812 lands.Summary
cloud tab
vm_active_limit_exceeded) offers Upgrade Plan on its page, ahead of retry and dismiss, when a bigger plan exists (not on max, team or founders)mode tabs
Showcase: tab switching and the Cloud sidebar
Testing
CloudTreeCategoryCreateActionTests(empty fleet keeps its line),CloudTreeHeaderActionsTests.sectionRefreshCloudSidebarPolishTests(machine-limit upgrade and plan ladder, one-label bar width, content minimum width)scripts/verify-local.pyandscripts/localization_catalog.py checkcleanmachines.empty.none,machines.pending.upgrade,rightSidebar.openAsPane.unavailable; changeddevices.empty.title,devices.discovery.toggle,devices.discovery.stop,devices.discovery.settingsDisabled. all 9 macOS localesChangelog
Changed: Cloud sections refresh from their headers, right sidebar tabs switch smoothly and always fit the open tab's name, and a create refused at the machine limit offers an upgrade
Tracking issue: #17110
Summary by CodeRabbit
New Features
Bug Fixes