cmux-next: a team switch keeps the new team's Cloud workspaces; B7b review follow-ups
Summary
Follow-ups from the review of #28358 (P-AUSTIN B7b, SHIP comment). #28358 has merged; the base is feat-cmux-next.
- Finding 1: a team switch no longer prunes the new team's Cloud workspaces.
isDeadcounted a Cloud machine that is not in the store's list as gone oncehasLoadedMachineswas true. During a team switch the user stays signed in, and the store keeps the previous team's list until the new team's list lands (CloudService.hasLoadedCurrentMachines). A saved workspace on a new-team machine was pruned from that old list. The rule now readshasLoadedCurrentMachines: the list of the user and team signed in now. - Finding 4: the "machine left the account" path has a test through the real
isDeadcall site. A small seam (WindowManager.cloudList, which readsCloudService.listFactsin the app) lets a test give a signed-in account with a loaded list. Tests cannot sign in. This seam is a named exception to REVIEW.md section 8 (no test seams in production source); #28398 (P-AUSTIN B7e) removes it with a fake signed-in CloudService for app tests. The new tests show that a deleted machine's workspace leaves the saved window, and that a listed machine's workspace stays with its machine. If the account-list rule (nowCloudMachineListFacts.accountListLoaded) always returned false, or if the two arguments at the call site were swapped, these tests fail. - Review of this PR, finding 2:
cloudSettledreads the current team's list too. During a team switch, a saved workspace this run never saw (or a forgotten SSH machine's or server's) read as gone before the new team's machines were listed.cloudSettlednow readshasLoadedCurrentMachines, asaccountMachineListLoadeddoes. Review finding 1 (the list landing is no observation edge, R2) needs a test that can change real CloudService state: it is in #28398. - Finding 3: one source of truth for the SSH and server id forms.
SSHHost.machineIDPrefixandServerReach.machineIDPrefixbuild theirmachineID, andMachineRegistry.isCloudMachineIDreads the same constants. No behavior change;sshAndServerMachineIDsAreNeverCloudIDschecks ids made by those types.
Finding 2 (not in this PR): design
An account or team switch still prunes the other account's Cloud workspaces: after the list of team or user B loads, the machines of A are not in it, and signing in to A again does not bring them back. This is older than #28358. Design:
- Save the account with each remote workspace's machine:
WindowRecord.workspaceMachinesgets a companion mapworkspace_accounts(workspace key to a hash of user id and team id, never the raw ids), written from aseenAccountmap thatreconcileMembershipfills next toseenMachine. isDeadcounts a Cloud machine as "left the account" only when the loaded list is for the account saved with the workspace. A workspace of another account is kept and shows as disconnected ("signed in to another account").- An older record without an account keeps today's rule.
It changes the saved window record (a new persisted field) and the display, so it is a named follow-up, not part of this PR: P-AUSTIN B7d. The display PR (stacked next) uses the same field for the space of each kept workspace if it lands first.
Owner of the area: PL-switch (window membership, plans/cmux-next/OWNERS.md "Workspace, tab and space switch"). PL-switch: this changes WindowManager+Membership.swift (isDead's account rule) only.
Testing
Commits, in this order:
975b0b42b44refactor: the seam, no behavior change.bd35653a676tests (red): finding 4 (green: coverage), finding 1 (red), finding 3 (green: guards the refactor).840bb0ab51afix: finding 1.98caa2b7e29refactor: finding 3.d6932541d92correction of the test setup ofaMachineDeletedFromTheAccountTakesItsWorkspaceOutOfTheSavedWindow. Reason: its second workspace was in no tree, soisDeadcorrectly read it as gone too (WindowManager+Membership.swift:439), andWindowStateDocument.prunedrops a window with no workspace (WindowStateDocument.swift:91). Its kept workspace is now on a machine the account's list has. Test-only.1998b6d219ered:aNeverSeenWorkspaceWaitsForTheNewTeamsList(review finding 2).b3670f4635afix:cloudSettledreadshasLoadedCurrentMachines.
Focused command: scripts/ci/package-test-lane.sh suite Packages/macOS/CmuxNext CloudWorkspaceAccountListTests,CloudWorkspaceRestoreTests (fleet cmux-ci run).
- Red
5d0392fddeeef0d9ec5ee11aatbd35653a676:aTeamSwitchDoesNotPruneTheNewTeamsWorkspaceFromTheOldTeamsListfails on its assertion (CloudWorkspaceAccountListTests.swift:64,isDeadtrue). It also shows the setup defect that commit 5 corrects. No crash, no compile error. - Compile-check
6de8c4e8ba7dcbafc81d483cat98caa2b7e29(app): passed. Commit 5 changes a test only. - Green
3e9093c4f937e414dc0b977catd6932541d92: both suites pass (aws-m4pro-6). - After the review (finding 2): compile-check, red at
1998b6d219eand green atb3670f4635a: job ids follow in a comment.
After merges of feat-cmux-next (the app-size ratchet became strict on 2026-10-30):
8. 9f937401f6a refactor: the rule moves out of CmuxNextApp into CmuxNextCloud as CloudMachineListFacts (accountListLoaded, machineLeft(listed:), settled(machinesLoaded:)); the App's remoteMachineLeft and accountMachineListLoaded are gone. Compile 92fc5fda95ea9cd3402e6c78 passed; tests 0ef90bf7d8ed762fa9a0f1eb passed.
9. db69d8ee3d1 red (review of 9f937401f6a): the current-account decision was still in CloudService.listFacts, which no test ran. thePreviousTeamsStoredListIsNotTheCurrentAccountsList builds the facts from the raw list state (hasLoadedMachines, loadedFor, account); the new init still ignores whom the list is for.
10. 6b0bb4f01f9 fix: CloudMachineListFacts decides hasLoadedMachines && loadedFor == account; CloudService.hasLoadedCurrentMachines and listFacts both read it.
11. 8b69ca5bdb3 doc only: CloudService points at the facts for the rule. Fleet red, green and compile for 9-11: job ids follow in a comment.
Observed / Not run
- Observed:
prepush-gate.py --base c63ead94875adds no violations; the fleet jobs above. - Not run: a live team switch in a signed-in app (tests cannot sign in; the seam gives the account state). Austin's-day numbers: no team switch in the scenario.
Changelog
Fixed: Switching your cmux Cloud team no longer removes the new team's Cloud workspaces from your windows before its machine list loads.
Proof
No capture: this changes which workspaces the saved window state keeps; nothing draws differently.