Skip to content

Add one-step grouped workspace creation - #6657

Merged
lawrencecchen merged 5 commits into
mainfrom
feat-workspace-create-group-placement
Jun 23, 2026
Merged

lawrencecchen merged 5 commits into
mainfrom
feat-workspace-create-group-placement

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add group_id/group_placement support to workspace.create
  • expose --group, --group-placement, and --group-reference on cmux workspace create/new-workspace
  • let workspace.group.add accept placement/reference params

Verification

  • cargo test in cmux-home
  • swift test in Packages/macOS/CmuxControlSocket
  • ./scripts/reload-cloud.sh --tag grpcrt
  • tagged preflight: workspace create --group --group-placement top returned the group ref and inserted the workspace directly after the anchor

Dogfood tag: http://127.0.0.1:17320/grpcrt


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Medium Risk
Changes workspace creation and sidebar group ordering paths used by CLI and mobile create; invalid group/reference now errors before commit, but wrong placement could still surprise users if callers pass inconsistent params.

Overview
Enables creating a workspace and joining a workspace group in one call, instead of create-then-workspace.group.add.

CLI (cmux new-workspace / cmux workspace create) gains --group, --group-placement (afterCurrent|top|end), and --group-reference, mapped to group_id, group_placement, and group_reference_workspace_id on the control API.

v2WorkspaceCreate (moved to TerminalController+WorkspaceCreate.swift) validates group existence, requires group_id when placement/reference is set, rejects bad placement and references that are not in the target group, then calls addWorkspaceToGroup with placement (default top). Success responses include group_id / group_ref.

workspace.group.add now accepts optional placement and reference_workspace_id (including explicit null), forwards them through the control seam to TabManager, and returns invalid_params when the reference workspace is not a group member (new localized string).

Coordinator tests cover forwarding, null reference, invalid placement, and invalid reference handling.

Reviewed by Cursor Bugbot for commit 8dd2d30. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Create a workspace and place it into a group in one step. Adds placement/reference across CLI and API with strict validation and localized errors; responses include group info when grouping is used.

  • New Features

    • CLI: cmux new-workspace and cmux workspace create add --group <id|ref>, --group-placement <afterCurrent|top|end>, and --group-reference <workspace>.
    • API: workspace.create accepts group_id, group_placement (default top), and group_reference_workspace_id; requires group_id when placement/reference is provided; validates group existence and that the reference is in the target group; returns group_id/group_ref. Also accepts aliases placement and reference_workspace_id.
    • API: workspace.group.add accepts placement and reference_workspace_id (nullable); rejects invalid placement, non-UUID references, and references not in the target group with invalid_params; forwards placement/reference to the controller; invalid-reference errors are localized.
  • Migration

    • No changes if you don’t use groups.
    • If you send placement or reference, include group_id; if you send a reference, it must be in that group.

Written for commit 8dd2d30. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for creating workspaces directly inside workspace groups, with optional in-group placement and an optional reference workspace for relative placement.
    • Added CLI flags for new-workspace: --group, --group-placement, and --group-reference (and updated examples/help accordingly).
  • Bug Fixes

    • Invalid group placement or reference values now return invalid_params instead of creating the workspace without grouping.
    • Grouping details are included in the create success response when requested.
  • Documentation

    • Refreshed new-workspace help/usage text and shortened related list-workspaces → create help to match the same flags.

@vercel

vercel Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 23, 2026 8:23am
cmux-staging Building Building Preview, Comment Jun 23, 2026 8:23am

@coderabbitai

coderabbitai Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds optional workspace group placement (--group, --group-placement, --group-reference) to new-workspace CLI and the underlying workspace.group.add control socket command. The ControlWorkspaceGroupContext protocol, coordinator, TerminalController, CLI parser, and test doubles are all updated to carry placement and referenceWorkspaceID through the full stack. New v2 mobile workspace-create methods are introduced with comprehensive parameter validation and group-attachment support.

Changes

Workspace Group Placement

Layer / File(s) Summary
ControlWorkspaceGroupContext protocol contract
Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/WorkspaceGroup/ControlWorkspaceGroupContext.swift
Extends controlAddWorkspaceToGroup protocol method signature to add placement: WorkspaceGroupNewPlacement? and referenceWorkspaceID: UUID?, imports CmuxSettings, and updates doc-comments.
Coordinator placement parsing and validation
Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/WorkspaceGroup/ControlCommandCoordinator+WorkspaceGroup.swift
Extends workspaceGroupAdd to parse placement into WorkspaceGroupNewPlacement (rejecting invalid non-empty strings) and validate reference_workspace_id as a UUID, then forwards both to controlAddWorkspaceToGroup.
TerminalController group placement implementation
Sources/TerminalController+ControlWorkspaceGroupContext.swift, Sources/TerminalController+WorkspaceCreate.swift, Sources/TerminalController.swift
Updates controlAddWorkspaceToGroup to accept and forward placement and referenceWorkspaceID to tabManager. Implements v2WorkspaceCreate with full parameter parsing, validation, layout decoding, group existence checks, workspace creation, group attachment post-creation, and response payload with group_id/group_ref. Adds v2MobileWorkspaceCreate wrapper for mobile-specific behavior. Extends shared workspace creation flow to integrate group targeting.
CLI flag parsing, help text, and localization
CLI/cmux.swift, Resources/Localizable.xcstrings
Adds --group, --group-placement, and --group-reference flag parsing to new-workspace, populates the RPC parameter map with group_id, group_placement, group_reference_workspace_id, expands the "unknown flag" error list in English, Japanese, Korean, and Ukrainian with the new group flags, and updates all usage strings, flag descriptions, and examples.
Test infrastructure and coordinator tests
Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/FakeWorkspaceControlCommandContext.swift, Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandContextTestStubs.swift, Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandCoordinatorWorkspaceTests.swift
Updates FakeWorkspaceControlCommandContext and test stub signatures to match the new protocol signature with placement and referenceWorkspaceID. Implements controlAddWorkspaceToGroup in fake to capture arguments and return configured resolution. Adds three coordinator test cases validating placement forwarding, null reference handling, and rejection of invalid placement.
Build configuration
cmux.xcodeproj/project.pbxproj
Adds TerminalController+WorkspaceCreate.swift to the cmux target compilation via PBXBuildFile, PBXFileReference, source group ordering, and build phase inclusion.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as cmux CLI
  participant TerminalController as TerminalController<br/>(v2WorkspaceCreate)
  participant TabManager as TabManager
  participant ControlWorkspaceGroupContext as ControlWorkspaceGroupContext

  CLI->>TerminalController: new-workspace --group G --group-placement after-current --group-reference R
  TerminalController->>TerminalController: parse & validate group_id, group_placement, group_reference_workspace_id
  TerminalController->>TabManager: addWorkspace(...)
  TabManager-->>TerminalController: workspaceID
  TerminalController->>TabManager: validate group G exists
  TerminalController->>ControlWorkspaceGroupContext: controlAddWorkspaceToGroup(groupID, workspaceID, placement, referenceWorkspaceID)
  ControlWorkspaceGroupContext->>TabManager: addWorkspaceToGroup(workspaceId, groupId, placement, referenceWorkspaceId)
  TabManager-->>ControlWorkspaceGroupContext: result
  ControlWorkspaceGroupContext-->>TerminalController: resolution
  TerminalController-->>CLI: {workspace_id, group_id, group_ref}
Loading
sequenceDiagram
  participant CLI as cmux CLI
  participant Coordinator as Coordinator<br/>(workspaceGroupAdd)
  participant TerminalController as TerminalController<br/>(controlAddWorkspaceToGroup)

  CLI->>Coordinator: workspace.group.add {placement, reference_workspace_id}
  Coordinator->>Coordinator: parse placement to WorkspaceGroupNewPlacement
  Coordinator->>Coordinator: validate reference_workspace_id as UUID
  Coordinator->>TerminalController: controlAddWorkspaceToGroup(groupID, workspaceID, placement?, referenceWorkspaceID?)
  TerminalController-->>Coordinator: ControlWorkspaceGroupAddResolution
  Coordinator-->>CLI: response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • manaflow-ai/cmux#4815: Implements first-class workspace group APIs in TerminalController and related group membership logic that this PR builds upon for placement support.
  • manaflow-ai/cmux#5856: Adds cloud CLI relay wiring for workspace group creation and placement flags that this PR extends on the back-end with placement validation and reference workspace handling.

Poem

🐇 A rabbit hops into the group,
Placement flags form a tidy loop.
--group, --group-placement too,
Coordinators validated anew.
Now workspaces find their rightful place,
Anchored snugly in workspace space! 🌿


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 errors, 1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Cmux User-Facing Error Privacy ❌ Error The PR adds a user-facing error message "Invalid layout: \(error.localizedDescription)" that exposes raw Swift decoder error details, violating the rule against "raw upstream error messages" in use... Replace "Invalid layout: \(error.localizedDescription)" with a generic message like "Invalid layout" without exposing the technical decoder error details.
Cmux Full Internationalization ❌ Error PR introduces new user-facing API error messages without localization: "Invalid placement", "Missing or invalid reference_workspace_id", and others in new TerminalController+WorkspaceCreate.swift a... Localize all new API error messages (at minimum: "Invalid placement", "Missing or invalid reference_workspace_id", "reference_workspace_id must be a member of the target group", etc.) with complete translations for all 20 existing locale...
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description is vague and incomplete. It lacks required sections from the template such as detailed testing methodology, demo video, and review trigger. Add detailed testing steps, include a demo video for the UI changes, copy-paste the review trigger block, and verify all checklist items are marked as complete.
✅ Passed checks (19 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add one-step grouped workspace creation' accurately captures the primary feature being introduced: enabling workspace creation and group placement in a single operation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed All production Swift changes properly maintain MainActor isolation: TerminalController extensions inherit @MainActor from the class, ControlWorkspaceGroupContext protocol is @MainActor, all paramet...
Cmux Swift Blocking Runtime ✅ Passed PR refactors existing workspace creation code and adds group placement support without introducing new blocking/timing synchronization patterns; v2MainSync usage is pre-existing.
Cmux Expensive Synchronous Load ✅ Passed PR adds workspace grouping without expensive synchronous loads. Changes only parse small layout parameter JSON and string parameters on socket handlers, no agent history, JSONL logs, transcripts, o...
Cmux Cache Substitution Correctness ✅ Passed v2WorkspaceCreate is a live API handler (not a persistence/snapshot/undo path) that reads tabManager.workspaceGroups, which is the authoritative in-memory state synchronously updated on the main th...
Cmux No Hacky Sleeps ✅ Passed PR modifies only Swift files, Xcode project config, and localization strings. Rule scope excludes Swift (covered separately) and only applies to TypeScript, JavaScript, shell, and build/runtime scr...
Cmux Algorithmic Complexity ✅ Passed PR adds workspace creation with group placement via single-request handlers using O(n) UUID lookups on validation, no nested loops, batch rescans, or hot-path inefficiencies; logical validation che...
Cmux Swift Concurrency ✅ Passed All Swift code changes follow modern concurrency patterns: v2MainSync uses @MainActor, methods return enums instead of completion handlers, tests use @Test/@mainactor, and no legacy DispatchQueue/c...
Cmux Swift @Concurrent ✅ Passed All new Swift functions in this PR are synchronous and either inherit @MainActor isolation (TerminalController extensions) or are lightweight synchronous operations (test stubs, protocol updates)....
Cmux Swift File And Package Boundaries ✅ Passed New 200-line TerminalController+WorkspaceCreate.swift extension has single clear responsibility (API handler), TerminalController.swift reduced 128 lines, and no mixed responsibilities. Complies wi...
Cmux Swiftpm Lockfiles ✅ Passed PR includes cmux.xcodeproj/project.pbxproj changes with corresponding cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff, satisfying the rule requirement for Xcode projec...
Cmux Swift Logging ✅ Passed No violations found: production Swift code adds no print/debugPrint/dump/NSLog statements, no file-scoped Logger declarations, and no logging of secrets or personal data.
Cmux Swiftui State Layout ✅ Passed PR contains no SwiftUI changes. All modifications are in CLI parsing, control socket coordination, TerminalController logic, tests, build config, and localization—no SwiftUI views, @Observable/@pub...
Cmux Architecture Rethink ✅ Passed PR implements grouped workspace creation using established architectural patterns: single @MainActor-guarded state mutation via v2MainSync, separated validation before creation, atomic creation+gro...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR contains no window-related code; it only adds workspace grouping logic (CLI args, group parameters, control socket forwarding). No NSWindow, NSPanel, NSWindowController, SwiftUI Window, or Windo...
Cmux Source Artifacts ✅ Passed All 11 changed files are intentional source code, tests, build configs, or localization catalogs; no local artifacts, generated logs, caches, DerivedData, temp folders, or build output detected.
Cmux No Test Or Debug Seam In Production Source ✅ Passed PR adds no test/debug seams in production source: new methods v2WorkspaceCreate, v2MobileWorkspaceCreate, and controlAddWorkspaceToGroup are production functionality with no test-only naming,...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-workspace-create-group-placement

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread Sources/TerminalController.swift Outdated
@greptile-apps

greptile-apps Bot commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wires up one-step grouped workspace creation across the CLI, socket API, and mobile create path. workspace.create and new-workspace now accept --group, --group-placement, and --group-reference, validated end-to-end before the workspace is created and inserted into the group. workspace.group.add gains the same placement/reference params with proper invalid_params rejection.

  • TerminalController+WorkspaceCreate.swift (new file, ~200 lines) extracts v2WorkspaceCreate/v2MobileWorkspaceCreate from TerminalController.swift and adds group-id validation, placement parsing, and group membership checks via a v2MainSync call before workspace creation.
  • ControlCommandCoordinator+WorkspaceGroup.swift extends workspace.group.add to validate and forward placement and reference_workspace_id; the new invalidReferenceWorkspace case is covered end-to-end by four new coordinator tests.
  • Resources/Localizable.xcstrings adds the new workspaceGroup.error.invalidReferenceWorkspace string but only supplies en and ja, while the catalog already carries 10+ additional locales for the same workspaceGroup.* domain.

Confidence Score: 4/5

Safe to merge with the localization gap addressed; group creation and validation logic is correct and well-tested.

The new workspaceGroup.error.invalidReferenceWorkspace string is added to the catalog with only en and ja entries while the catalog already carries 10+ additional locales for the same domain. Users in Korean, German, French, Russian, Thai, and other supported locales will see the English fallback for this error. The rest of the change — parameter parsing, validation, protocol updates, and new tests — is clean.

Resources/Localizable.xcstrings — the new error string needs locale entries for all catalog-supported locales beyond en/ja.

Important Files Changed

Filename Overview
Sources/TerminalController+WorkspaceCreate.swift New 200-line file splitting workspace-create out of TerminalController.swift; adds group_id/placement/reference validation and mobile create delegation.
Resources/Localizable.xcstrings Adds workspaceGroup.error.invalidReferenceWorkspace with only en/ja, missing the 10+ other locales already present in the catalog; cli.workspace.create.error.unknownFlag updated correctly across all its covered locales.
Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/WorkspaceGroup/ControlCommandCoordinator+WorkspaceGroup.swift workspace.group.add now validates placement and reference_workspace_id, returns invalidReferenceWorkspace, and forwards both parameters to the context. Logic is clean and well-tested.
Sources/TerminalController+ControlWorkspaceGroupContext.swift Implementation of controlAddWorkspaceToGroup updated with reference membership check and placement forwarding; also supplies the new invalidReferenceWorkspace localized string.
CLI/cmux.swift Adds --group, --group-placement, --group-reference option parsing to workspace create; updates help text and error message for unknown flags. All 4 covered locales updated for the unknownFlag error.
Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandCoordinatorWorkspaceTests.swift Four new coordinator tests cover placement forwarding, null reference, invalid-reference-workspace rejection, and invalid-placement rejection.
Sources/TerminalController.swift v2WorkspaceCreate and v2MobileWorkspaceCreate removed from this file and relocated to the new TerminalController+WorkspaceCreate.swift extension file.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CLI as CLI / socket caller
    participant Coord as ControlCommandCoordinator
    participant TC as TerminalController
    participant TM as TabManager

    CLI->>Coord: workspace.create(group_id, group_placement, group_reference_workspace_id)
    Coord->>TC: v2WorkspaceCreate(params)
    TC->>TM: v2MainSync: workspaceGroups.contains(group_id) + tabs.contains(referenceId)
    TM-->>TC: (groupExists, referenceIsMember)
    alt group not found
        TC-->>Coord: .err not_found
    else invalid reference
        TC-->>Coord: .err invalid_params
    end
    TC->>TM: v2MainSync: addWorkspace(...) + addWorkspaceToGroup(placement, reference)
    TM-->>TC: ws.id
    TC-->>Coord: .ok workspace_id, group_id, group_ref
    Coord-->>CLI: response

    CLI->>Coord: workspace.group.add(group_id, workspace_id, placement, reference_workspace_id)
    Coord->>TC: controlAddWorkspaceToGroup(groupID, workspaceID, placement, referenceWorkspaceID)
    TC->>TM: tabs.contains(referenceId in group)
    alt invalid reference
        TC-->>Coord: .invalidReferenceWorkspace
        Coord-->>CLI: .err invalid_params
    end
    TC->>TM: addWorkspaceToGroup(workspaceId, groupId, placement, referenceWorkspaceId)
    TM-->>TC: tab.groupId updated
    TC-->>Coord: .added
    Coord-->>CLI: .ok group_id, workspace_id
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant CLI as CLI / socket caller
    participant Coord as ControlCommandCoordinator
    participant TC as TerminalController
    participant TM as TabManager

    CLI->>Coord: workspace.create(group_id, group_placement, group_reference_workspace_id)
    Coord->>TC: v2WorkspaceCreate(params)
    TC->>TM: v2MainSync: workspaceGroups.contains(group_id) + tabs.contains(referenceId)
    TM-->>TC: (groupExists, referenceIsMember)
    alt group not found
        TC-->>Coord: .err not_found
    else invalid reference
        TC-->>Coord: .err invalid_params
    end
    TC->>TM: v2MainSync: addWorkspace(...) + addWorkspaceToGroup(placement, reference)
    TM-->>TC: ws.id
    TC-->>Coord: .ok workspace_id, group_id, group_ref
    Coord-->>CLI: response

    CLI->>Coord: workspace.group.add(group_id, workspace_id, placement, reference_workspace_id)
    Coord->>TC: controlAddWorkspaceToGroup(groupID, workspaceID, placement, referenceWorkspaceID)
    TC->>TM: tabs.contains(referenceId in group)
    alt invalid reference
        TC-->>Coord: .invalidReferenceWorkspace
        Coord-->>CLI: .err invalid_params
    end
    TC->>TM: addWorkspaceToGroup(workspaceId, groupId, placement, referenceWorkspaceId)
    TM-->>TC: tab.groupId updated
    TC-->>Coord: .added
    Coord-->>CLI: .ok group_id, workspace_id
Loading

Reviews (5): Last reviewed commit: "Localize invalid group reference errors" | Re-trigger Greptile

Comment thread Sources/TerminalController.swift Outdated
Comment on lines +13301 to +13335
@@ -13286,6 +13325,14 @@ class TerminalController {
if let layoutNode {
ws.applyCustomLayout(layoutNode, baseCwd: cwd ?? ws.currentDirectory)
}
if let groupId {
tabManager.addWorkspaceToGroup(
workspaceId: ws.id,
groupId: groupId,
placement: groupPlacement ?? .top,
referenceWorkspaceId: groupReferenceWorkspaceId
)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 TOCTOU gap between group-existence check and workspace creation

The group is validated in one v2MainSync call (line 13301–13312) and then the workspace is created and grouped in a separate v2MainSync call (line 13313–13338). Because v2MainSync serializes to the main thread one dispatch at a time, another concurrent socket command (e.g. workspace.group.delete) can run between the two blocks, deleting the group after groupExists = true is set. When that happens, addWorkspaceToGroup silently no-ops (as the existing comment notes), so the workspace is created but never added to the group — yet the response returns the group_id as if the association succeeded, misleading the caller.

The group-existence guard and the addWorkspaceToGroup call should both live inside the same v2MainSync closure so the check and the mutation are atomic on the main actor.

Comment thread Sources/TerminalController.swift Outdated
Comment on lines +13272 to +13275
} else if v2HasNonNullParam(params, "reference_workspace_id") {
guard let parsed = v2UUID(params, "reference_workspace_id") else {
return .err(code: "invalid_params", message: "Missing or invalid group_reference_workspace_id", data: nil)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 When the caller provides reference_workspace_id with a non-UUID value, the error message says "Missing or invalid group_reference_workspace_id" — referencing the wrong parameter name. A caller who passed reference_workspace_id will be confused about which field to fix.

Suggested change
} else if v2HasNonNullParam(params, "reference_workspace_id") {
guard let parsed = v2UUID(params, "reference_workspace_id") else {
return .err(code: "invalid_params", message: "Missing or invalid group_reference_workspace_id", data: nil)
}
} else if v2HasNonNullParam(params, "reference_workspace_id") {
guard let parsed = v2UUID(params, "reference_workspace_id") else {
return .err(code: "invalid_params", message: "Missing or invalid reference_workspace_id", data: nil)
}

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@lawrencecchen
lawrencecchen force-pushed the feat-workspace-create-group-placement branch from 187a315 to 42f7467 Compare June 23, 2026 05:12
Comment thread Sources/TerminalController+WorkspaceCreate.swift
Comment thread Sources/TerminalController+WorkspaceCreate.swift

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 14775-14777: The help text for the workspace create command is
missing documentation for the --group-reference flag. Locate the create command
help text (the multi-line description starting with "Create a workspace") and
add --group-reference to the list of inherited group-related flags. The help
text currently lists --env, --env-file, --group, and --group-placement, but
should also include --group-reference to provide complete documentation of all
supported flags for this command.

In
`@Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/WorkspaceGroup/ControlCommandCoordinator`+WorkspaceGroup.swift:
- Around line 275-277: The presence check for reference_workspace_id incorrectly
treats an explicit JSON null as invalid. Modify the condition in the if
statement to distinguish between an absent field and an explicit null value.
Instead of only checking if params["reference_workspace_id"] != nil, also verify
that the value is not a null/NSNull type before determining it as an invalid
parameter. This way, both missing and explicitly null reference_workspace_id
will be treated as valid (absent optional), while only invalid non-null values
will trigger the error.

In `@Sources/TerminalController.swift`:
- Around line 13256-13264: The error message strings "Missing or invalid
group_id" and "Invalid group_placement" are user-facing strings that must be
localized according to coding guidelines. Wrap each error message with
String(localized:defaultValue:) using an appropriate localization key, then add
matching entries to Resources/Localizable.xcstrings for all supported locales.
Apply the same localization pattern to all error messages in the affected
sections (lines 13256-13264, 13268-13275, and 13306-13309) to ensure consistent
internationalization throughout the socket error responses.
- Around line 13301-13312: Combine the group existence validation (currently in
the v2MainSync block checking if groupId exists and if
tabManager.workspaceGroups contains it) with the workspace creation and
attachment logic that occurs in a separate v2MainSync call around lines
13328-13335 into a single main-actor transaction. This ensures that the group
cannot be deleted or invalidated between the validation check and the actual
attachment, preventing the race condition where an ungrouped workspace could be
created while returning successful group metadata.
- Around line 13254-13279: The code is parsing group-scoped parameters like
group_placement, group_reference_workspace_id, and reference_workspace_id
without validating that group_id is also provided. Add validation logic after
parsing these parameters to check if any of them (rawGroupPlacement,
groupReferenceWorkspaceId, or the reference_workspace_id param) are non-null
when groupId is nil, and return an error response in such cases. This ensures
that group-scoped options are only accepted when group_id is actually provided,
preventing silent ignoring of the caller's grouping intent.

In `@Sources/TerminalController`+ControlWorkspaceGroupContext.swift:
- Around line 207-212: The call to tabManager.addWorkspaceToGroup with
referenceWorkspaceId returns success based only on checking if tab.groupId
equals groupID, but does not validate that the referenceWorkspaceID is valid and
belongs to the target group. This means the method could report success while
the workspace is not actually moved. Either add validation to confirm that
referenceWorkspaceID belongs to the target group before returning success, or
refactor addWorkspaceToGroup to return an explicit placement result that
indicates whether the operation actually succeeded rather than inferring success
from membership alone.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1f871cad-d741-4d67-9b1e-a3a3f3f1fe87

📥 Commits

Reviewing files that changed from the base of the PR and between f2dd188 and 187a315.

📒 Files selected for processing (8)
  • CLI/cmux.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/WorkspaceGroup/ControlCommandCoordinator+WorkspaceGroup.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/WorkspaceGroup/ControlWorkspaceGroupContext.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandContextTestStubs.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandCoordinatorWorkspaceTests.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/FakeWorkspaceControlCommandContext.swift
  • Sources/TerminalController+ControlWorkspaceGroupContext.swift
  • Sources/TerminalController.swift

Comment thread CLI/cmux.swift Outdated
Comment thread Sources/TerminalController.swift Outdated
Comment on lines +13254 to +13279
let groupId = v2UUID(params, "group_id")
if v2HasNonNullParam(params, "group_id"), groupId == nil {
return .err(code: "invalid_params", message: "Missing or invalid group_id", data: nil)
}
let rawGroupPlacement = v2RawString(params, "group_placement")
?? (groupId == nil ? nil : v2RawString(params, "placement"))
let groupPlacement = WorkspaceGroupNewPlacement(rawString: rawGroupPlacement)
if let raw = rawGroupPlacement,
!raw.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
groupPlacement == nil {
return .err(code: "invalid_params", message: "Invalid group_placement", data: ["group_placement": raw])
}
let groupReferenceWorkspaceId: UUID?
if v2HasNonNullParam(params, "group_reference_workspace_id") {
guard let parsed = v2UUID(params, "group_reference_workspace_id") else {
return .err(code: "invalid_params", message: "Missing or invalid group_reference_workspace_id", data: nil)
}
groupReferenceWorkspaceId = parsed
} else if v2HasNonNullParam(params, "reference_workspace_id") {
guard let parsed = v2UUID(params, "reference_workspace_id") else {
return .err(code: "invalid_params", message: "Missing or invalid group_reference_workspace_id", data: nil)
}
groupReferenceWorkspaceId = parsed
} else {
groupReferenceWorkspaceId = nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject group-scoped options when group_id is missing.

group_placement, group_reference_workspace_id, and reference_workspace_id are parsed even without group_id, but Line 13328 only applies grouping when groupId exists. A request can therefore create an ungrouped workspace successfully while silently ignoring the caller’s group placement/reference intent.

Suggested direction
+        let hasGroupScopedOptions =
+            v2HasNonNullParam(params, "group_placement")
+            || v2HasNonNullParam(params, "group_reference_workspace_id")
+            || v2HasNonNullParam(params, "reference_workspace_id")
         let groupId = v2UUID(params, "group_id")
         if v2HasNonNullParam(params, "group_id"), groupId == nil {
             return .err(code: "invalid_params", message: "Missing or invalid group_id", data: nil)
         }
+        if groupId == nil, hasGroupScopedOptions {
+            return .err(code: "invalid_params", message: "group_id is required for group placement options", data: nil)
+        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/TerminalController.swift` around lines 13254 - 13279, The code is
parsing group-scoped parameters like group_placement,
group_reference_workspace_id, and reference_workspace_id without validating that
group_id is also provided. Add validation logic after parsing these parameters
to check if any of them (rawGroupPlacement, groupReferenceWorkspaceId, or the
reference_workspace_id param) are non-null when groupId is nil, and return an
error response in such cases. This ensures that group-scoped options are only
accepted when group_id is actually provided, preventing silent ignoring of the
caller's grouping intent.

Comment thread Sources/TerminalController.swift Outdated
Comment thread Sources/TerminalController.swift Outdated
Comment thread Sources/TerminalController+ControlWorkspaceGroupContext.swift
@lawrencecchen
lawrencecchen force-pushed the feat-workspace-create-group-placement branch from 42f7467 to cc839f8 Compare June 23, 2026 05:23

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/WorkspaceGroup/ControlCommandCoordinator`+WorkspaceGroup.swift:
- Around line 272-273: The error messages "Invalid placement" and "Missing or
invalid reference_workspace_id" in the workspace group error responses are
hardcoded English strings that violate localization requirements. Replace these
bare string literals with localization function calls (using the appropriate
localization system for this codebase) and create corresponding translation keys
in all supported locale files. Ensure both error messages at line 272 and line
277 are properly localized through the same mechanism used elsewhere in the
codebase for user-facing API/command output strings.

In `@Sources/TerminalController`+WorkspaceCreate.swift:
- Around line 52-60: The error messages being returned in the workspace creation
error handling (specifically the "Invalid group_placement" message and similar
messages at the referenced line ranges) are not localized for user-facing
output. Wrap each message string with String(localized: "key.description",
defaultValue: "English message") using appropriate localization keys, and add
corresponding entries to Resources/Localizable.xcstrings for all supported
locales. Apply this fix to the error return statements throughout the method,
including the ones at lines 52-60, 65-70, and 102-105.
- Around line 50-75: The code currently accepts group-related parameters
(group_reference_workspace_id, reference_workspace_id, groupPlacement) even when
group_id is not provided, and it returns group fields without verifying that
addWorkspaceToGroup actually succeeded. Add validation to reject
group_reference_workspace_id or reference_workspace_id if group_id is nil,
validate that any provided reference workspace actually belongs to the target
group before creating the workspace, and after the workspace creation is
persisted, only include group fields in the response if ws.groupId == groupId is
confirmed. This validation logic needs to be added around the parameter parsing
section (where group_id, group_placement, and reference parameters are
processed) and the persistence section (where addWorkspaceToGroup is called and
results are returned).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 36d6859c-3f1a-402c-800c-dfe7c4ebcf6f

📥 Commits

Reviewing files that changed from the base of the PR and between 187a315 and cc839f8.

📒 Files selected for processing (10)
  • CLI/cmux.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/WorkspaceGroup/ControlCommandCoordinator+WorkspaceGroup.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/WorkspaceGroup/ControlWorkspaceGroupContext.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandContextTestStubs.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandCoordinatorWorkspaceTests.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/FakeWorkspaceControlCommandContext.swift
  • Sources/TerminalController+ControlWorkspaceGroupContext.swift
  • Sources/TerminalController+WorkspaceCreate.swift
  • Sources/TerminalController.swift
  • cmux.xcodeproj/project.pbxproj
💤 Files with no reviewable changes (1)
  • Sources/TerminalController.swift

Comment on lines +50 to +75
let groupId = v2UUID(params, "group_id")
if v2HasNonNullParam(params, "group_id"), groupId == nil {
return .err(code: "invalid_params", message: "Missing or invalid group_id", data: nil)
}
let rawGroupPlacement = v2RawString(params, "group_placement")
?? (groupId == nil ? nil : v2RawString(params, "placement"))
let groupPlacement = WorkspaceGroupNewPlacement(rawString: rawGroupPlacement)
if let raw = rawGroupPlacement,
!raw.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
groupPlacement == nil {
return .err(code: "invalid_params", message: "Invalid group_placement", data: ["group_placement": raw])
}
let groupReferenceWorkspaceId: UUID?
if v2HasNonNullParam(params, "group_reference_workspace_id") {
guard let parsed = v2UUID(params, "group_reference_workspace_id") else {
return .err(code: "invalid_params", message: "Missing or invalid group_reference_workspace_id", data: nil)
}
groupReferenceWorkspaceId = parsed
} else if v2HasNonNullParam(params, "reference_workspace_id") {
guard let parsed = v2UUID(params, "reference_workspace_id") else {
return .err(code: "invalid_params", message: "Missing or invalid group_reference_workspace_id", data: nil)
}
groupReferenceWorkspaceId = parsed
} else {
groupReferenceWorkspaceId = nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate group targeting before persisting the workspace.

group_reference_workspace_id / reference_workspace_id are accepted without group_id and then ignored, and the create path reports group_id/group_ref without confirming addWorkspaceToGroup actually attached the new workspace. Reject orphaned group-placement/reference params, validate the reference belongs to the target group before creation, and only return group fields after ws.groupId == groupId is confirmed.

Suggested direction
         let groupId = v2UUID(params, "group_id")
         if v2HasNonNullParam(params, "group_id"), groupId == nil {
             return .err(code: "invalid_params", message: "Missing or invalid group_id", data: nil)
         }
+        if groupId == nil,
+           v2HasNonNullParam(params, "group_placement")
+               || v2HasNonNullParam(params, "group_reference_workspace_id")
+               || v2HasNonNullParam(params, "reference_workspace_id") {
+            return .err(
+                code: "invalid_params",
+                message: "group_id is required when using group placement parameters",
+                data: nil
+            )
+        }

Also validate any non-nil reference against the target group’s anchor/member set before tabManager.addWorkspace(...), and capture a post-add failure instead of unconditionally returning group_id.

Also applies to: 97-131, 140-146

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/TerminalController`+WorkspaceCreate.swift around lines 50 - 75, The
code currently accepts group-related parameters (group_reference_workspace_id,
reference_workspace_id, groupPlacement) even when group_id is not provided, and
it returns group fields without verifying that addWorkspaceToGroup actually
succeeded. Add validation to reject group_reference_workspace_id or
reference_workspace_id if group_id is nil, validate that any provided reference
workspace actually belongs to the target group before creating the workspace,
and after the workspace creation is persisted, only include group fields in the
response if ws.groupId == groupId is confirmed. This validation logic needs to
be added around the parameter parsing section (where group_id, group_placement,
and reference parameters are processed) and the persistence section (where
addWorkspaceToGroup is called and results are returned).

Comment on lines +52 to +60
return .err(code: "invalid_params", message: "Missing or invalid group_id", data: nil)
}
let rawGroupPlacement = v2RawString(params, "group_placement")
?? (groupId == nil ? nil : v2RawString(params, "placement"))
let groupPlacement = WorkspaceGroupNewPlacement(rawString: rawGroupPlacement)
if let raw = rawGroupPlacement,
!raw.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
groupPlacement == nil {
return .err(code: "invalid_params", message: "Invalid group_placement", data: ["group_placement": raw])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Localize the new group-create error messages.

These message: values are returned through user-facing socket/CLI/mobile flows, but they are bare English strings. Wrap the new group errors with String(localized: "…", defaultValue: "…") and add matching Resources/Localizable.xcstrings entries for every supported locale.

As per coding guidelines, “All user-facing strings must be localized using String(localized: "key.name", defaultValue: "English text").” As per path instructions, apply .github/review-bot-rules/full-internationalization.md for production user-facing text.

Also applies to: 65-70, 102-105

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/TerminalController`+WorkspaceCreate.swift around lines 52 - 60, The
error messages being returned in the workspace creation error handling
(specifically the "Invalid group_placement" message and similar messages at the
referenced line ranges) are not localized for user-facing output. Wrap each
message string with String(localized: "key.description", defaultValue: "English
message") using appropriate localization keys, and add corresponding entries to
Resources/Localizable.xcstrings for all supported locales. Apply this fix to the
error return statements throughout the method, including the ones at lines
52-60, 65-70, and 102-105.

Sources: Coding guidelines, Path instructions

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bf83e6a. Configure here.

Comment thread Sources/TerminalController+WorkspaceCreate.swift

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Sources/TerminalController+WorkspaceCreate.swift (2)

88-100: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Treat layout: null as omitted.

layout is optional, but an explicit JSON null still enters the decode branch and fails the request instead of using the default layout.

Suggested fix
-        if let rawLayout = params["layout"] {
-            guard JSONSerialization.isValidJSONObject(rawLayout),
+        if v2HasNonNullParam(params, "layout") {
+            guard let rawLayout = params["layout"],
+                  JSONSerialization.isValidJSONObject(rawLayout),
                   let layoutData = try? JSONSerialization.data(withJSONObject: rawLayout) else {
                 return .err(code: "invalid_params", message: "layout must be a valid JSON object", data: nil)
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/TerminalController`+WorkspaceCreate.swift around lines 88 - 100, The
current check `if let rawLayout = params["layout"]` evaluates to true even when
the JSON value is explicitly null, causing the decode logic to attempt
validation and fail. Add an additional guard condition to check that rawLayout
is not an instance of NSNull before proceeding with JSONSerialization validation
and JSONDecoder decoding. This will ensure that an explicit null value is
treated the same as an omitted layout parameter, allowing the default layout to
be used without error.

94-99: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Localize the new non-group workspace-create errors.

These errors are returned through socket/mobile user-facing flows but remain bare English strings. Route them through String(localized:defaultValue:) and add matching Resources/Localizable.xcstrings entries for all supported locales. As per coding guidelines, “All user-facing strings must be localized using String(localized: "key.name", defaultValue: "English text").” As per path instructions, apply .github/review-bot-rules/full-internationalization.md for production user-facing text.

Also applies to: 147-148, 163-166

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/TerminalController`+WorkspaceCreate.swift around lines 94 - 99, The
error messages in the workspace-create flow, including the "invalid layout"
error in the JSONDecoder catch block and other error returns mentioned at the
specified line ranges, are currently bare English strings that need to be
localized. Wrap each user-facing error message string with the
String(localized:defaultValue:) pattern, providing a descriptive key and the
English text as the default value. Then add corresponding entries to the
Resources/Localizable.xcstrings file for all supported locales to provide
translations for each localized key, ensuring all error messages across the
workspace-create operation are properly internationalized.

Sources: Coding guidelines, Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@Sources/TerminalController`+WorkspaceCreate.swift:
- Around line 88-100: The current check `if let rawLayout = params["layout"]`
evaluates to true even when the JSON value is explicitly null, causing the
decode logic to attempt validation and fail. Add an additional guard condition
to check that rawLayout is not an instance of NSNull before proceeding with
JSONSerialization validation and JSONDecoder decoding. This will ensure that an
explicit null value is treated the same as an omitted layout parameter, allowing
the default layout to be used without error.
- Around line 94-99: The error messages in the workspace-create flow, including
the "invalid layout" error in the JSONDecoder catch block and other error
returns mentioned at the specified line ranges, are currently bare English
strings that need to be localized. Wrap each user-facing error message string
with the String(localized:defaultValue:) pattern, providing a descriptive key
and the English text as the default value. Then add corresponding entries to the
Resources/Localizable.xcstrings file for all supported locales to provide
translations for each localized key, ensuring all error messages across the
workspace-create operation are properly internationalized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 030d96b4-d377-429c-9953-e5f983452eba

📥 Commits

Reviewing files that changed from the base of the PR and between cc839f8 and d785038.

📒 Files selected for processing (4)
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/WorkspaceGroup/ControlCommandCoordinator+WorkspaceGroup.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandCoordinatorWorkspaceTests.swift
  • Resources/Localizable.xcstrings
  • Sources/TerminalController+WorkspaceCreate.swift

@lawrencecchen
lawrencecchen merged commit bdd422f into main Jun 23, 2026
34 of 36 checks passed
@lawrencecchen
lawrencecchen deleted the feat-workspace-create-group-placement branch June 23, 2026 06:56

This branch was successfully deployed

1 active deployment
Preview – cmux — 8dd2d308 Deployed Jun 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant