Skip to content

Add beta Workspace Tasks - #6708

Closed
azooz2003-bit wants to merge 29 commits into
mainfrom
task-workspace-tasks
Closed

azooz2003-bit wants to merge 29 commits into
mainfrom
task-workspace-tasks

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Jun 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Add beta-gated Workspace Tasks with per-workspace open/archive persistence, native surface/popover UI, sidebar tasks control, and shared socket/CLI mutation paths.
  • Add beta-gated Workspace Controls for configurable sidebar row hover controls with close preserved and capped at three controls.
  • Add settings/schema/localization, focused tests, CLI docs, and a Workspace Tasks skill for coding agents.

Testing

  • scripts/ci/run-app-host-xcodebuild.sh -project cmux.xcodeproj -scheme cmux-unit -configuration Debug -derivedDataPath /tmp/cmux-wtask-unit -clonedSourcePackagesDirPath "$PWD/.ci-source-packages" -disableAutomaticPackageResolution -destination "platform=macOS" test -only-testing:cmuxTests/WorkspaceTasksTests
  • swift test --package-path Packages/macOS/CmuxSettings
  • swift test --package-path Packages/macOS/CmuxSettingsUI
  • swift test --package-path Packages/macOS/CmuxWorkspaces
  • swift test --package-path Packages/macOS/CmuxSettingsUI --filter SettingsRowAnchorResolution
  • python3 scripts/normalize-pbxproj.py
  • ./scripts/check-pbxproj.sh
  • ./scripts/lint-pbxproj-test-wiring.sh
  • git diff --check
  • jq empty Resources/Localizable.xcstrings
  • jq empty web/data/cmux.schema.json
  • /tmp/cmux-wtask-unit/Build/Products/Debug/cmux workspace tasks --help
  • /tmp/cmux-wtask-unit/Build/Products/Debug/cmux workspace --help

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


Summary by cubic

Adds beta Workspace Tasks with a native panel and popover, per‑workspace persistence, sidebar status, configurable hover controls, and safer open/index handling that focuses the tasks panel and switches to the right workspace. Also restores and hardens the mobile terminal set‑font path with typed payloads, scoped updates, localized errors, and live font streaming that survives reconnects and output‑tracking resets.

  • New Features

    • Workspace Tasks: per‑workspace open/archived lists; new surface kind workspaceTasks with a restyled panel and popover (header counts, accent stripe, drag handles); sidebar rows show task status; session restore + global search; hover add and between‑task insert dividers; row actions for archive/unarchive/remove/move; open via panelType=workspacetasks (splits supported); rejects stale pane targets; clamps insert/move indices; beta‑gated; opening from the popover or open focuses the tasks panel and switches to the target workspace.
    • CLI + socket: cmux workspace tasks <list|add|archive|unarchive|remove|move|open> and workspace.tasks.{list,add,archive,unarchive,remove,move,open}; resolves workspace refs; validates required flags and rejects conflicting workspace targets; bounded inputs (280‑char titles; max 200 open/200 archived; empty titles rejected; move requires a destination; index params clamped); execution policy allowlist + tests; localized help/errors; open focuses the target workspace.
    • Sidebar Workspace Controls: sidebar.workspaceControls (max 3, deduped; close always preserved); the Tasks control shows whenever the Workspace Tasks beta is enabled, and the Workspace Controls beta unlocks chooser UI; settings UI, settings file mapping, and web/data/cmux.schema.json updated.
    • Mobile terminal font: cmux mobile set-font <points> [--surface <id>] [--workspace <id>] and mobile.terminal.set_font; Mac emits terminal.set_font; iOS decodes typed payloads and applies per‑surface or per‑workspace; validates scope flags and positive font size with localized errors; live font streams persist across reconnects and terminal output‑tracking resets.
  • Migration

    • Enable in Settings → Beta Features: turn on “Workspace Tasks” and (optional) “Workspace Controls”; then configure Settings → Sidebar → “Workspace Controls”.
    • Try it: cmux workspace tasks list, cmux workspace tasks add "Title", cmux workspace tasks unarchive <id>, cmux workspace tasks open. From the sidebar Tasks popover, click “Open as tab” to open and focus the tasks panel. For iOS live‑resize: pair a device, then run cmux mobile set-font 12 (optionally add --surface or --workspace).

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

Review in cubic

Summary by CodeRabbit

Release Notes

  • New Features
    • Workspace Tasks (beta): Per-workspace task list with list, add, archive/complete, remove, reorder (move), and open; appears in search and persists across sessions.
    • Workspace Tasks panel: New “Workspace Tasks” surface (checklist icon) in the sidebar.
    • Workspace Controls (beta): New sidebar hover control options, including a Tasks control.
    • Mobile: Added cmux mobile set-font to update live terminal font size (optionally scoped to a surface/workspace).
  • CLI
    • Added cmux workspace tasks subcommands with updated help/usage.
  • Settings
    • Added beta toggles for Workspace Tasks and Workspace Controls, plus the workspaceControls sidebar configuration.

@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 30, 2026 6:21am
cmux-staging Building Building Preview, Comment Jun 30, 2026 6:21am

@greptile-apps

greptile-apps Bot commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds beta-gated Workspace Tasks and Workspace Controls — per-workspace open/archive task lists with a new workspaceTasks surface kind, popover and panel UIs, sidebar row task-status badges, configurable hover controls, shared socket/CLI mutation paths, session persistence, global search integration, and a mobile terminal set-font command with typed payloads.

  • Workspace Tasks: Workspace+Tasks.swift owns all mutations (add/archive/unarchive/remove/move) with cap enforcement and index clamping; TerminalController+WorkspaceTasks.swift exposes them as nonisolated v2MainSync socket handlers; CLI subcommands mirror the socket API.
  • Workspace Controls: WorkspaceRowControlSanitizer enforces the close-always-present invariant and 3-control cap; sidebar rows now compute a WorkspaceRowControlsSnapshot and show a task-status badge.
  • Policy gap: workspace.tasks.unarchive is registered in TerminalController.swift's switch and allowlist but is absent from ControlCommandExecutionPolicy.socketWorkerMethods and its test, so the control socket policy routes it differently from every other workspace.tasks.* command.

Confidence Score: 4/5

Safe to merge after adding workspace.tasks.unarchive to ControlCommandExecutionPolicy and its test; all other changes are well-structured and beta-gated.

The workspace.tasks.unarchive command is fully registered in TerminalController.swift but absent from ControlCommandExecutionPolicy.socketWorkerMethods — the only place the control socket library uses to categorize worker-lane methods. Every other workspace.tasks.* command is present; this one-line omission causes unarchive to be routed inconsistently, which is a real defect on the changed path. The remaining code (mutation logic, UI, CLI, session persistence) is carefully written and the test suite is thorough. Fixing the policy entry and its test is the only blocker.

ControlCommandExecutionPolicy.swift and its test file: workspace.tasks.unarchive must be added to socketWorkerMethods and the corresponding test assertion before the socket command works correctly end-to-end.

Important Files Changed

Filename Overview
Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Wire/ControlCommandExecutionPolicy.swift Adds six workspace.tasks commands to socketWorkerMethods but omits workspace.tasks.unarchive, breaking its routing relative to all sibling task commands.
Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandExecutionPolicyTests.swift Policy test adds workspace.tasks commands but also omits workspace.tasks.unarchive, leaving the gap untested.
Sources/TerminalController+WorkspaceTasks.swift New file implementing all nonisolated workspace.tasks socket handlers via v2MainSync; includes pre-checks for caps and anchor-error helpers.
Sources/Workspace+Tasks.swift New file with Workspace task mutation helpers with cap enforcement and index clamping.
Sources/Workspace.swift Adds workspaceTaskList/workspaceTasks to Workspace, session persistence/restore, and surface creation helpers.
Sources/WorkspaceTask.swift New Sendable Codable value type for a workspace task with bounded title and timestamps.
Sources/Panels/WorkspaceTasksPanel.swift New @observable @mainactor Panel subclass for the tasks surface with weak workspace reference.
Sources/ContentView.swift Adds WorkspaceTaskSidebarStatus, workspaceRowControls snapshot, and task status badge wiring into TabItemView.
CLI/CMUXCLI+WorkspaceTasks.swift New CLI command group for all workspace.tasks subcommands with argument validation mirroring socket checks.
cmuxTests/WorkspaceTasksTests.swift Comprehensive tests covering task limits, index clamping, anchor resolution, archive/unarchive, move, and sanitization.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CLI as cmux CLI / Socket client
    participant Policy as ControlCommandExecutionPolicy
    participant Worker as Socket Worker Lane
    participant TC as TerminalController (nonisolated)
    participant Main as MainActor (v2MainSync)
    participant WS as Workspace

    CLI->>Policy: "workspace.tasks.* command"
    Policy-->>Worker: socketWorkerMethods allowlist match
    Worker->>TC: "v2WorkspaceTasks{List,Add,Archive,Unarchive,Remove,Move,Open}(params)"
    TC->>Main: "v2MainSync { resolve workspace }"
    Main->>WS: addWorkspaceTask / archiveWorkspaceTask / etc.
    WS-->>Main: mutated WorkspaceTask
    Main-->>TC: V2CallResult (.ok / .err)
    TC-->>Worker: result
    Worker-->>CLI: JSON response

    Note over Policy,Worker: workspace.tasks.unarchive is missing from socketWorkerMethods — routes incorrectly
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 cmux CLI / Socket client
    participant Policy as ControlCommandExecutionPolicy
    participant Worker as Socket Worker Lane
    participant TC as TerminalController (nonisolated)
    participant Main as MainActor (v2MainSync)
    participant WS as Workspace

    CLI->>Policy: "workspace.tasks.* command"
    Policy-->>Worker: socketWorkerMethods allowlist match
    Worker->>TC: "v2WorkspaceTasks{List,Add,Archive,Unarchive,Remove,Move,Open}(params)"
    TC->>Main: "v2MainSync { resolve workspace }"
    Main->>WS: addWorkspaceTask / archiveWorkspaceTask / etc.
    WS-->>Main: mutated WorkspaceTask
    Main-->>TC: V2CallResult (.ok / .err)
    TC-->>Worker: result
    Worker-->>CLI: JSON response

    Note over Policy,Worker: workspace.tasks.unarchive is missing from socketWorkerMethods — routes incorrectly
Loading

Reviews (21): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment on lines +183 to +188
private nonisolated static func workspaceTaskDateString(_ date: Date) -> String {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter.string(from: date)
}

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 ISO8601DateFormatter is allocated fresh on every call to workspaceTaskDateString, and this method is called per task inside the map(v2WorkspaceTaskPayload) closures (up to twice per task — once for createdAt, once for archivedAt). ISO8601DateFormatter is an expensive Objective-C bridge object. A static cached instance avoids the per-element allocation while remaining thread-safe since the formatter is fully configured before the first use and never mutated after.

Suggested change
private nonisolated static func workspaceTaskDateString(_ date: Date) -> String {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter.string(from: date)
}
private nonisolated static let workspaceTaskDateFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter
}()
private nonisolated static func workspaceTaskDateString(_ date: Date) -> String {
workspaceTaskDateFormatter.string(from: date)
}

Rule Used: Flag per-call allocating formatting on hot or conc... (source)

Comment on lines +150 to +165
) -> [String: Any] {
let windowId = v2ResolveWindowId(tabManager: tabManager)
var payload: [String: Any] = [
"window_id": v2OrNull(windowId?.uuidString),
"window_ref": v2Ref(kind: .window, uuid: windowId),
"workspace_id": workspace.id.uuidString,
"workspace_ref": v2Ref(kind: .workspace, uuid: workspace.id),
"tasks": workspace.workspaceTasks.map(v2WorkspaceTaskPayload),
"open": workspace.openWorkspaceTasks.map(v2WorkspaceTaskPayload),
"archived": workspace.archivedWorkspaceTasks.map(v2WorkspaceTaskPayload),
"open_count": workspace.openWorkspaceTasks.count,
"archived_count": workspace.archivedWorkspaceTasks.count
]
if let changedTask {
payload["task"] = v2WorkspaceTaskPayload(changedTask)
}

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 Triple traversal of workspaceTasks in every socket response

v2WorkspaceTasksPayload iterates workspace.workspaceTasks three times: once directly for "tasks", once via workspace.openWorkspaceTasks (which filters the whole list for isOpen), and once via workspace.archivedWorkspaceTasks (which filters for isArchived). That is 3n iterations and 2n intermediate filter passes on a user-owned collection with no configured bound. A single partition loop would produce all three arrays in O(n) and avoid allocating two intermediate filtered arrays per socket response. Since every mutation command calls this payload builder, the overhead compounds with task-list size.

Rule Used: Flag production code that adds nested full-collect... (source)

@coderabbitai

coderabbitai Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces a complete beta-gated per-workspace task system across data model, workspace state, v2 RPC methods, CLI command namespace, configurable sidebar hover controls, SwiftUI panel UI, settings management, and session persistence. Additionally, implements mobile live terminal font-size updates via streaming events and socket dispatch.

Changes

Workspace Tasks and Sidebar Controls

Layer / File(s) Summary
Core contracts and data types
Sources/WorkspaceTask.swift, Packages/macOS/CmuxWorkspaces/.../SurfaceKind.swift, Sources/Panels/Panel.swift, Packages/macOS/CmuxSettings/.../WorkspaceRowControlOption.swift, Packages/macOS/CmuxSettings/.../WorkspaceRowControlSanitizer.swift, Packages/macOS/CmuxSettings/.../BetaFeaturesCatalogSection.swift, Packages/macOS/CmuxSettings/.../SidebarCatalogSection.swift, Sources/SessionPersistence.swift, Sources/CmuxSettingsJSONPathSupport.swift, web/data/cmux.schema.json
Defines WorkspaceTask (Codable, Identifiable, Equatable); introduces SurfaceKind.workspaceTasks and PanelType.workspaceTasks; adds WorkspaceRowControlOption enum with .close and .tasks cases plus WorkspaceRowControlSanitizer for deduplication/enforcement; introduces workspaceTasks and workspaceControls beta-feature keys; adds sidebar.workspaceControls settings key with [.close] default; extends SessionWorkspaceSnapshot with optional tasks field; adds JSON schema and path support.
Workspace task operations and state
Sources/Workspace+Tasks.swift, Sources/WorkspaceTaskList.swift, Sources/Workspace.swift, Sources/TabManager.swift
Extends Workspace with addWorkspaceTask, archiveWorkspaceTask, removeWorkspaceTask, moveWorkspaceTask, sanitizedWorkspaceTasks, computed openWorkspaceTasks/archivedWorkspaceTasks, and static workspaceTasksBetaEnabled helper. Adds WorkspaceTaskList observable backing store. Adds @Published var workspaceTasks property to Workspace. Integrates task snapshots into session save/restore. Includes task metadata (count, title, timestamps) in sessionAutosaveFingerprint.
Workspace tasks panel and surface lifecycle
Sources/Panels/WorkspaceTasksPanel.swift, Sources/Workspace.swift, Sources/Canvas/WorkspaceCanvasHostView.swift, Sources/ClosedItemHistory.swift, Sources/CmuxLifecycleEventPublishing.swift, Sources/Search/GlobalSearchDocuments.swift
Implements WorkspaceTasksPanel (Panel + ObservableObject) with lifecycle stubs and weak workspace reference. Extends Workspace with openOrFocusWorkspaceTasksSurface, newWorkspaceTasksSurface, and splitPaneWithWorkspaceTasks methods. Registers panel in canvas icon mapping, closed-item history title formatting, lifecycle event publisher, and global search hit classification.
Terminal v2 workspace.tasks RPC and socket dispatch
Sources/TerminalController+WorkspaceTasks.swift, Sources/TerminalController.swift, Packages/macOS/CmuxControlSocket/.../ControlCommandExecutionPolicy.swift, Packages/macOS/CmuxControlSocket/Tests/...ControlCommandExecutionPolicyTests.swift
Implements six v2 handlers (v2WorkspaceTasksList/Add/Archive/Remove/Move/Open) with per-method validation and shared v2WorkspaceTasksCommand guard (beta check, UUID validation, workspace/tabmanager resolution). Provides v2WorkspaceTasksPayload and v2WorkspaceTaskPayload for response construction. Routes all methods in socket-worker dispatcher. Advertises in v2Capabilities. Adds socket-worker execution policy and tests.
Panel creation and raw token routing
Sources/TerminalController+ControlPaneContext.swift, Sources/TerminalController+ControlSurfaceContext2.swift, Sources/TerminalControllerV2ParamParsingSupport.swift, Sources/TerminalPaneDropTargetView.swift, Sources/TerminalController+MoveTabToNewWorkspace.swift
Wires .workspaceTasks into controlPaneCreate and controlSurfaceCreate/controlSurfaceSplit flows. Extends panelType(forRawToken:) and v2PanelType to map workspacetasks token. Adds workspace.tasks.open to explicitFocusParamV2Methods. Prevents file-drop routing for .workspaceTasks panels.
CLI workspace tasks namespace and contract
CLI/cmux.swift, docs/cli-contract.md
Adds complete cmux workspace tasks command: subcommand dispatch (list, add, archive/complete, remove/delete/rm, move/reorder, open), argument/option parsing (UUID validation, positional count constraints, --workspace/--window resolution), placement validation (--before/--after/--index), v2 socket calls, and output formatting. Updates cmux workspace dispatcher, error messages, usage text, and help routing. Documents CLI contract with command forms and response formats.
Sidebar control layout and button primitives
Sources/Sidebar/WorkspaceRowControlsLayout.swift, Sources/Sidebar/WorkspaceRowControlsSnapshot.swift, Sources/Sidebar/WorkspaceRowControlButton.swift
Adds WorkspaceRowControlsLayout for calculating hit size, spacing, and minimum sidebar width based on control count and font scale. Introduces WorkspaceRowControlsSnapshot for feature-flag-driven resolution of visible controls with layout. Implements WorkspaceRowControlButton SF Symbol button rendering with icon sizing, color, tooltip, and accessibility labels.
Sidebar row visibility logic and header refactoring
Sources/Sidebar/SidebarWorkspaceSnapshotRefreshPolicy.swift, Sources/SidebarWorkspaceGroupHeaderView.swift
Introduces shouldShowWorkspaceControls helper for shared visibility conditions. Refactors SidebarWorkspaceGroupHeaderView with extracted computed properties for controls (disclosure, anchor, unread badge, plus), menus, and accessibility labels. Updates plus-button visibility logic to use new helper. Consolidates context-menu wiring.
Sidebar and ContentView integration
Sources/ContentView.swift
Wires beta workspace tasks/controls and sidebar workspace controls as live settings inputs. Updates minimumSidebarWidth to include WorkspaceRowControlsSnapshot.resolved layout minimum. Extends command-palette surface-type labeling and keyword generation for .workspaceTasks. Adds workspaceRowControls to SidebarTabItemSettingsSnapshot computed from resolved snapshot. Refactors TabItemView to replace close-only hover overlay with generic workspaceRowControlButton builder supporting close/tasks controls and tasks popover presentation.
Settings configuration and UI
Packages/macOS/CmuxSettingsUI/.../BetaFeaturesSection.swift, Packages/macOS/CmuxSettingsUI/.../SidebarSection.swift, Packages/macOS/CmuxSettingsUI/.../CuratedSettingEntry+Default.swift, Sources/SettingsNavigation.swift, Sources/KeyboardShortcutSettingsFileStore.swift
Adds workspace tasks and workspace controls toggles to BetaFeaturesSection with localized titles and subtitles. Implements SidebarSection workspace controls row with sanitized checkbox bindings for individual control options, beta-flag gating, and conditional help text. Registers curated search entries and settings path anchor mappings. Extends keyboard shortcut settings parser to read/normalize workspaceControls JSON string array.
WorkspaceTasksPanelView and UI composition
Sources/Panels/WorkspaceTasksPanelView.swift, Sources/Panels/WorkspaceTasksPopoverView.swift, Sources/Panels/WorkspaceTasksDisabledView.swift, Sources/Panels/WorkspaceTasksView.swift, Sources/Panels/WorkspaceTasksHeaderView.swift, Sources/Panels/WorkspaceTaskAddComposer.swift, Sources/Panels/WorkspaceTasksSectionView.swift, Sources/Panels/WorkspaceTaskRowView.swift, Sources/Panels/WorkspaceTaskInsertionDividerView.swift, Sources/Panels/WorkspaceTaskIconButton.swift, Sources/Panels/WorkspaceTasksActions.swift, Sources/Panels/PanelContentView.swift
Implements full SwiftUI panel hierarchy: WorkspaceTasksPanelView (beta-gated parent), WorkspaceTasksPopoverView (fixed-frame variant), WorkspaceTasksDisabledView (disabled state), WorkspaceTasksView (main layout with header/add/sections), WorkspaceTasksHeaderView (title/counts/open-as-tab button), WorkspaceTaskAddComposer (text input with validation), WorkspaceTasksSectionView (open/archived lists with insertion dividers), WorkspaceTaskRowView (task controls: archive/move/remove), WorkspaceTaskInsertionDividerView (plus-circle activation/text field), WorkspaceTaskIconButton (reusable icon button), WorkspaceTasksActions (action closures bundle). Wires panel rendering in PanelContentView.
Localization and documentation
Resources/Localizable.xcstrings, skills/cmux-workspace-tasks/SKILL.md
Updates cmux workspace usage and error messages across English, Japanese, Korean, Ukrainian to include tasks subcommand. Adds workspaceTasks localization keys for UI surface labels, CRUD control text, empty states, beta toggles, sidebar tooltips, socket validation/error messages, and full CLI cmux workspace tasks <subcommand> [flags] usage/help. Documents cmux-workspace-tasks skill with workspace scoping (default to caller, CMUX_WORKSPACE_ID fallback), supported operations, and interaction rules.
Tests and Xcode project integration
cmuxTests/WorkspaceTasksTests.swift, Packages/macOS/CmuxSettings/Tests/...WorkspaceRowControlOptionTests.swift, Packages/macOS/CmuxSettingsUI/Tests/...SettingsRowAnchorResolutionTests.swift, cmux.xcodeproj/project.pbxproj, cmuxTests/CMUXCLIErrorOutputRegressionTests.swift
Adds comprehensive test suite for task sanitization/bounds, task lifecycle (add/move/archive/remove), session JSON round-trip, controls snapshot beta-flag resolution, and layout width calculations. Tests workspace row control option sanitization and settings anchor resolution. Registers all new source/test files in Xcode project file references, groups, and build phases for cmux and cmuxTests targets. Includes CLI regression test for placement argument validation.

Mobile Terminal Font Updates

Layer / File(s) Summary
Mobile font event type, CLI command, and v2 RPC handler
Packages/iOS/CmuxMobileRPC/.../MobileTerminalSetFontEvent.swift, CLI/cmux.swift, Sources/TerminalController.swift
Introduces MobileTerminalSetFontEvent decodable struct with JSON key mapping (font_size, surface_id, workspace_id). Adds cmux mobile set-font CLI subcommand with font-size argument validation and optional --surface/--workspace flags, v2 socket call, and localized output. Implements v2MobileTerminalSetFont RPC handler that validates font size and emits terminal.set_font events on MobileHostService.
Mobile live font streaming and dispatch
Packages/iOS/CmuxMobileShell/.../MobileShellComposite.swift
Extends terminalOutputTransport.eventTopics to include "terminal.set_font". Introduces per-surface AsyncStream continuations and UUID tokens for managing live font updates. Implements terminalLiveFontStream(surfaceID:) public API for registering/unregistering continuation streams. Adds handleTerminalSetFontEvent to decode events and yield font sizes to targeted/all surfaces based on scope (surfaceID, workspaceID, or broadcast). Integrates event listener dispatch and lifecycle reset.
Mobile UI integration and test coverage
Packages/iOS/CmuxMobileShellUI/.../GhosttySurfaceRepresentable.swift, Packages/iOS/CmuxMobileTerminal/.../GhosttySurfaceView.swift, cmuxTests/TerminalControllerSocketSecurityTests.swift, ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift
Wires live font stream in GhosttySurfaceRepresentable.Coordinator by subscribing to store.terminalLiveFontStream and calling setLiveFontSize on attach; cancels task on detach. Adds setLiveFontSize(points:) public method on GhosttySurfaceView to apply absolute font size and update zoom HUD. Updates socket security test expectations and mobile event subscription test to include terminal.set_font topic.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SidebarTabItemView as TabItemView
  participant WorkspaceTasksPopover
  participant Workspace
  participant v2RPC as v2 RPC Handler
  participant CLI as CLI / Socket Client

  rect rgba(100, 149, 237, 0.5)
    Note over User, Workspace: Sidebar control interaction flow
    User->>SidebarTabItemView: hover workspace row
    SidebarTabItemView->>SidebarTabItemView: compute WorkspaceRowControlsSnapshot.resolved()
    SidebarTabItemView->>SidebarTabItemView: render close/tasks control buttons
    User->>SidebarTabItemView: click tasks button
    SidebarTabItemView->>WorkspaceTasksPopover: present popover
    User->>WorkspaceTasksPopover: click "open as tab"
    WorkspaceTasksPopover->>Workspace: openOrFocusWorkspaceTasksSurface()
    Workspace->>Workspace: newWorkspaceTasksSurface or reattach panel
    Workspace-->>SidebarTabItemView: panel surface created
  end

  rect rgba(60, 179, 113, 0.5)
    Note over CLI, Workspace: Socket RPC task mutation path
    CLI->>v2RPC: workspace.tasks.add {title, before_task_id}
    v2RPC->>v2RPC: validate beta enabled, UUIDs, resolve workspace
    v2RPC->>Workspace: addWorkspaceTask(title:before:)
    Workspace->>Workspace: sanitizedWorkspaceTasks, insert, `@Published` notify
    Workspace-->>v2RPC: inserted WorkspaceTask
    v2RPC->>v2RPC: construct v2WorkspaceTasksPayload(task, open[], archived[])
    v2RPC-->>CLI: {task, open, archived, counts}
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • manaflow-ai/cmux#4975: The main PR adds new workspace-tasks/workspace-controls settings keys and UI (e.g., extending BetaFeaturesCatalogSection/SidebarCatalogSection with workspaceTasks/workspaceControls) on top of the new CmuxSettings/CmuxSettingsUI package structure introduced by retrieved PR #4975.
  • manaflow-ai/cmux#6222: Both PRs touch workspace "focus/selection" plumbing: the main PR's WorkspaceTasksPanel/task-surface focus logic relies on Workspace.focusedPanelId, while the retrieved PR refactors Workspace.focusedPanelId (and related panel-order queries) to forward through WorkspaceSurfaceListModel.

Suggested reviewers

  • lawrencecchen
  • austinywang

Poem

🐇✨ A checklist blooms where code once lay bare,
Tasks open, archived—handled with care!
The sidebar shimmers with buttons anew,
cmux workspace tasks now sails right through.
From socket to CLI, the bunny did weave,
With fonts that update—a gift to believe!
🗒️ Hop hop, it's done—go archive and achieve! 🎉


Important

Pre-merge checks failed

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

❌ Failed checks (2 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Swift File And Package Boundaries ❌ Error PR violates .github/review-bot-rules/swift-file-package-boundaries.md: (1) CLI/cmux.swift adds 576 lines to file already at 34,546-line budget, exceeding 250-line threshold for >800-line files with... Extract Workspace+Tasks domain logic and TerminalController+WorkspaceTasks socket code into a new SwiftPM package (e.g., CmuxWorkspaceTasks or CmuxTasksManagement), update .github/swift-file-length-budget.tsv, and reduce CLI/cmux.swift b...
Cmux Full Internationalization ❌ Error PR introduces 24 user-facing CLI localization keys (cli.workspaceTasks.*) with only English and Japanese translations, violating the rule requiring all locales already supported by the catalog (en,... Add Korean (ko) and Ukrainian (uk) translations for all 24 cli.workspaceTasks.* keys to match the en,ja,ko,uk pattern established for workspace commands.
Docstring Coverage ⚠️ Warning Docstring coverage is 15.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR covers summary and testing well, but it omits the required Demo Video, Review Trigger, and Checklist sections from the template. Add the missing template sections, including a demo video link for UI changes, the review-trigger comment block, and the checklist items.
✅ Passed checks (21 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add beta Workspace Tasks' clearly describes the main feature being introduced and accurately reflects the primary change in the changeset.
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 WorkspaceTask and related models use proper isolation: nonisolated Sendable value types, @MainActor observable types, and nonisolated socket methods with explicit @MainActor boundaries. No implicit...
Cmux Swift Blocking Runtime ✅ Passed PR introduces no blocking/timing synchronization (semaphores, locks, sleeps, polling). New workspace tasks code avoids these patterns entirely. Short 0.16s UI animation delays are acceptable per ch...
Cmux Browser Automation Off-Main ✅ Passed PR contains no browser socket automation changes—only workspace tasks, workspace controls, and mobile terminal font features. Browser check is not applicable.
Cmux Expensive Synchronous Load ✅ Passed No expensive synchronous agent-history loads (RestorableAgentSessionIndex, FileManager, JSON parsing) added to main actor or interactive paths; socket handlers are nonisolated, panel close/SwiftUI...
Cmux Cache Substitution Correctness ✅ Passed Workspace tasks use an in-memory @Observable WorkspaceTaskList cache with no staleness risk: the cache is created fresh per workspace, all mutations route through explicit workspace methods that di...
Cmux No Hacky Sleeps ✅ Passed The "cmux no hacky sleeps" rule applies only to TypeScript, JavaScript, shell, and non-Swift build/runtime scripts. This PR contains only 63 Swift files, 2 Markdown docs, 1 JSON schema, and 1 Xcode...
Cmux Algorithmic Complexity ✅ Passed All workspace task collections are explicitly bounded to 400 tasks max per workspace (200 open + 200 archived), with no unbounded scaling. Operations like addWorkspaceTask, archiveWorkspaceTask, an...
Cmux Swift Concurrency ✅ Passed The PR introduces new workspace tasks and controls features using modern Swift concurrency patterns. New code uses @Observable (not Combine), async/await, and proper async patterns. The iOS fire-an...
Cmux Swift @Concurrent ✅ Passed All new async functions properly annotated; nonisolated sync v2 handlers (workspace tasks, mobile set-font) don't need @concurrent; off-thread async work uses Task.detached hops.
Cmux Swiftpm Lockfiles ✅ Passed PR complies with SwiftPM lockfile policy: no .gitignore ignores Package.resolved, Xcode project changes include root Package.resolved, and all 11 packages with Package.resolved lockfiles include th...
Cmux Swift Logging ✅ Passed All new workspace tasks and mobile font feature code passes Swift logging rules: no print/NSLog/debugPrint/dump in app/runtime code; CLI output (usage/JSON) is permitted per rules.
Cmux User-Facing Error Privacy ✅ Passed All new user-facing error messages in workspace tasks and mobile font features contain only safe, user-facing copy: human-readable instructions, public field names, system constants, and UUIDs. No...
Cmux Swiftui State Layout ✅ Passed All SwiftUI changes follow .github/review-bot-rules/swiftui-state-layout.md: no new ObservableObject/StateObject anti-patterns, no GeometryReader layout changes, ForEach rows receive value snapshot...
Cmux Architecture Rethink ✅ Passed PR introduces workspace tasks and controls with clean state ownership: tasks centrally owned by Workspace via WorkspaceTaskList, controls computed once from settings via WorkspaceRowControlsSnapsho...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds workspace tasks UI components (Panel protocol implementation, SwiftUI Views) and a .popover modifier for the UI. No NSWindow, NSPanel, NSWindowController, SwiftUI Window, or WindowGroup cla...
Cmux Source Artifacts ✅ Passed All 68 changed files are intentional product sources, tests, localization catalogs, configuration files, or documentation. No prohibited artifacts (logs, temp dirs, build output, caches, DerivedDat...
Cmux No Test Or Debug Seam In Production Source ✅ Passed No test/debug seams found in production source files. All 15+ new workspace task files and modified production sources are clean of #if DEBUG blocks, test/debug naming patterns (ForTesting, TestHoo...
Cmux No Ambient Global State ✅ Passed All new code follows the no-ambient-global-state rule: WorkspaceTaskList is owned by Workspace (not singleton), workspace tasks APIs are extension methods, new types are value structs/enums/views w...
✨ 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 task-workspace-tasks

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.

@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: 11

🤖 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 7585-8065: Extract the workspace-tasks command implementation into
a separate scoped type or file to reduce the size of the monolithic CLI file.
Create a new type (such as WorkspaceTasksCommandHandler) and move all the
implementation functions including runWorkspaceTasksListCommand,
runWorkspaceTasksAddCommand, runWorkspaceTasksTaskMutationCommand,
runWorkspaceTasksMoveCommand, runWorkspaceTasksOpenCommand,
workspaceTasksTargetParams, applyWorkspaceTaskPlacement,
rejectUnknownWorkspaceTasksFlags, positionalArguments, and all the summary/usage
helper functions into this new type. Keep only the runWorkspaceTasksCommand
function as a thin dispatch shim in the original file that delegates to the new
type, reducing the addition from 400+ lines to under 250 lines in the main CLI
file.
- Around line 7705-7721: When assigning the title using the expression titleOpt
?? positionalArguments(rem5).joined(separator: " "), add validation to reject
extra positional arguments if titleOpt is provided. After parsing the title,
check that if titleOpt is not nil, the positionalArguments(rem5) array must be
empty. If positional arguments exist when --title is explicitly used, throw a
CLIError to prevent silent dropping of unexpected arguments.

In
`@Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift`:
- Around line 147-149: The new sidebar row path
"setting:sidebarAppearance:workspace-controls" has been added to the code but is
missing from the rowConfigPaths list in the SettingsRowAnchorResolutionTests
test. Add this row path to the rowConfigPaths array alongside the other existing
sidebar appearance paths to maintain the test contract that every standalone row
path resolves properly.

In `@skills/cmux-workspace-tasks/SKILL.md`:
- Around line 1-74: The Commands section of the SKILL.md documentation is
incomplete and does not show all supported flags and argument patterns for
workspace task operations. Update the "Add a task" examples to document that the
add command supports `--before` and `--index` flags in addition to `--after` for
positioning control. Update the "Insert or reorder" examples to show that the
move command accepts `--task`, `--task-id`, or `--id` as alternative flag forms
for specifying the task UUID instead of only the positional argument.
Additionally, clarify in the "Open the native Workspace Tasks surface" example
that the `--focus` flag requires a boolean string value formatted as `"true"` or
`"false"` to explicitly show users the expected value format.

In `@Sources/CmuxSettingsJSONPathSupport.swift`:
- Around line 245-246: Replace the hardcoded string literal
"sidebar.workspaceControls" at line 389 with a reference to the constant
SidebarSettingsFileMapping.workspaceControlsPath that is already defined at line
245. This ensures the settings path key is maintained in a single place and
prevents drift if the key needs to be updated in the future.

In `@Sources/ContentView.swift`:
- Around line 1245-1252: The minimum sidebar width calculation in this function
currently uses GhosttyConfig.defaultSidebarFontSize when calling
SidebarTabItemFontScale.scale, but it should use the actual configured
sidebarFontScale value instead (the same way SidebarTabItemSettingsSnapshot
resolves row controls at lines 9632-9637). Replace the hardcoded
GhosttyConfig.defaultSidebarFontSize argument with the active/configured
sidebarFontScale so that the computed minimumSidebarWidth accounts for the
user's actual font size setting, preventing under-calculation that can cause
clipping or overlap of row controls.

In `@Sources/Panels/WorkspaceTasksPanelView.swift`:
- Around line 4-5: Replace the `@ObservedObject` property wrappers for the panel
and workspace properties with the newer `@Observable` pattern. Mark the
WorkspaceTasksPanel and Workspace model classes with the `@Observable` macro
instead of conforming to ObservableObject, remove any `@Published` property
wrappers from their properties, and convert the state access to use `@State` or
immutable value snapshots with action closures as required by the repository's
SwiftUI state layout guidelines for new cmux-owned code.

In `@Sources/TerminalController`+WorkspaceTasks.swift:
- Around line 25-27: The current implementation uses nil-coalescing operators on
the result of v2UUID and v2Int calls, which silently collapses
malformed-but-present values to nil instead of detecting them as invalid
parameters. Refactor the parameter extraction logic for beforeTaskId,
afterTaskId, and index (and their fallback alternatives on the referenced lines)
to first validate that if a parameter key is present in params, it is actually
valid. If a parameter key exists but contains a malformed value, return an
invalid_params error response instead of treating it as an absent parameter. Use
explicit validation checks on the raw parameter values before attempting type
conversion, rather than relying on nil-coalescing to handle the distinction
between absent and malformed parameters.

In `@Sources/Workspace.swift`:
- Around line 2207-2210: The workspaceTasks property is currently using the
`@Published` property wrapper, which violates the project's SwiftUI state-layout
rules for cmux-owned state. Remove the `@Published` decorator from the
workspaceTasks property and refactor this workspace state to use the `@Observable`
macro pattern instead, implementing the state management through `@State`
snapshots or value closures as required by the project guidelines for new state
surfaces.
- Around line 1525-1533: The `.workspaceTasks` case in the
`createPanel(from:...)` method restores workspace tasks panels without verifying
the workspace-tasks beta feature gate is enabled. Add a guard statement at the
beginning of the `.workspaceTasks` case to check if the workspace-tasks feature
is enabled (using the appropriate beta gate check), and return nil if the
feature is disabled. This ensures that session restore respects the feature flag
and cannot bypass it.

In `@web/data/cmux.schema.json`:
- Line 767: The description text added on line 767 for the workspace sidebar
controls setting is hardcoded without a corresponding descriptionKey property
for i18n support. Add a descriptionKey field alongside the description that
references a message key following your i18n naming conventions, then create
matching entries in all locale message files listed in web/i18n/routing.ts with
the appropriate translations for each locale to ensure full internationalization
coverage.
🪄 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: d5d7ea43-cb49-47e3-9883-fca4ce7adda9

📥 Commits

Reviewing files that changed from the base of the PR and between 18e4d64 and 6b9515a.

📒 Files selected for processing (48)
  • CLI/cmux.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Wire/ControlCommandExecutionPolicy.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandExecutionPolicyTests.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/BetaFeaturesCatalogSection.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/SidebarCatalogSection.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/WorkspaceRowControlOption.swift
  • Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/WorkspaceRowControlOptionTests.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BetaFeaturesSection.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SidebarSection.swift
  • Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift
  • Packages/macOS/CmuxWorkspaces/Sources/CmuxWorkspaces/Core/Values/SurfaceKind.swift
  • Packages/macOS/CmuxWorkspaces/Tests/CmuxWorkspacesTests/Core/WorkspaceCoreValueTests.swift
  • Resources/Localizable.xcstrings
  • Sources/Canvas/WorkspaceCanvasHostView.swift
  • Sources/ClosedItemHistory.swift
  • Sources/CmuxLifecycleEventPublishing.swift
  • Sources/CmuxSettingsJSONPathSupport.swift
  • Sources/ContentView.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/Panels/Panel.swift
  • Sources/Panels/PanelContentView.swift
  • Sources/Panels/WorkspaceTasksPanel.swift
  • Sources/Panels/WorkspaceTasksPanelView.swift
  • Sources/Search/GlobalSearchDocuments.swift
  • Sources/SessionPersistence.swift
  • Sources/SettingsNavigation.swift
  • Sources/Sidebar/SidebarWorkspaceSnapshotRefreshPolicy.swift
  • Sources/Sidebar/WorkspaceRowControlButton.swift
  • Sources/Sidebar/WorkspaceRowControlsLayout.swift
  • Sources/Sidebar/WorkspaceRowControlsSnapshot.swift
  • Sources/SidebarWorkspaceGroupHeaderView.swift
  • Sources/TabManager.swift
  • Sources/TerminalController+ControlPaneContext.swift
  • Sources/TerminalController+ControlSurfaceContext2.swift
  • Sources/TerminalController+MoveTabToNewWorkspace.swift
  • Sources/TerminalController+WorkspaceTasks.swift
  • Sources/TerminalController.swift
  • Sources/TerminalControllerV2ParamParsingSupport.swift
  • Sources/TerminalPaneDropTargetView.swift
  • Sources/Workspace+Tasks.swift
  • Sources/Workspace.swift
  • Sources/WorkspaceTask.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/WorkspaceTasksTests.swift
  • docs/cli-contract.md
  • skills/cmux-workspace-tasks/SKILL.md
  • web/data/cmux.schema.json

Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift Outdated
Comment thread skills/cmux-workspace-tasks/SKILL.md
Comment thread Sources/CmuxSettingsJSONPathSupport.swift
Comment thread Sources/Panels/WorkspaceTasksPanelView.swift Outdated
Comment thread Sources/TerminalController+WorkspaceTasks.swift Outdated
Comment thread Sources/Workspace.swift
Comment thread Sources/Workspace.swift Outdated
Comment thread web/data/cmux.schema.json
Comment on lines +88 to +100
return v2WorkspaceTasksCommand(params: params) { workspace, tabManager in
guard let task = workspace.archiveWorkspaceTask(id: taskId) else {
return .err(code: "not_found", message: String(localized: "socket.workspaceTasks.notFound", defaultValue: "Task not found"), data: ["task_id": taskId.uuidString])
}
return .ok(v2WorkspaceTasksPayload(workspace: workspace, tabManager: tabManager, changedTask: task))
}
}

nonisolated func v2WorkspaceTasksRemove(params: [String: Any]) -> V2CallResult {
guard let taskId = v2UUID(params, "task_id") ?? v2UUID(params, "id") else {
return .err(
code: "invalid_params",
message: String(localized: "socket.workspaceTasks.taskIdRequired", defaultValue: "workspace task command requires task_id"),

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 not_found returned when archive is full

archiveWorkspaceTask returns nil for two distinct reasons: the task ID was not found, and the task exists but the archive cap (200) has been reached. Because v2WorkspaceTasksArchive maps both nil returns to the same not_found / "Task not found" response, a caller that supplies a valid task_id of an open task will receive {"code":"not_found"} when the archive bucket is full — indistinguishable from a genuinely missing task. An agent or script that retries on not_found will loop indefinitely; one that surfaces the message to the user will show an incorrect error. The add command avoids this by pre-checking openCount < maximumOpenTaskCount before calling addWorkspaceTask, then mapping the residual nil to an anchor-not-found error. The same separation is needed here: pre-check the archive count inside the v2WorkspaceTasksCommand closure and return limit_exceeded before calling the mutating method.

@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: 1

🤖 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 `@Sources/Panels/WorkspaceTasksPanelView.swift`:
- Around line 19-20: The computed properties openWorkspaceTasks and
archivedWorkspaceTasks in the Workspace model are filtering the entire
workspaceTasks collection on every access, causing redundant O(n) scans whenever
the body property renders. Instead of leaving these as computed properties that
filter repeatedly, refactor the Workspace model to cache the filtered task
arrays as stored properties that update only when workspaceTasks changes (using
didSet or by observing the `@Published` property). Alternatively, compute the
filtered snapshots once in a single location before passing them to
WorkspaceTasksPanelView and WorkspaceTasksPopoverView to ensure filtering
happens only once per state change rather than on every render cycle.
🪄 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: 1020ef2b-fb89-4616-a11e-b7d78484f6ea

📥 Commits

Reviewing files that changed from the base of the PR and between 6b9515a and 2282e78.

📒 Files selected for processing (2)
  • Resources/Localizable.xcstrings
  • Sources/Panels/WorkspaceTasksPanelView.swift

Comment thread Sources/Panels/WorkspaceTasksPanelView.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 (1)
Sources/TerminalController+WorkspaceTasks.swift (1)

81-81: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate malformed task_id instead of treating it as absent.

Lines 81, 97, and 113 extract task_id/id with nil-coalescing, then fail with "requires task_id" when nil. If task_id is present but malformed (e.g., invalid UUID format), v2UUID returns nil and the error message is misleading—it should report "Unresolved task_id" not "requires task_id."

This is the same issue the past review comment flagged for placement params (lines 42-44), which was correctly fixed by introducing v2WorkspaceTasksPlacementValidationError (lines 271-299). Apply the same validation pattern here.

🛡️ Proposed fix pattern

Before extracting task_id in each method, validate it explicitly:

 nonisolated func v2WorkspaceTasksArchive(params: [String: Any]) -> V2CallResult {
-    guard let taskId = v2UUID(params, "task_id") ?? v2UUID(params, "id") else {
+    for key in ["task_id", "id"] {
+        if v2HasNonNullParam(params, key), v2UUID(params, key) == nil {
+            return .err(
+                code: "invalid_params",
+                message: String(
+                    format: String(localized: "socket.workspaceTasks.unresolvedIdentifier", defaultValue: "Unresolved %@"),
+                    locale: .current,
+                    key
+                ),
+                data: nil
+            )
+        }
+    }
+    guard let taskId = v2UUID(params, "task_id") ?? v2UUID(params, "id") else {
         return .err(
             code: "invalid_params",
             message: String(localized: "socket.workspaceTasks.taskIdRequired", defaultValue: "workspace task command requires task_id"),
             data: nil
         )
     }

Apply the same pattern to v2WorkspaceTasksRemove (line 97) and v2WorkspaceTasksMove (line 113).

Also applies to: 97-97, 113-113

🤖 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`+WorkspaceTasks.swift at line 81, The issue is
that task_id/id parameter extraction at lines 81, 97, and 113 uses
nil-coalescing which treats malformed UUIDs (invalid format) the same as missing
parameters, resulting in a misleading "requires task_id" error instead of
"Unresolved task_id". Apply the same validation error pattern that was
implemented for placement params using v2WorkspaceTasksPlacementValidationError
(lines 271-299) to each of the three methods. Before extracting task_id in each
method (the unnamed method containing line 81, v2WorkspaceTasksRemove at line
97, and v2WorkspaceTasksMove at line 113), validate the parameter explicitly to
distinguish between missing and malformed values, then throw the appropriate
error that distinguishes these two cases just as the placement validation error
does.
🤖 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`+WorkspaceTasks.swift:
- Line 81: The issue is that task_id/id parameter extraction at lines 81, 97,
and 113 uses nil-coalescing which treats malformed UUIDs (invalid format) the
same as missing parameters, resulting in a misleading "requires task_id" error
instead of "Unresolved task_id". Apply the same validation error pattern that
was implemented for placement params using
v2WorkspaceTasksPlacementValidationError (lines 271-299) to each of the three
methods. Before extracting task_id in each method (the unnamed method containing
line 81, v2WorkspaceTasksRemove at line 97, and v2WorkspaceTasksMove at line
113), validate the parameter explicitly to distinguish between missing and
malformed values, then throw the appropriate error that distinguishes these two
cases just as the placement validation error does.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 65dab73f-3606-4755-b470-6508a8d4ca41

📥 Commits

Reviewing files that changed from the base of the PR and between 2282e78 and 4216645.

📒 Files selected for processing (8)
  • CLI/cmux.swift
  • Resources/Localizable.xcstrings
  • Sources/Panels/WorkspaceTasksPanelView.swift
  • Sources/TerminalController+WorkspaceTasks.swift
  • Sources/Workspace+Tasks.swift
  • Sources/WorkspaceTask.swift
  • cmuxTests/CMUXCLIErrorOutputRegressionTests.swift
  • cmuxTests/WorkspaceTasksTests.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: 2

🤖 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 3855-3865: The set-font command parsing is too permissive and
could misparse arguments when flags are positioned early or ignore extra
positional arguments and unknown flags. Replace the simple first(where:) logic
that finds the first non-flag token as sizeArg with a proper argument parser
that walks through the entire rest array once, explicitly skips recognized
option values for --surface and --workspace, rejects any unknown flags with a
CLIError, and ensures exactly one positional size argument is provided before
the optional flags.

In `@Sources/TerminalController.swift`:
- Around line 12928-12962: The error messages in the v2MobileTerminalSetFont
function ("Missing or invalid font_size" and "font_size must be a positive
number of points") are plain string literals but must be localized since they
are user-facing. Replace both error message strings with the
String(localized:defaultValue:) pattern, using descriptive keys like
"error.terminal.font_size.invalid_params" and
"error.terminal.font_size.invalid_value", then add corresponding entries to the
Resources/Localizable.xcstrings file with translations for all supported
locales.
🪄 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: 174652b0-bbfc-4ac0-a0e2-f40f11d47538

📥 Commits

Reviewing files that changed from the base of the PR and between 4216645 and 7ea152c.

📒 Files selected for processing (12)
  • CLI/cmux.swift
  • Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileTerminalSetFontEvent.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/GhosttySurfaceRepresentable.swift
  • Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Wire/ControlCommandExecutionPolicy.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandExecutionPolicyTests.swift
  • Resources/Localizable.xcstrings
  • Sources/TerminalController.swift
  • cmuxTests/RightSidebarRemoteCommandTests.swift
  • cmuxTests/TerminalControllerSocketSecurityTests.swift
  • ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift

Comment thread CLI/cmux.swift Outdated
Comment thread Sources/TerminalController.swift
Comment on lines 12925 to 12971
}
}

/// Publish a `terminal.set_font` event to connected iOS device(s) so the
/// mirrored terminal live-zooms its font. This drives the same iOS apply path
/// as a pinch/zoom step, but originates from Mac automation
/// (`cmux mobile set-font <size>`).
nonisolated func v2MobileTerminalSetFont(params: [String: Any]) -> V2CallResult {
guard let fontSize = v2Double(params, "font_size") else {
return .err(
code: "invalid_params",
message: String(
localized: "socket.mobile.setFont.error.missingOrInvalidFontSize",
defaultValue: "Missing or invalid font_size"
),
data: nil
)
}
guard fontSize.isFinite, fontSize > 0 else {
return .err(
code: "invalid_params",
message: String(
localized: "socket.mobile.setFont.error.positiveFontSizeRequired",
defaultValue: "font_size must be a positive number of points"
),
data: ["font_size": fontSize]
)
}
var payload: [String: Any] = ["font_size": fontSize]
if let surfaceID = v2RawString(params, "surface_id") {
payload["surface_id"] = surfaceID
}
if let workspaceID = v2RawString(params, "workspace_id") {
payload["workspace_id"] = workspaceID
}
let hasSubscribers = MobileHostService.hasEventSubscribers(topic: "terminal.set_font")
MobileHostService.emitEvent(topic: "terminal.set_font", payload: payload)
return .ok([
"ok": true,
"font_size": fontSize,
"delivered": hasSubscribers,
])
}

/// Mobile-gated wrapper over ``v2WorkspaceAction(params:)``.
func v2MobileWorkspaceAction(params: [String: Any]) -> V2CallResult {
let rawAction = v2RawString(params, "action")

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 Unvalidated scope identifiers silently forwarded to iOS push event

surface_id and workspace_id are read with v2RawString instead of v2UUID. Every other command in this file that accepts these params calls v2UUID, which returns invalid_params for malformed values. Here, any non-null string — including garbage like "not-a-uuid" — is forwarded verbatim in the terminal.set_font push payload, and the response still returns {"ok": true, "delivered": true}. The iOS subscriber receives the bad identifier, can't match any real surface/workspace, and silently mis-scopes (or drops) the font change. The caller has no way to detect the scope failure from the response.

Comment on lines +151 to +163
message: String(localized: "socket.workspaceTasks.openFailed", defaultValue: "Workspace Tasks surface could not be opened"),
data: nil
)
}
return .ok(v2WorkspaceTasksPayload(workspace: workspace, tabManager: tabManager, changedSurfaceId: panel.id))
}
}

nonisolated private func v2WorkspaceTasksCommand(
params: [String: Any],
body: @MainActor (_ workspace: Workspace, _ tabManager: TabManager) -> V2CallResult
) -> V2CallResult {
for key in ["workspace_id", "surface_id", "terminal_id", "tab_id", "pane_id", "window_id"] {

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 Move anchor-not-found error misidentifies the subject task

When moveWorkspaceTask returns nil because before_task_id or after_task_id was not found in the task's bucket, the handler returns {"code":"not_found","data":{"task_id":"<SUBJECT>"}}. The task_id in the data refers to the subject task — which IS present — so the error falsely reports that task as missing. A caller that retries on not_found will loop indefinitely with a valid task ID. The add command avoids this by calling v2WorkspaceTasksAnchorErrorData(beforeTaskId:afterTaskId:) to surface the actual anchor ID. The move handler should do the same: check whether the subject task exists first (if it doesn't, return not_found with the task ID), and if the anchor lookup then fails, return not_found with the anchor ID and a distinct message.

# Conflicts:
#	CLI/cmux.swift
#	Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileTerminalSetFontEvent.swift
#	Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
#	Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/GhosttySurfaceRepresentable.swift
#	Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
#	Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Wire/ControlCommandExecutionPolicy.swift
#	Resources/Localizable.xcstrings
#	Sources/TerminalController.swift
# Conflicts:
#	.github/swift-file-length-budget.tsv
# Conflicts:
#	.github/swift-file-length-budget.tsv
#	Sources/SidebarWorkspaceGroupHeaderView.swift
@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

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

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants