Skip to content

cmux NEXT: New Cloud Space, a space that is one Cloud machine - #27810

Open
austinywang wants to merge 16 commits into
feat-cmux-nextfrom
feat-cmux-next-cloud-space
Open

austinywang wants to merge 16 commits into
feat-cmux-nextfrom
feat-cmux-next-cloud-space

Conversation

@austinywang

@austinywang austinywang commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

New Cloud Space: a space that is one Cloud machine. Switching to it shows that machine's own workspaces, terminals and browser tabs. Switching back shows the Mac's.

  • Create: space.newCloud (palette, File > New, a Cloud machine's right-click menu, cmux space new-cloud, MCP) picks the target machine, else the one New Cloud Workspace uses, else a new one. It wakes the machine and creates a space in one create-profile command, with follows = [machine session] and default_session_id = machine session. The daemon already had both fields; nothing in Swift sent them. Running it again for the same machine reuses that machine's space.
  • Membership: following the session moves the machine's unpinned workspaces out of Default and into the Cloud space (RoomMembership, unchanged).
  • Creates go to the machine: a create with no machine asked for (Cmd-N, the first workspace of an empty space, the CLI, the palette) resolves the space's default session in startWorkspace and runs on that daemon. A paused machine resumes on that create, and the workspace is made once it connects.
  • Mac-bound creates stay on the Mac: Finder's New Workspace Here, cmux open <dir>, a resumed or imported chat, and a create with a command keep running on the Mac.
  • Paused machine on entry: entering a space whose machine has not loaded waits for its tree. It shows the workspaces the machine already has and creates one only if there are none.
  • Unknown machine: if a space names a session that no listed machine serves, the create is refused. It is never pinned onto the Mac.
  • Resolution: a session resolves through its live daemon, else through the home registry's transport record, so a paused machine still resolves after a relaunch.

Browsers in a Cloud space are the existing remote-workspace browser tabs: the page renders on the Mac, and the machine's localhost is proxied over the link. A page that runs inside the VM is still the opt-in "Open on " path.

Follow-ups, not in this PR:

  • Set Default Session / Follow Session actions (data-model.md :411).
  • Machine health on the space switcher.
  • Delete Space and forget-session cleanup for a deleted machine.
  • An in-flight guard against two quick runs creating two spaces for one machine.

Changelog

Added: New Cloud Space, a space that shows one Cloud machine's workspaces, terminals and browser tabs, and creates new workspaces on that machine.

Testing

New CloudSpaceTests (CmuxNextAppTests) cover:

  • A Cloud space resolves to its paused machine's daemon (no identity this run).
  • A plain space resolves to the Mac.
  • A space whose machine is missing refuses.
  • Mac-bound spawns stay on the Mac.
  • follows moves the machine's workspaces out of Default.
  • create-profile sends default_session_id and follows.

Also run: SpaceSurfacesTests, ActionSurfaceParityTests, ActionSurfaceExportTests and MachineIconTests.

The base (c5af877) does not compile: JoinConsent+App.swift:222 calls the async FolderChanges.events() from sync code. #27722 and #27758 fix that, so the suites ran on side branch cloud-space-test-side: this head plus #27722 commit.

Observed / Not run

  • Observed: prepush-gate.py passes (only app-size warnings). The fleet suite results are in the comment below.
  • Not run: a live Cloud machine dogfood on a tagged build.

No capture: the only visible change is one menu and palette row, "New Cloud Space". The behavior needs a signed-in Cloud machine, which the capture minis do not have. A tagged dogfood build follows.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Adds New Cloud Space, a space that shows one Cloud machine's workspaces, terminals, and browser tabs, and creates new workspaces on that machine instead of the Mac.

New Features

  • space.newCloud (palette, File > New, a Cloud machine's right-click menu, cmux space new-cloud, MCP) picks the target machine, wakes it, and creates the space in one create-profile command with follows and default_session_id; running it again reuses the existing space. The machine's session leaves Default's follows, so its workspaces show only in the Cloud space.
  • Creates with no machine asked for (Cmd-N, an empty space's first workspace, the CLI, the palette) resolve the space's default session and run on that daemon; a paused machine resumes and the workspace is made once it connects. A create naming a machine that does not resolve is refused, never made on this Mac instead.
  • A Cloud machine whose daemon can start and attach chats opens the New Tab page first in a new workspace, built for that machine; a machine without those capabilities keeps the terminal.
  • Mac-bound creates — Finder's New Workspace Here, cmux open <dir>, a resumed or imported chat, a create with a command or browser profile, first launch's workspace, and a script's create without --session — stay on the Mac.
  • Entering a space whose machine has not loaded waits for its tree, shows the machine's existing workspaces, and creates one only if there are none; a failed wake or load returns the window to the space it can show now (its previous, else default, adopting its workspaces from a closed window before making one), never into another unloaded space's wait.
  • Switching to a space naming a session no listed machine serves is refused and the window keeps its space; sessions resolve through their live daemon, else the home registry's transport record, so a paused machine still resolves after a relaunch.

Written for commit c79ce70. Summary will update on new commits.

View guided diff Turn on auto-fix

austinywang and others added 3 commits October 29, 2026 18:19
A Cloud space follows one Cloud machine's session and makes it the space's
default session, in one create-profile command. Switching to it shows the
machine's own workspaces, terminals and browser tabs; New Workspace, Cmd-N,
an empty space's first workspace, the CLI and the palette create on the
machine (startWorkspace resolves the space's default session). A paused
machine resumes on that create, and the workspace is made once it connects,
never on the Mac instead. The session resolves through the live daemon, else
the home registry's transport record, so a paused machine still resolves
after a relaunch.

Entry points: palette, File > New, a Cloud machine's right-click menu,
`cmux space new-cloud` and MCP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nc in an autoclosure)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ace shows its workspaces; an unknown machine refuses

Review findings:
- A create carrying this Mac's folder, command, chat or session (Finder's New
  Workspace Here, cmux open <dir>, a resumed or imported chat) stays on the Mac.
- Cmd-N decides its page and key hold on the daemon the space creates on.
- Entering a space whose machine has not loaded waits for its tree and adopts
  the workspaces it already has; only an empty machine gets a new one.
- A space naming a session no listed machine serves refuses the create instead
  of pinning a Mac workspace into it (MachineRegistry.SpaceCreation).
- The session resolves through the shared transport reader (any machine kind);
  the space follows the identity's lowercased session id; a machine the person
  disconnected fails at once instead of waiting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austinywang austinywang added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 30, 2026
@austinywang

Copy link
Copy Markdown
Contributor Author

Review (correctness subagent) at 3e95edb, then fixed in eefffde:

  1. Fixed. Every nil-daemon create took the space's machine, so Mac-bound creates landed on the VM: Finder's New Workspace Here, cmux open <dir>, resumed or imported chats, and sandbox notices. → spaceCreation keeps a spawn with cwd, command, firstChat or firstSession on the Mac. Test: aCreateCarryingSomethingOfThisMacStaysOnTheMacInACloudSpace.
  2. Fixed. Cmd-N decided its New Tab page and key hold on the local daemon while creating on the Cloud one. → It now uses the space's daemon (AppActions+Workspaces.swift).
  3. Fixed. Entering a paused Cloud space added a new empty workspace each time, because the machine's tree had not loaded. → enterLoaded wakes the machine, waits for the tree, adopts the workspaces it already has, and creates one only if there are none.
  4. Fixed. An unresolved session (machine list not loaded yet, or the account signed out) fell back to the Mac and pinned a Mac workspace into the Cloud space. → MachineRegistry.SpaceCreation.unresolved refuses. Test: aPlainSpaceCreatesOnTheMacAndAnUnknownMachineIsRefusedNotMadeOnTheMac.
  5. Fixed. A duplicate transport parser → reuses MachineRegistry.machineID(transport:), so SSH-machine sessions resolve too.
  6. Fixed. The space follows identity.sessionID ?? registryID.lowercased(), the same form daemon(session:) matches.
  7. Open (follow-up). Two quick runs can create two spaces for one machine before the mirror arrives.
  8. Left to autofix. The generated exports (action-surfaces.json, the docs JSON) come from PR autofix. They are never hand-edited.
  9. Fixed. A machine the person disconnected fails at once instead of waiting 180 s.

@blacksmith-sh

blacksmith-sh Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Found 8 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmp689a7vu3/summary-missing.json (live):/summary-missing.json (live): View Logs
/tmp/tmp_pu1lxpr/summary-fail.json (live):/summary-fail.json (live): View Logs
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows/
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows
View Logs
[new_workspaces] the window draws no frames/[new_workspaces] the window draws no frames View Logs
[tabs] debug.tab_switch timed 0 of 40 switches/
[tabs] debug.tab_switch timed 0 of 40 switches
View Logs
[tabs] the window draws no frames/[tabs] the window draws no frames View Logs
imported web bundle inputs are not routed:/imported web bundle inputs are not routed: View Logs
test_every_part_of_bun_run_check_runs_in_the_warn_branch (main.WebviewsWarnSplitCov
ersCheck.test_every_part_of_bun_run_check_runs_in_the_warn_branch)/
test_every_part_of_bun_run_check_runs_in_the_warn_branch (main.WebviewsWarnSplitCov
ersCheck.test_every_part_of_bun_run_check_runs_in_the_warn_branch)
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 3f3693f: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr27810 -f ref=feat-cmux-next-cloud-space -f platform=macos -f backend_mode=local

red: cmux-next checks (god files, concurrency, crash safety, l10n); NEW: red check: Web bundle key inputs (+1 more) is not red on the base 735d16b; do not merge until green; see lane-rules

Default changes

This pull request changes 9 defaults at c79ce70dadc9 (what everyone gets without configuring anything):

  • tunable-source.CmuxNextActions/ActionSurfaceCatalog+Menus.swift:layoutPlacements: "8b64c25d164605360d891a4f9a0f73037775e0c507ce178e0c33f90ddd53ebb2" -> "d21a719a557ee549fc2637d7967de343ee42cf3023d153615e0b28074ea4f7c2"
  • tunable-source.CmuxNextApp/AppActions+Workspaces.swift:newWorkspace: "5b8b25bacaa99050c87588161a2cb8c4357387418aba2b2d1da560d8f8afe2b9" -> "42964d2bcfd3c004878c512d556ba2c3cf190debd8f94b951f2072948cf79462"
  • tunable-source.CmuxNextApp/AppActions.swift:bind: "2501c9f6727f1604de3be831cb601d48f028edcfdfac546eb1785d0d0ded5c5b" -> "3cc18cb24be7ce2782e6036989819fed4d1f598ac90b7f7ecb88629290536d6c"
  • tunable-source.CmuxNextApp/Handlers/CloudHandlers+Account.swift:workspaceMachine: (none) -> "d94e5c4917ef1aefc2bf9cd2d0830f9797cdcca98c91afb328cda19bfa246166"
  • tunable-source.CmuxNextApp/Handlers/RoomHandlers.swift:bindCloudSpace: (none) -> "d92595eca1253d7ac2ccfcad49d050d8b63dade8e1b743a2b1093eb37f8b9e40"
  • tunable-source.CmuxNextApp/Handlers/RoomHandlers.swift:newCloudSpace: (none) -> "2b271bcabcda8ed45f8a1c14533259fd9bea88a9559308c3932be87581f3f750"
  • tunable-source.CmuxNextApp/Handlers/RoomHandlers.swift:space: (none) -> "6c3341c2d1ea908b9778b896418e340ba87abb7d339606960442be6e65120e04"
  • tunable-source.CmuxNextApp/NewTabPage.swift:page: "da208c345141697f0fefba747ad0b334627bbedc03a723e9c953f4ec660a1823" -> "5f4f20ae3f52d780739a0c31f7a39291aad1bb62a8a81e7ed9b94b7e59adbf7c"
  • tunable-source.CmuxNextDaemonService/CloudStrings.swift:notConnected: (none) -> "4e49259e0f66db03d30841c61b2586faeddaff2d1a38fb78859ca7cdf6f15dc7"

Needs a review. It passes with an independent review receipt on this commit: a comment or review whose first line is Review (<reviewer>): SHIP at c79ce70dadc94bf71312f3ed68b1936b32c1edb2, from a member, not a self-review (the newest verdict naming the head decides; a later BLOCK beats an earlier SHIP), or with an approving review on this commit from @lawrencecchen or @teamleaderleo (the call owners in .github/defaults-call-owners.json, never the author). A receipt posted as a review re-runs this check; after a plain comment, re-run it. The needs a call label alone does not pass, and a new push needs a new receipt.

Regenerate the snapshot with python3 scripts/ci/defaults_guard.py snapshot.

…les and scripts stay on the Mac

Re-review findings:
- Switching into a space whose machine is not listed is refused; the window
  keeps its space (regression test: switchingIntoASpaceWhoseMachineIsUnknown...).
- A wake or load that fails returns the window to the space it came from, and
  says why only while the window still shows the Cloud space.
- First launch's workspace, a file opened from this Mac, a browser-profile
  create and a script's create without --session stay on this Mac.
- A resume failure from an earlier attempt no longer ends a new wait at once.
- The space follows the tree's registry_id, as membership qualifies it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Third review: the switch back to the previous space could start that space's
own wait (another unloaded Cloud space) and loop between the two. fallBack
goes to the previous space only when the window shows something there now,
else to default, else to default with a new workspace on this Mac.
Regression test: aFailedCloudSpaceFallsBackToDefaultNotIntoAnotherWait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austinywang

Copy link
Copy Markdown
Contributor Author

Re-review (fresh correctness subagent) at eefffde: FIX. Resolved in 6512ca0:

  1. Fixed. A failed wake or load left the window empty in the Cloud space. → It returns the window to the space it came from, and refuses only while the window still shows the Cloud space.
  2. Fixed. Switching into a .unresolved space produced an empty window. → The switch is refused and the window keeps its space. Test: switchingIntoASpaceWhoseMachineIsUnknownKeepsTheWindowWhereItIs.
  3. Fixed. These now stay on the Mac: the first-launch workspace, a file opened from this Mac, a browser-profile create, and non-person newWorkspace runs without --session or a space. Test: aBrowserProfileCreateStaysOnTheMac. Behavior note: CLI, MCP, phone and page creates follow the Cloud space only when they name it or the machine. Keyboard, palette, menu and sidebar + follow it.
  4. Fixed. A stale resumeFailure is cleared before a new wait.
  5. Fixed. The space follows the raw registry_id, as membership qualifies it.
    6, 7. Open (follow-up). Duplicate spaces from two quick runs, and a double enterLoaded on a quick switch away and back.
  6. Partly. Added switch and fallback regression tests. A test for the enterLoaded wake path with a fake machine is a follow-up.

Third review (fresh subagent) at 6512ca0: FIX. Resolved in 5aa55b4:

  1. Fixed. The switch back could start the previous Cloud space's own wait and loop between two unloaded Cloud spaces. → fallBack goes to the previous space only when the window shows something there now, else to Default, else to Default with a new Mac workspace. It never waits. Test: aFailedCloudSpaceFallsBackToDefaultNotIntoAnotherWait.
  2. Noted. Clearing resumeFailure can make a concurrent second waiter wait the full bound instead of failing fast. Low risk; left as is.

…a closed window before making one

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austinywang

Copy link
Copy Markdown
Contributor Author

Receipts for head 0e379d2cf1b9369188f3070de81fa1e3dac81950 (author: Austin Wang's agent, Claude Opus 5.5; base c5af877).

  • Fleet suites: CMUX_SWIFT_TEST_SUITES = CloudSpaceTests, SpaceSurfacesTests, ActionSurfaceParityTests, ActionSurfaceExportTests, MachineIconTests, SidebarSpacePagingTests, ProfileBarDotReorderTests, MoveToNewWindowTests. Result: 8 passed, 0 failed (step cloud-space-suites-c-aa99a2b302bc, run by cmux-ci run --class light --script scripts/ci/run-swift-testing-suites.sh). It ran on side branch cloud-space-test-side = this head + fix(cmux-next): await FolderChanges.events() in the machine consent watch (base Release compile red) #27722 commit, because the base does not compile on its own (JoinConsent+App.swift:222). The same 8 also passed at 5aa55b4.
  • Reviews: 4 rounds by fresh correctness subagents. The last one, on 5aa55b4, returned SHIP. Its one low finding (the fallback should adopt Default's workspaces from a closed window before making one) is applied in this head.
  • Dogfood build: tag 27810-new-cloud-space, built at aa99a2b (this head + fix(cmux-next): await FolderChanges.events() in the machine consent watch (base Release compile red) #27722) with the direct dev backend. Iteration build only, not exact-head evidence.
  • Not run: a live end-to-end check against a signed-in Cloud machine.

…its chats on the machine

A Cloud machine whose daemon starts and attaches chats (agent-session-start-v1,
agent-session-attach-v1) now opens the New Tab page first, as this Mac does,
instead of a terminal. Its first page is built for that machine (its folders,
no Mac home or projects, not warmed in this Mac's acpmux), and its tab is a
registry: record, so the first send starts the chat in the machine's acpmux
(the same path Cmd-T in a remote pane already takes). A machine without those
capabilities keeps the terminal. Cmd-N holds typed keys only for a connected
machine, never through a resume.

Tests: aCloudWorkspacesFirstPageShowsTheMachineNotThisMac,
aCloudMachineThatCannotStartChatsOpensATerminal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Cloud space gets no icon: it shows its color dot like every space. The
home daemon makes default follow each session it first records, so the
machine's workspaces also showed in Default; New Cloud Space now takes the
machine's session out of default's follows (also for an existing Cloud space).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextApp/AgentTabs+Wiring.swift
#	Packages/macOS/CmuxNext/Sources/CmuxNextApp/NewTabPage.swift
@austinywang

Copy link
Copy Markdown
Contributor Author

Sweep fix: merge conflict with feat-cmux-next (690 behind) in AgentTabs+Wiring.swift and NewTabPage.swift. Resolution keeps both: firstPageNewTab uses the base's locateTabOnAnyMachine and passes this PR's machine. NewTabPage.page takes both this PR's machine: and the base's paletteActions: (still passed to omnibar), and NewTabPageMachine gets machine. Every firstPageNewTab caller and test closure takes three arguments. New head 7eb1ca8. The head needs CI and a review receipt before relabel.

@lawrencecchen

Copy link
Copy Markdown
Contributor

Conflict wave: I resolved the merge with feat-cmux-next 7f4c8b4 locally, but I did not push it. The prepush arch ratchet refuses it: app_lines CmuxNextApp +247. These are this PR's own App lines: RoomHandlers +76, WindowManager+Profiles +53, MachineRegistry +39, WorkspaceSpawn +30, CloudMachineSession +21 and others. The resolution kept both sides: space.bringHere and space.newCloud in the exemptions, feat's paletteActions: false with this PR's machine:, and this PR's Cloud-space routing in startWorkspace without followClassicMode, which feat removed with Classic Mode in 74d73a3.

Question for the author: where should the New Cloud Space code live? The answer is one of these two: move the space/machine logic out of CmuxNextApp into a package (the ratchet's rule) or a reviewed arch-ratchet exception for +247 with an owner and an expiry. The local merge is commit d4ed84ae310 in the resolver's worktree. I can push it as soon as the App lines have a place.

…ext-cloud-space

Kept both sides: space.bringHere (feat) and space.newCloud (this PR) in the
exemptions; feat's NewTabPage paletteActions: false with this PR's machine;
feat's sourceWindow key hold (the window the create came from) with this PR's
rule that only a connected space machine holds the keys; this PR's Cloud space
routing in startWorkspace, without followClassicMode, which feat removed with
Classic Mode (74d73a3).
…, never made on this Mac (red)

Review of the merge bf39da2 found this in the PR's routing: with no space and
a non-person origin, an unresolved `machine` fell back to this Mac's connected
daemon and passed the not-connected check, so `cmux new-workspace --session <bad>
--command ...` ran the command on the person's Mac. A fake local daemon (with
profiles-v1, so the old path pins and creates) records the commands it gets; the
test awaits the handler's captured work, then expects the machineNotConnected
refusal and no create-workspace. Red here.
…ever made on this Mac (fail closed)

The not-connected check reads the daemon the request named (`asked`), not the
fallback daemon a script's create without a machine goes to.
…ace (owner austinywang, expiry 2026-11-08)

Lead decision 2026-11-01 07:30Z. Receipt: the independent review of the code
head 5d39eb0. Austin moves the cloud-space and window-space logic into
packages before the expiry.
@lawrencecchen

Copy link
Copy Markdown
Contributor

@austinywang, the lead decided at 07:30Z: this PR lands with a reviewed arch-ratchet app_lines exception of +248. You own moving the cloud-space and window-space logic into packages before the exception expires on 2026-11-08. The exception is in plans/cmux-next/arch-ratchet.json.

What I pushed (the work and the credit stay yours):

  • bf39da2: merge of feat. Kept both sides: space.bringHere and space.newCloud in the exemptions; feat's paletteActions: false with your machine:; feat's sourceWindow key hold with your rule that only a connected space machine holds the keys. followClassicMode is gone, as feat removed Classic Mode.
  • 64d4b88 (red) and 5d39eb0 (fix): the independent review found a fail-open path. A script's create that named a machine this app cannot resolve fell back to this Mac's connected daemon, so cmux new-workspace --session <bad> --command ... ran the command on your Mac. The check now reads the named machine (asked?.connection == nil). The test aScriptsCreateOnAnUnknownMachineIsRefusedNotMadeOnTheMac uses a fake local daemon.
  • 27b2e57: the exception (receipt: SHIP at 5d39eb0).

Still open from the review (non-blocking, for your move): a Cmd-N while a Cloud space's machine wakes can make two workspaces (WindowManager+Profiles.swift:80), and the doc references to creationDaemon are stale (it is creation(in:)). Fleet red, green and compile results follow in a comment.

…ions and requires that New Workspace ran

The test's services never bound AppActions, so perform("newTab") returned false and captured no work on
both the red and the green (CloudSpaceTests.swift:206, work.count 0). It now binds them, requires the run,
and has a one-minute limit so the red's create on this Mac cannot hang the suite.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 issues found across 26 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Packages/macOS/CmuxNext/Sources/CmuxNextActions/ActionSurfaceCatalog+Exemptions.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextActions/ActionSurfaceCatalog+Exemptions.swift:53">
P2: This adds `space.newCloud` to `cliNamed`, which changes the CLI surface, but `plans/cmux-next/action-surfaces.json` was not regenerated. The export check in `ActionSurfaceParityTests.exportIsFresh` will fail in CI with "action-surfaces.json is stale". Regenerate with `CMUX_UPDATE_ACTION_SURFACES=1 swift test --filter ActionSurfaceParityTests`, and refresh `web/data/next-docs/cli-reference.generated.json` as well if the docs generator requires it.</violation>
</file>

<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/RoomHandlers.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/RoomHandlers.swift:372">
P2: Concurrent `space.newCloud` runs can create duplicate spaces for one machine because both can pass this lookup before either profile is mirrored. Serialize creation per session or make the daemon request idempotent by session.</violation>

<violation number="2" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/RoomHandlers.swift:388">
P1: This whole-list write is built from the app mirror, which can lag the daemon. Sessions the daemon added to `default` since the last `personal-changed` would be dropped. Fetch `listPersonal()` from the daemon first, as `RoomDefaultsUpdates` does, and subtract from that list.</violation>
</file>

<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Onboarding/ExternalOpenController.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Onboarding/ExternalOpenController.swift:151">
P2: The new comment says only files are Mac-bound, but `createWorkspaceForPending` runs for every route `perform` handles: browser tabs, ssh, Handoff, and deep links with no focused pane. This line now pins all of them to the Mac, even when the window shows a Cloud space, and that is not in the PR description. Either reword the comment to cover every external open, or pin only the file route if that is the intent.</violation>
</file>

<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/WorkspaceSpawn.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/WorkspaceSpawn.swift:26">
P3: The `.unresolved` refusal reuses the 'not connected yet, wait or resume' message, which misleads when the session is missing from the fleet (signed out or machine deleted). Use a dedicated message for an unresolved space.</violation>

<violation number="2" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/WorkspaceSpawn.swift:50">
P2: When no window profile resolves, `spaceCreation` returns `.mac` while `WorkspaceSpawner` still births the workspace in `.defaultProfile`. Fall back to `.defaultProfile` here so both use the same room and the Cloud default session is honored.</violation>
</file>

<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/MachineRegistry.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/MachineRegistry.swift:268">
P2: A profile missing from the local store makes `creation(in:)` return `.mac`, so a create in a Cloud space lands on this Mac. The PR says unresolved sessions must be refused, not sent to the Mac. Return `.unresolved` when the profile is absent, and keep `.mac` only for a profile that exists with no default session.</violation>
</file>

<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudMachineSession.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudMachineSession.swift:555">
P2: A disconnect during this wait leaves `connectedForSpace()` blocked for the full 180-second timeout because the condition never observes `disconnected`. Include the terminal disconnect state in the wait and reject it after the wait returns.</violation>
</file>

<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/WindowManager+Profiles.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/WindowManager+Profiles.swift:74">
P2: This continuation can mutate a window after the user closes it. Require the window to still be registered and open before adopting workspaces or creating one.</violation>
</file>

<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/AppActions+Workspaces.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/AppActions+Workspaces.swift:166">
P3: For an unresolved space, `spaceDaemon` falls back to `services.daemon`, so the New Tab page check and the key hold use the Mac even though this create is refused. The comment above says these are "that daemon's", which does not hold in this case. Use an optional `spaceDaemon` and skip the page and key decision when the space resolves to nothing.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

// workspaces would show on the Mac's space too: the machine is the Cloud space's alone.
let defaultFollows = local.store.personal.follows[.defaultProfile] ?? []
if let followed = defaultFollows.first(where: { $0.lowercased() == session.lowercased() }) {
_ = try await connection.request(SetProfileFollowsRequest(profile: .defaultProfile,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This whole-list write is built from the app mirror, which can lag the daemon. Sessions the daemon added to default since the last personal-changed would be dropped. Fetch listPersonal() from the daemon first, as RoomDefaultsUpdates does, and subtract from that list.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/RoomHandlers.swift, line 388:

<comment>This whole-list write is built from the app mirror, which can lag the daemon. Sessions the daemon added to `default` since the last `personal-changed` would be dropped. Fetch `listPersonal()` from the daemon first, as `RoomDefaultsUpdates` does, and subtract from that list.</comment>

<file context>
@@ -315,3 +316,78 @@ final class RoomDefaultsUpdates {
+        // workspaces would show on the Mac's space too: the machine is the Cloud space's alone.
+        let defaultFollows = local.store.personal.follows[.defaultProfile] ?? []
+        if let followed = defaultFollows.first(where: { $0.lowercased() == session.lowercased() }) {
+            _ = try await connection.request(SetProfileFollowsRequest(profile: .defaultProfile,
+                                                                      sessionIDs: defaultFollows.subtracting([followed]).sorted()))
+        }
</file context>

"workspaceGroup.moveToSpace", "space.setTheme", "space.clearTheme",
// Every space action is a verb (Lawrence 14223: no exceptions): the space keys and the color family too.
"space.next", "space.previous", "space.selectByNumber", "space.openInNewWindow", "space.bringHere", "space.newIncognito", "space.color.grey", "space.color.blue",
"space.next", "space.previous", "space.selectByNumber", "space.openInNewWindow", "space.bringHere", "space.newIncognito", "space.newCloud", "space.color.grey", "space.color.blue",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This adds space.newCloud to cliNamed, which changes the CLI surface, but plans/cmux-next/action-surfaces.json was not regenerated. The export check in ActionSurfaceParityTests.exportIsFresh will fail in CI with "action-surfaces.json is stale". Regenerate with CMUX_UPDATE_ACTION_SURFACES=1 swift test --filter ActionSurfaceParityTests, and refresh web/data/next-docs/cli-reference.generated.json as well if the docs generator requires it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextActions/ActionSurfaceCatalog+Exemptions.swift, line 53:

<comment>This adds `space.newCloud` to `cliNamed`, which changes the CLI surface, but `plans/cmux-next/action-surfaces.json` was not regenerated. The export check in `ActionSurfaceParityTests.exportIsFresh` will fail in CI with "action-surfaces.json is stale". Regenerate with `CMUX_UPDATE_ACTION_SURFACES=1 swift test --filter ActionSurfaceParityTests`, and refresh `web/data/next-docs/cli-reference.generated.json` as well if the docs generator requires it.</comment>

<file context>
@@ -50,7 +50,7 @@ nonisolated extension ActionSurfaceCatalog {
         "workspaceGroup.moveToSpace", "space.setTheme", "space.clearTheme",
         // Every space action is a verb (Lawrence 14223: no exceptions): the space keys and the color family too.
-        "space.next", "space.previous", "space.selectByNumber", "space.openInNewWindow", "space.bringHere", "space.newIncognito", "space.color.grey", "space.color.blue",
+        "space.next", "space.previous", "space.selectByNumber", "space.openInNewWindow", "space.bringHere", "space.newIncognito", "space.newCloud", "space.color.grey", "space.color.blue",
         "space.color.red", "space.color.yellow", "space.color.green", "space.color.pink", "space.color.purple",
         "space.color.cyan", "space.color.orange",
</file context>

Task { [weak self] in
do {
_ = try await windows.createWorkspace(WorkspaceSpawn(), into: target)
// The file was opened from this Mac: its workspace is this Mac's, whatever space the window shows.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new comment says only files are Mac-bound, but createWorkspaceForPending runs for every route perform handles: browser tabs, ssh, Handoff, and deep links with no focused pane. This line now pins all of them to the Mac, even when the window shows a Cloud space, and that is not in the PR description. Either reword the comment to cover every external open, or pin only the file route if that is the intent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Onboarding/ExternalOpenController.swift, line 151:

<comment>The new comment says only files are Mac-bound, but `createWorkspaceForPending` runs for every route `perform` handles: browser tabs, ssh, Handoff, and deep links with no focused pane. This line now pins all of them to the Mac, even when the window shows a Cloud space, and that is not in the PR description. Either reword the comment to cover every external open, or pin only the file route if that is the intent.</comment>

<file context>
@@ -148,7 +148,8 @@ final class ExternalOpenController {
         Task { [weak self] in
             do {
-                _ = try await windows.createWorkspace(WorkspaceSpawn(), into: target)
+                // The file was opened from this Mac: its workspace is this Mac's, whatever space the window shows.
+                _ = try await windows.createWorkspace(WorkspaceSpawn(), on: windows.services.daemon, into: target)
             } catch {
</file context>
Suggested change
// The file was opened from this Mac: its workspace is this Mac's, whatever space the window shows.
// Opened from this Mac (a file, link or tab from another app): its workspace is this Mac's, whatever space the window shows.

Comment on lines +50 to +51
guard let profile = spawn.profile ?? windowID.flatMap({ states[$0]?.profileID }) ?? active?.state.profileID else { return .mac }
return services.machines.creation(in: profile)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When no window profile resolves, spaceCreation returns .mac while WorkspaceSpawner still births the workspace in .defaultProfile. Fall back to .defaultProfile here so both use the same room and the Cloud default session is honored.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/WorkspaceSpawn.swift, line 50:

<comment>When no window profile resolves, `spaceCreation` returns `.mac` while `WorkspaceSpawner` still births the workspace in `.defaultProfile`. Fall back to `.defaultProfile` here so both use the same room and the Cloud default session is honored.</comment>

<file context>
@@ -19,7 +20,35 @@ extension WindowManager {
+        guard spawn.cwd == nil, spawn.command == nil, spawn.firstChat == nil, spawn.firstSession == nil, spawn.browserProfile == nil else {
+            return .mac
+        }
+        guard let profile = spawn.profile ?? windowID.flatMap({ states[$0]?.profileID }) ?? active?.state.profileID else { return .mac }
+        return services.machines.creation(in: profile)
     }
</file context>
Suggested change
guard let profile = spawn.profile ?? windowID.flatMap({ states[$0]?.profileID }) ?? active?.state.profileID else { return .mac }
return services.machines.creation(in: profile)
let profile = spawn.profile ?? windowID.flatMap({ states[$0]?.profileID }) ?? active?.state.profileID ?? .defaultProfile
return services.machines.creation(in: profile)

Comment on lines +555 to +556
self?.resumeFailure != nil || daemon.connection != nil
}) else { throw DaemonError.notConnected }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: A disconnect during this wait leaves connectedForSpace() blocked for the full 180-second timeout because the condition never observes disconnected. Include the terminal disconnect state in the wait and reject it after the wait returns.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudMachineSession.swift, line 555:

<comment>A disconnect during this wait leaves `connectedForSpace()` blocked for the full 180-second timeout because the condition never observes `disconnected`. Include the terminal disconnect state in the wait and reject it after the wait returns.</comment>

<file context>
@@ -536,3 +537,23 @@ final class CloudMachineSession {
+        }
+        let daemon = daemon
+        guard await CloudMove.holds(within: .seconds(180), until: { [weak self] in
+            self?.resumeFailure != nil || daemon.connection != nil
+        }) else { throw DaemonError.notConnected }
+        if let failure = resumeFailure { throw ActionFailure(message: failure) }
</file context>
Suggested change
self?.resumeFailure != nil || daemon.connection != nil
}) else { throw DaemonError.notConnected }
self?.disconnected == true || self?.resumeFailure != nil || daemon.connection != nil
}), !disconnected else { throw ActionFailure(message: CloudStrings.notConnected) }

} catch {
failure = RefusalStrings.describe(error)
}
guard let state = states[windowID], state.profileID == profile else { return }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This continuation can mutate a window after the user closes it. Require the window to still be registered and open before adopting workspaces or creating one.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Windows/WindowManager+Profiles.swift, line 74:

<comment>This continuation can mutate a window after the user closes it. Require the window to still be registered and open before adopting workspaces or creating one.</comment>

<file context>
@@ -49,6 +60,48 @@ extension WindowManager {
+        } catch {
+            failure = RefusalStrings.describe(error)
+        }
+        guard let state = states[windowID], state.profileID == profile else { return }
+        if let failure {
+            fallBack(preferring: previous, in: state)
</file context>
Suggested change
guard let state = states[windowID], state.profileID == profile else { return }
guard let state = states[windowID], registry.value.window(windowID)?.isOpen == true, state.profileID == profile else { return }

if daemon == nil {
switch spaceCreation(spawn, into: windowID) {
case .mac: break
case .unresolved: return { throw ActionFailure(message: CloudStrings.notConnected) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The .unresolved refusal reuses the 'not connected yet, wait or resume' message, which misleads when the session is missing from the fleet (signed out or machine deleted). Use a dedicated message for an unresolved space.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/WorkspaceSpawn.swift, line 26:

<comment>The `.unresolved` refusal reuses the 'not connected yet, wait or resume' message, which misleads when the session is missing from the fleet (signed out or machine deleted). Use a dedicated message for an unresolved space.</comment>

<file context>
@@ -19,7 +20,35 @@ extension WindowManager {
+        if daemon == nil {
+            switch spaceCreation(spawn, into: windowID) {
+            case .mac: break
+            case .unresolved: return { throw ActionFailure(message: CloudStrings.notConnected) }
+            case let .machine(space):
+                // A Cloud space creates on its machine. A paused machine or an ended link wakes on this create,
</file context>
Suggested change
case .unresolved: return { throw ActionFailure(message: CloudStrings.notConnected) }
case .unresolved: return { throw ActionFailure(message: CloudStrings.spaceUnresolved) }

}
// With no machine asked for, the create goes where the space creates (a Cloud space's machine), so the page
// decision and the key hold below are that daemon's.
let spaceDaemon = windows.spaceCreation(spawn, into: target).daemon ?? services.daemon

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: For an unresolved space, spaceDaemon falls back to services.daemon, so the New Tab page check and the key hold use the Mac even though this create is refused. The comment above says these are "that daemon's", which does not hold in this case. Use an optional spaceDaemon and skip the page and key decision when the space resolves to nothing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/AppActions+Workspaces.swift, line 166:

<comment>For an unresolved space, `spaceDaemon` falls back to `services.daemon`, so the New Tab page check and the key hold use the Mac even though this create is refused. The comment above says these are "that daemon's", which does not hold in this case. Use an optional `spaceDaemon` and skip the page and key decision when the space resolves to nothing.</comment>

<file context>
@@ -152,17 +152,26 @@ extension AppActions {
         }
+        // With no machine asked for, the create goes where the space creates (a Cloud space's machine), so the page
+        // decision and the key hold below are that daemon's.
+        let spaceDaemon = windows.spaceCreation(spawn, into: target).daemon ?? services.daemon
         // A person's Cmd-N onto the New Tab page in the window they type in: every key from this
         // moment waits for the new page's field, in order (cx-kql5h), not for the workspace to show.
</file context>

@lawrencecchen

Copy link
Copy Markdown
Contributor

Review (claude-review-subagent-cw): SHIP at a5a5f98

Correctness first. I read the code; I did not compile or run tests on this machine. The fleet results below are the author's runs.

  1. The fail-closed fix is correct (AppActions+Workspaces.swift:159-160). The check reads asked?.connection == nil, so newTab refuses a named machine that does not resolve (an unknown id, or Cloud off) and a named machine that is not connected. A script's create with no space never goes to this Mac's daemon in place of the named machine.
  2. The regression test CloudSpaceTests.aScriptsCreateOnAnUnknownMachineIsRefusedNotMadeOnTheMac is now valid. It binds the real actions (AppActions.bind(services)) and requires performed. It also requires exactly one captured work and waits for that work to end, with no yields and a 1-minute limit. It compares the failure with the constant WorkspaceVerbStrings.machineNotConnected. On the old check, the create goes to the local fake daemon and fails with a different message. Fleet red 6c03c3ef0f1124a66c55355d at de597d8 (the fix reverted) failed at CloudSpaceTests.swift:216 ("malformed cmux-tui response ... 'changed'" from the local daemon). Fleet green 83030322f667e3783ecf2155 at this head passed (12/12 CloudSpaceTests). Compile ce72737fb549b089221571f8 passed.
  3. The exception entry in plans/cmux-next/arch-ratchet.json is correct and complete. It has date, row app_lines, allow 248, why, author, owner austinywang, a receipt from a reviewer who is not the author, and expiry 2026-11-08. arch-ratchet.py over 6eb6839..5d39eb0 measures exactly +248 app_lines. Over 6eb6839..a5a5f98 it reports "no architecture count rises".
  4. The merge bf39da2 keeps both sides: space.bringHere + space.newCloud; paletteActions: false + machine:; the sourceWindow key hold + "only a connected space machine holds keys"; and the Cloud space routing without followClassicMode (feat removed it in 74d73a3). The head merges clean with origin/feat-cmux-next ef5aeb2.

Non-blocking follow-ups:

  • WindowManager+Profiles.swift:80: enterLoaded creates a workspace when nothing is adoptable. A Cmd-N that the window already holds during the wake is not adoptable, so the window can get two workspaces. Check WindowProfiles.visible(registry.members(of:), ...) before the create.
  • RoomHandlers.swift:322 and CloudSpaceTests.swift:11 name MachineRegistry.creationDaemon; the function is creation(in:).
  • The test's fake daemon answers pin-workspace with {}, so on the red the create stops at the pin, before create-workspace. The first expect catches it. Answer {"changed": true} to make the second expect (no create-workspace) meaningful too.

@lawrencecchen

Copy link
Copy Markdown
Contributor

Conflict wave evidence at head a5a5f98:

  • Regression test CloudSpaceTests.aScriptsCreateOnAnUnknownMachineIsRefusedNotMadeOnTheMac. The first version never bound AppActions, so perform("newTab") did nothing (work.count 0 on red and green). a5a5f98 binds the actions and requires that New Workspace ran.
    • Red: fleet 6c03c3ef0f1124a66c55355d at de597d8 (this head with the fix 5d39eb0 reverted) FAILED at CloudSpaceTests.swift:216. The create went to this Mac's daemon, not machineNotConnected.
    • Green: fleet 83030322f667e3783ecf2155 at a5a5f98 PASSED (CloudSpaceTests 12/12).
  • Compile: ce72737fb549b089221571f8 at a5a5f98 PASSED (CmuxNext + app).
  • Review (claude-review-subagent-cw): SHIP at a5a5f98 (comment above).
  • arch-ratchet: the reviewed app_lines exception (+248, owner austinywang, expiry 2026-11-08) is in plans/cmux-next/arch-ratchet.json. The ratchet reports no architecture count rise.

@austinywang: you own the exception. Before 2026-11-08, please move the cloud-space and window-space logic out of CmuxNextApp into packages: RoomHandlers.bindCloudSpace, WindowManager+Profiles enterLoaded/fallBack/SpaceCreation, the MachineRegistry creation helpers and the WorkspaceSpawn Cloud-space routing. Then remove the exception. Review follow-ups that do not block: a Cmd-N while a Cloud space's machine wakes can make two workspaces (WindowManager+Profiles.swift:80); stale creationDaemon doc references (RoomHandlers.swift:322, CloudSpaceTests.swift:11); the fake daemon should answer pin-workspace with {"changed": true} so that the test's second expect also catches the bug.

@lawrencecchen lawrencecchen added the ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds label Nov 1, 2026
…or Austin's package move (merge service rule, cmux-ad 12:25Z)
@lawrencecchen lawrencecchen removed the ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds label Nov 1, 2026
@lawrencecchen

Copy link
Copy Markdown
Contributor

Conflict wave: the merge service held this PR. It adds an app_lines exception (+248) while the tip watcher's grow flag is set, and its body names no merged move PR (cmux-ad 12:25Z). The lead told me to change the PR, not the rule. I looked for a mechanical move of 248+ existing CmuxNextApp lines in the cloud-space / window-space area. None exists. RoomHandlers, WindowManager+Profiles, MachineRegistry and WorkspaceSpawn depend on App-only types (AppActionContext, AppServices, WindowManager, CloudMachineSession), so they need new seams first. RoomStrings is a static namespace enum, which the package namespace lint refuses.

So I took the exception out (3f3693f) and removed ready-to-land.

@austinywang: this PR now waits for your package move. Open a move PR that takes at least 248 lines of the cloud-space / window-space logic out of CmuxNextApp (for example RoomHandlers.bindCloudSpace, WindowManager+Profiles enterLoaded/fallBack/SpaceCreation, the MachineRegistry creation helpers, the WorkspaceSpawn Cloud-space routing), land it, then name it in this PR's body. After that, this PR needs a new review at its head and the label again. The fix, the regression test (red 6c03c3ef, green 83030322) and the compile (ce72737f) at a5a5f98 stay valid for the code; only the ratchet entry changed.

@teamleaderleo

Copy link
Copy Markdown
Collaborator

Review (codex gpt-6-astra high, next-review): SHIP at 3f3693f

SHIP at 3f3693f

Visual verdict: no images; Leo tries the build

  1. [P1] [follow-up] space(following:) only removes the machine session from Default if personal.follows already contains it (RoomHandlers.swift:384-389). A newly resumed machine can reach registryID before asynchronous put-session registration updates the home store; the cleanup is then skipped, and the later automatic Default follow leaves the machine’s workspaces visible in both spaces.

Follow-ups: [P1] Await or reconcile session registration before performing the one-time Default follow removal.

@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 1, 2026 22:56 Inactive
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 1, 2026 22:56 Inactive
@cmux-bot
cmux-bot Bot temporarily deployed to ci-routing-agent November 1, 2026 22:56 Inactive
@teamleaderleo

Copy link
Copy Markdown
Collaborator

Review (codex gpt-6-astra high, next-review): BLOCK at c79ce70

BLOCK at c79ce70

Visual verdict: no images; Leo tries the build

  1. [P1][blocking] CloudMove.holds is undefined at CloudMachineSession.swift:557, RoomHandlers.swift:358, and WindowManager+Profiles.swift:70. The existing helper is CloudWait.holds (CmuxNextCloud/Move/CloudWait.swift:8), so the Swift target cannot compile.

  2. [P2][follow-up] A paused Cloud machine’s first Cmd-N workspace opens a terminal instead of the advertised New Tab page. opensPage is decided before the machine wakes (AppActions+Workspaces.swift:170-171), while opensRemotePage requires capabilities and a registryHost that are unavailable before the paused daemon connects (AgentTabs+Wiring.swift:345-348).

Follow-ups:

  • [P2][follow-up] Preserve New Tab page selection across the paused-machine wake/connect path.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — c79ce70d Deployed Nov 1, 2026 by cmux-bot[bot] via wait for same-key tree publisher #97770
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants