Skip to content

CmuxFleet: board UI — right-sidebar mode, store/rows, config sheet, pane-openable - #7612

Closed
austinywang wants to merge 1 commit into
feat-fleet-core-enginefrom
feat-fleet-board-ui
Closed

austinywang wants to merge 1 commit into
feat-fleet-core-enginefrom
feat-fleet-board-ui

Conversation

@austinywang

@austinywang austinywang commented Jul 8, 2026 •

Copy link
Copy Markdown
Contributor

Fleet PR 4 of the chain in #7361 (stacks on #7418).

What

  • RightSidebarMode.fleet (beta-gated, default off): new board panel in the right-sidebar mode switcher, also openable as a pane like Files/Find/Sessions. Enabled via Settings → Beta Features → Fleet, CLI arg fleet, and the command palette (all respecting the gate).
  • Board: five fixed sections — Queue / Running / Needs input / Review / Done — with counts. Rows show title, colored state chip, attempt badge, PR link, and last error; context menu offers Open / Retry / Cancel / Copy PR URL. Quick-add field enqueues tasks; header has fleet picker, running dot, Start/Stop, and gear.
  • Config sheet: create a fleet (name, repo root with directory picker, agent command template with placeholder help, max concurrent agents), inline validation errors.
  • FleetBoardProjection (CmuxFleet package): pure state→column mapping with exhaustive no-default switches, row snapshots, and canUserRetry/canUserCancel predicates mirroring the reducer rules.
  • FleetEngine.onStateChange: single notification chokepoint after every public mutation and signal-driven state change; FleetBoardStore coalesces refreshes to one rebuild per runloop tick and only publishes on Equatable change.
  • Shared open path: board row click and socket fleet.task.open both call the extracted FleetTaskWorkspaceOpener.openTask — clicking a row is the only focus-changing action.
  • RightSidebarMode extracted to its own file so RightSidebarPanelView.swift lands at 507 lines (was at its 553 cap); no new file exceeds 218 lines; the length-budget TSV is untouched.

Snapshot boundary

Nothing below the board's LazyVStack/ForEach holds an ObservableObject: sections and rows receive immutable FleetBoardRowSnapshot values plus a closure action bundle, and row views are Equatable with .equatable() (per the #2586 rule).

Localization audit

All 52 new user-facing keys (mode label, column headers, chips, context menus, config sheet, empty state, Settings row, palette subtitle) have both en and ja entries in Resources/Localizable.xcstrings / the settings catalog; parity was script-checked. No existing keys modified.

Tests

  • CmuxFleet package: 67 tests pass (projection mapping/ordering/predicates, onStateChange coverage for every mutation and reduced signals).
  • CmuxSettingsUI package: 78 tests pass.
  • cmuxTests (cmux-unit): RightSidebarCommandPaletteTests (incl. new beta-gate regression test for the pane command), FileExplorerStateModePersistenceTests, RightSidebarModeShortcutHintTests — all pass. Note: the shortcut-hint suite has a pre-existing environment dependency (expects the feed beta default enabled in the host defaults domain); verified the 3 affected tests fail identically without this diff and pass with the gate on.
  • Lints: pbxproj test wiring, workspace package groups, Package.resolved policy all pass. Tagged Debug build (fleet-board-ui) is green.

🤖 Generated with Claude Code


Note

Cursor Bugbot is generating a summary for commit 0148091. Configure here.


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


Summary by cubic

Adds a beta-gated Fleet board to the right sidebar (also openable as a pane) to track tasks across Queue, Running, Needs Input, Review, and Done. Keeps the UI live with a new FleetEngine.onStateChange and a lightweight projection/store.

  • New Features

    • Right-sidebar fleet mode and pane command, hidden behind the Beta Features toggle; shows column counts and rows with title, state, attempts, PR link, and last error.
    • Row actions: Open, Retry, Cancel, Copy PR URL; clicking a row focuses the task’s workspace.
    • Header with fleet picker, running indicator, Start/Stop, gear; quick-add input; config sheet to create fleets (name, repo root, agent command template, max concurrent) with inline validation.
    • Command palette and Settings wiring respect the gate; new strings added in en and ja.
  • Refactors

    • CmuxFleet: added FleetBoardProjection plus FleetBoardSnapshot/FleetBoardRowSnapshot; FleetTaskState now exposes canUserRetry/canUserCancel.
    • Engine/UI sync: new FleetEngine.onStateChange; FleetBoardStore coalesces rebuilds and publishes only on Equatable change.
    • Shared task open path via FleetTaskWorkspaceOpener; extracted RightSidebarMode to its own file and updated availability gating.

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

Review in cubic

Fleet PR 4 of #7361. Adds a beta-gated RightSidebarMode.fleet panel
rendering every fleet task in Queue / Running / Needs input / Review /
Done sections, driven by a pure FleetBoardProjection in CmuxFleet and a
coalescing FleetBoardStore fed by the new FleetEngine.onStateChange
chokepoint. Row click and socket fleet.task.open share one
FleetTaskWorkspaceOpener path; retry/cancel/add/start/stop go through
FleetEngine only. Includes a create-fleet config sheet, quick task add,
command-palette wiring gated by the beta toggle, Settings beta row, and
en+ja localization for all new strings. Extracts RightSidebarMode into
its own file so RightSidebarPanelView stays under its length cap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 8, 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 Jul 8, 2026 9:39pm
cmux-staging Building Building Preview, Comment Jul 8, 2026 9:39pm

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c37eaa47-b9a9-4c26-9b9e-9c9eecbddf8a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-fleet-board-ui

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.

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds the beta-gated Fleet board to the right sidebar and pane system. The main changes are:

  • New Fleet board snapshot and projection types in CmuxFleet.
  • Board UI with columns, row actions, quick add, and config sheet.
  • FleetEngine state-change notifications for board refreshes.
  • Fleet mode wiring for settings, command palette, sidebar, panes, and localization.

Confidence Score: 4/5

The Fleet board has two changed paths that need fixes before merging.

  • Fleet board selection can leak across sidebar and pane instances because the store is shared.
  • Fleet socket commands can still run while the Fleet beta is disabled.
  • The projection, localization, and sidebar command gating otherwise look consistent from the inspected changes.

Sources/Fleet/FleetBoardStore.swift, Sources/TerminalController+ControlFleetContext.swift

Security Review

The Fleet beta gate is enforced for the sidebar and command palette, but Fleet control-socket operations can still run while the feature is disabled.

Important Files Changed

Filename Overview
Sources/Fleet/FleetBoardStore.swift Adds the app-side Fleet board store and refresh coalescing, but keeps per-board selection state in a shared singleton.
Sources/TerminalController+ControlFleetContext.swift Routes task opening through the shared opener, but Fleet socket commands still bypass the beta gate.
Sources/Fleet/FleetBoardPanelView.swift Adds the Fleet board panel with header actions, quick add, empty state, and snapshot-driven sections.
Sources/Fleet/FleetBoardRows.swift Adds snapshot-based section and row views with localized state chips and context actions.
Packages/macOS/CmuxFleet/Sources/CmuxFleet/Board/FleetBoardProjection.swift Adds pure Fleet state-to-board projection with grouped and sorted row snapshots.
Packages/macOS/CmuxFleet/Sources/CmuxFleet/Engine/FleetEngine.swift Adds a state-change callback and notification calls after Fleet mutations.
Sources/RightSidebarMode+Availability.swift Adds Fleet mode parsing and availability checks behind the new beta setting.
Resources/Localizable.xcstrings Adds English and Japanese strings for the Fleet board, settings, commands, and task states.

Reviews (1): Last reviewed commit: "Add Fleet board UI: right-sidebar mode, ..." | Re-trigger Greptile


@MainActor
final class FleetBoardStore: ObservableObject {
static let shared = FleetBoardStore()

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 Fleet Selection Bleeds Across Boards

FleetBoardStore.shared owns selectedFleetID and snapshot, but FleetBoardPanelView() is mounted from both the right sidebar and Fleet tool panes. When two board instances are open, changing the fleet picker in one instance refreshes this singleton and silently switches the other instance to the same fleet.

Rule Used: Flag new ambient global state in production Swift:... (source)

@@ -114,24 +114,12 @@ extension TerminalController: ControlFleetContext {
}

func controlFleetTaskOpen(taskID: String) -> ControlFleetTaskOpenResolution {

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 security Socket Fleet Bypasses Gate

fleet.task.open now reaches the shared opener without checking RightSidebarBetaFeatureSettings.isFleetEnabled(), and the sibling controlFleet* socket handlers call the Fleet engine the same way. A socket client can create, start, mutate, or open Fleet tasks while the Fleet beta is disabled, even though the sidebar and command-palette paths are default-denied.

Rule Used: Flag correctness-critical detection/identity deriv... (source)

@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 using default effort and found 1 potential issue.

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 0148091. Configure here.

if after == before {
return .invalidState(before.state)
}
notifyStateChanged()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Double notifyStateChanged fires in userAction method

Low Severity

The userAction method calls apply(signal, to: id) which internally calls notifyStateChanged() (in the apply function at line 83 of FleetEngine+Signals.swift when next != task), and then userAction unconditionally calls notifyStateChanged() again at line 328 when the task changed. Since next != task and after != before are equivalent conditions, the callback always fires twice per user action. The store's coalescing prevents user-visible impact, but this contradicts the stated "single notification chokepoint" design.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0148091. Configure here.

@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 — 01480910 Deployed Jul 8, 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