Skip to content

cmux-next sidebar: a working row's second line says what its agent does now ("Now: …") - #23474

Merged
teamleaderleo merged 5 commits into
feat-cmux-nextfrom
leo/sidebar-now-line
Oct 21, 2026
Merged

teamleaderleo merged 5 commits into
feat-cmux-nextfrom
leo/sidebar-now-line

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Slice S2 of Leo's sidebar-organization ask (item A, the "now:" line). It builds on #23390.

What changes

While an agent chat in a workspace works, a row that already has a second line shows "Now: …" there, with what the agent is doing right now (for example "Now: Running the test suite"). The text is the acpmux lane's step, which acpmux already masks; the raw prompt is never shown.

  • Rows keep their height. A one-line row stays one line, so rows don't grow and shrink as agents start and stop.
  • Which chat. With several chats working in one workspace, the one that started last is shown.
  • Toggle. Debug toggle sidebar.nowLine, on by default.
  • Strings. New string sidebar.row.now, in all 21 locales.

Capture

The capture uses expanded rows; the Debug fixture sidebar.laneFixture gives each row a sample agent state, picked by workspace name so it is the same on every run. The working rows (mobile, api-server) show "Now: Running the test suite", and S1's words show on the rest.

Capture steps-f80a41da25 at 3bc155c1a105 (fleet build, capture mini, text checked for private data)

Light Dark
light dark

Tests

Tests/CmuxNextAppTests/SidebarNowLineTests.swift (committed first):

  • a working row knows its step and an idle one doesn't;
  • a row with a second line says it there;
  • a one-line row stays one line;
  • toggle off.

Observed / Not run

  • Ran:
  • Not run locally: the Swift suites (no builds on this Mac); this PR's CI runs them.

Changelog

  • Added: A working sidebar row's second line says what its agent is doing now.

Agent session: cosmic-gray

🤖 Generated with Claude Code

teamleaderleo and others added 2 commits October 21, 2026 07:38
…es now ("Now: …")

The acpmux lane's masked step of the workspace's working chat replaces the text of a row's
second line while it works; a one-line row keeps its height (the hover card has it). Debug
toggle sidebar.nowLine, on by default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bd1-a2ac-74f1-85e7-0322693c12bd&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by chief-sidebar on cosmic-gray · session

@teamleaderleo teamleaderleo added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 98e8a08: HQ feed card · gallery before/after runs · checks

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

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr23474 -f ref=leo/sidebar-now-line -f platform=macos -f backend_mode=local

Not verified: macOS jobs did not run: compile and app tests were skipped on 98e8a08bc6; ci-status is green without them.

CI passes on 98e8a08bc6 (run 39635743280 attempt 1).
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

Tour of 98e8a08bc6 (run)

Path-selected interactions

Interaction Changed paths
core Packages/macOS/CmuxNext/Sources/CmuxNextAgentLanes/SidebarLaneFixture.swift
Packages/macOS/CmuxNext/Sources/CmuxNextAgentLanes/SidebarLaneStatus.swift
Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Logic/SidebarLayoutMetrics.swift
Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Logic/SidebarTunables.swift
Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Logic/WorkspaceRowContent.swift
Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Model/SidebarWorkspace.swift
Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Resources/Localizable.xcstrings
Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Strings\+Priority.swift
Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Views/WorkspaceRowView\+Accessibility.swift
Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Views/WorkspaceRowView.swift
Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarNowLineTests.swift

Core UI (before): DEGRADED — 19 ok, 1 unavailable

tour

Video (mp4)

# Step Result Command
1 Launch ok
2 New workspace ok cmux workspace new --name Tour
3 Type in the terminal ok cmux workspace name:Tour screen current pane current tab current terminal current write --text "echo hello from the cmux-next tour\r"
4 A command reports progress: tab loading spinner ok cmux workspace name:Tour screen current pane current tab current terminal current write --text "clear; printf '\\033]9;4;3\\007'; sleep 900\r"
5 Split right ok cmux action run splitRight
6 Split down ok cmux action run splitDown
7 New terminal tab ok cmux action run newSurface
8 Browser tab ok cmux tab new-browser --url https://example.com
9 Hide the sidebar ok cmux action run toggleSidebar
10 Show the sidebar ok cmux action run toggleSidebar
11 Notification ok cmux notify --title Tour --body "Hello from CI"
12 Notifications panel ok cmux action run showNotifications
13 Agent chat ok cmux agent new-chat
14 Agent workspace ok cmux workspace new --name Agent
15 Agent starts ok cmux workspace name:Agent screen current pane current tab current terminal current write --text "clear; printf '\\033]7501;state=working\\033\\\\'; sleep 8; printf '\\033]7501;state=done\\033\\\\'; sleep 900\r"
16 An agent works: row working spinner ok cmux workspace new --name Notes
17 The agent finishes: done mark in the spinner's place ok
18 Session tree: keycaps on the first rows ok cmux settings set sidebar.sessionTree true
19 Session tree back off for later tours ok cmux settings set sidebar.sessionTree false
20 Profile menu unavailable: exited 1 (the menu shot needs a screen capture route) cmux action run sidebar.profileMenu
Screenshots (15)

1. Launch
Launch

2. New workspace
New workspace

3. Type in the terminal
Type in the terminal

4. A command reports progress: tab loading spinner
A command reports progress: tab loading spinner

5. Split right
Split right

6. Split down
Split down

7. New terminal tab
New terminal tab

8. Browser tab
Browser tab

9. Hide the sidebar
Hide the sidebar

11. Notification
Notification

12. Notifications panel
Notifications panel

13. Agent chat
Agent chat

16. An agent works: row working spinner
An agent works: row working spinner

17. The agent finishes: done mark in the spinner's place
The agent finishes: done mark in the spinner's place

18. Session tree: keycaps on the first rows
Session tree: keycaps on the first rows

### Core UI \(after\): DEGRADED — 19 ok, 1 unavailable

tour

Video (mp4)

# Step Result Command
1 Launch ok
2 New workspace ok cmux workspace new --name Tour
3 Type in the terminal ok cmux workspace name:Tour screen current pane current tab current terminal current write --text "echo hello from the cmux-next tour\r"
4 A command reports progress: tab loading spinner ok cmux workspace name:Tour screen current pane current tab current terminal current write --text "clear; printf '\\033]9;4;3\\007'; sleep 900\r"
5 Split right ok cmux action run splitRight
6 Split down ok cmux action run splitDown
7 New terminal tab ok cmux action run newSurface
8 Browser tab ok cmux tab new-browser --url https://example.com
9 Hide the sidebar ok cmux action run toggleSidebar
10 Show the sidebar ok cmux action run toggleSidebar
11 Notification ok cmux notify --title Tour --body "Hello from CI"
12 Notifications panel ok cmux action run showNotifications
13 Agent chat ok cmux agent new-chat
14 Agent workspace ok cmux workspace new --name Agent
15 Agent starts ok cmux workspace name:Agent screen current pane current tab current terminal current write --text "clear; printf '\\033]7501;state=working\\033\\\\'; sleep 8; printf '\\033]7501;state=done\\033\\\\'; sleep 900\r"
16 An agent works: row working spinner ok cmux workspace new --name Notes
17 The agent finishes: done mark in the spinner's place ok
18 Session tree: keycaps on the first rows ok cmux settings set sidebar.sessionTree true
19 Session tree back off for later tours ok cmux settings set sidebar.sessionTree false
20 Profile menu unavailable: exited 1 (the menu shot needs a screen capture route) cmux action run sidebar.profileMenu
Screenshots (15)

1. Launch
Launch

2. New workspace
New workspace

3. Type in the terminal
Type in the terminal

4. A command reports progress: tab loading spinner
A command reports progress: tab loading spinner

5. Split right
Split right

6. Split down
Split down

7. New terminal tab
New terminal tab

8. Browser tab
Browser tab

9. Hide the sidebar
Hide the sidebar

11. Notification
Notification

12. Notifications panel
Notifications panel

13. Agent chat
Agent chat

16. An agent works: row working spinner
An agent works: row working spinner

17. The agent finishes: done mark in the spinner's place
The agent finishes: done mark in the spinner's place

18. Session tree: keycaps on the first rows
Session tree: keycaps on the first rows

Tours are data in scripts/cmux-next/media/tours: add a step or a tour file to show your feature.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-usa2g created for this PR by the cmux coordinator.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (claude-review-subagent): SHIP at 64c74b4

I read the diff against the merge-base with feat-cmux-next. Nothing was built or run.

Correctness

  • Isolation is fine. SidebarLayoutOptions.rowContent (nonisolated) reads only ws.agentStep and content.detail. Strings.rowNow is called only on the main actor, in WorkspaceRowView.configure and accessibilityText. .map(Strings.fn) is already used in this module (WorkspaceHoverCard.swift:544, PresenceDotsView.swift:40), so passing the main-actor function reference is fine.
  • Row height does not change. SidebarLayoutMetrics.height(for:) only counts lines (content.detail == nil ? 0 : 1) and never measures text width. The new line is set only when detail != nil, and stays non-nil both while the agent works and after it stops. So the "Now: " prefix in the drawn text cannot change the row's height, and hasSubtitle = row.detail != nil still agrees with what is drawn. Every rowContent caller (SidebarLayout, +Fold, SidebarRowIndex patching) goes through the same function, so layout and in-place patches agree.
  • The guard change in SidebarLaneStatus.applying is fine. Lanes are now read when only nowLine is on, and merge and chip are still each gated on their own toggle. rowStatus is on by default, so lanes were already read by default before this PR. No new cost by default.
  • The chip from cmux-next sidebar: each row says when its agent needs you, finished or failed (the Priority view's words) #23390 works alongside this. The chip writes content.chip/content.time on the name line, and the new line writes content.now/content.detail on the second line. A working lane gets no chip, and a needs-you/failed lane gets no step (step keeps only .working). So both never describe the same lane.
  • Tests:
    • aRowWithASecondLineSaysItThere: WorkspaceRowContent(expanded:) takes notification, then description, then lastMessage, so setting lastMessage gives detail != nil. Valid.
    • aOneLineRowStaysOneLine: under the default WorkspaceRowElements.standard, the only second-line text is agentStatus, and the fixture row has no status, so detail == nil. Valid.
    • SidebarLaneStatusTests.toggleOffLeavesRowsAsTheyWere checks only activity, and merge stays gated on laneStatus, so it is unaffected. SidebarRowStatusTests checks only chip, time, activity and order, so it is unaffected too.
  • Localization: sidebar.row.now is valid JSON, has all 21 locales (no locale missing compared with the catalog), every value is translated, and each has exactly one %@. It has a translator comment and extractionState: manual, the same as nearby entries. Swift "Now: \(step)" interpolation produces %@, so the source and catalog match.

Nits (not blocking)

  • The doc comment on SidebarLaneStatus.applying still says "with both toggles off no lane is read". There are now three toggles.
  • On a compact row whose second line shows folder, branch, description, etc., the step replaces the whole line, not just one part of it. That matches the "in place of the second line's text" doc, so I take it as intended. If people miss the folder while an agent works, prepending the step to the line would be the follow-up.
  • agentStep is shown as-is. If an acpmux step can contain a newline, the subtitle could show odd text. A trim or first-line cut in step(_:) would be cheap insurance.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / defaults-snapshot is red on 20 of 21 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

…ble; a step shows its first line (review nits)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bd1-a2ac-74f1-85e7-0322693c12bd&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (claude-review-subagent): SHIP at 32b71cc

Scope: git diff 64c74b4fb5b3..32b71cc2e8e6 (read locally, no build or test run).

Correct:

  • import Foundation (internal under InternalImportsByDefault) is fine: Foundation is used only inside the body of step(_:) (trimmingCharacters(in: .whitespaces)), never in a public or package signature. Sibling files in CmuxNextAgentLanes already import it the same way.
  • split(whereSeparator: \.isNewline) on String gives [Substring] and drops empty pieces, so a leading blank line still yields the first real line; Substring.trimmingCharacters(in:) comes from Foundation's StringProtocol extension and returns String. The result type String? matches.
  • Isolation: SidebarLaneStatus is @MainActor, and so is SidebarNowLineTests (@MainActor @Suite), so the synchronous call in aStepOfSeveralLinesShowsItsFirst compiles.
  • Test expectation holds: AgentActivityLane.of(summary:) keeps the step as given (it only maps "" to nil), so " Running tests\nsecond line" becomes "Running tests".
  • The doc comment fix ("every toggle") is accurate.

Findings:

  1. (mismatch, not a blocker) The commit subject says "the lane fixture picks by name so captures are stable", but SidebarLaneFixture.swift is not in this commit: at this head it still hashes workspace.id (line 23). That change seems to be uncommitted in the author's worktree. Either push it (that new head then needs a quick re-review) or drop the claim from the squash message and PR body. It is Debug-only capture code, so it doesn't block.
  2. (nit) If the latest working lane's step has only whitespace or blank lines, step returns nil instead of falling back to an older working lane that does have a step. This is rare and harmless: the row just stays one line.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (claude-review-subagent): SHIP at 3bc155c

Scope: git diff 32b71cc2e8e6..3bc155c1a105, one file, Packages/macOS/CmuxNext/Sources/CmuxNextAgentLanes/SidebarLaneFixture.swift (Debug capture fixture behind sidebar.laneFixture, off by default). Read locally only, not built or run.

  • Correctness: the state pick now hashes workspace.name in place of workspace.id. WorkspaceModel.name is public internal(set) var name: String (CmuxNextDaemon/Store/WorkspaceModel.swift:12), so .unicodeScalars.reduce works the same as before. The & 0xFFFF mask keeps the accumulator non-negative, so % states.count stays in range. An empty name gives 0, which is still a valid index.
  • Isolation: WorkspaceModel is @MainActor @Observable and SidebarLaneFixture is @MainActor, so the read is on the main actor. The function already read workspace.name for the lane's name/title, so Observation tracks nothing new.
  • Identity: the lane id is still fixture:\(workspace.id), so lane ids stay unique per workspace. Two workspaces with the same name share a sample state, and renaming a row changes its state. Both are intended for a capture fixture (stable captures across slots, where ids differ).
  • Doc comments were updated to match.

No findings.

@cursor

cursor Bot commented Oct 21, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

…bar-now-line

# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Logic/SidebarTunables.swift
@teamleaderleo
teamleaderleo merged commit f432685 into feat-cmux-next Oct 21, 2026
@teamleaderleo
teamleaderleo deleted the leo/sidebar-now-line branch October 21, 2026 19:21
@github-actions

Copy link
Copy Markdown
Contributor

Shipped in:

lawrencecchen added a commit that referenced this pull request Oct 22, 2026
…achine marks patch in place

Since Oct 21 every acpmux lane event (each agent step, a few a second per working agent) mapped
every sidebar row that shows a chat again: each row's mapping read the whole AgentLaneStore.lanes
dictionary (sidebar.laneStatus and sidebar.rowStatus on by default, #23390; the Now: line, #23474),
and the pull request badge feed, on by default since #23495, re-ran its pass over every workspace,
tab and lane on each event too.

- AgentLaneStore keeps one observable cell per session: rows read lane(session) and are told about
  their own lanes only; the badge feed reads links(session) (folder, pull requests, spawnedBy, peer,
  chat), which a new step or state leaves alone. lanes stays for whole-store readers.
- A pull request badge change patches the rows whose badge changed instead of laying the list out
  again (SidebarView render).
- The hover card is not rebuilt (timeline read, refit) when only the hovered row's step changed:
  the card does not show it.
- A row's machine mark image is drawn again only when the mark or its size changes (#22763 made a
  new NSImage on every configure).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lawrencecchen added a commit that referenced this pull request Oct 22, 2026
…achine marks patch in place

Since Oct 21 every acpmux lane event (each agent step, a few a second per working agent) mapped
every sidebar row that shows a chat again: each row's mapping read the whole AgentLaneStore.lanes
dictionary (sidebar.laneStatus and sidebar.rowStatus on by default, #23390; the Now: line, #23474),
and the pull request badge feed, on by default since #23495, re-ran its pass over every workspace,
tab and lane on each event too.

- AgentLaneStore keeps one observable cell per session: rows read lane(session) and are told about
  their own lanes only; the badge feed reads links(session) (folder, pull requests, spawnedBy, peer,
  chat), which a new step or state leaves alone. lanes stays for whole-store readers.
- A pull request badge change patches the rows whose badge changed instead of laying the list out
  again (SidebarView render).
- The hover card is not rebuilt (timeline read, refit) when only the hovered row's step changed:
  the card does not show it.
- A row's machine mark image is drawn again only when the mark or its size changes (#22763 made a
  new NSImage on every configure).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lawrencecchen added a commit that referenced this pull request Oct 22, 2026
…by default

Lawrence 2026-10-21: make it fast, but off by default. sidebar.laneStatus and sidebar.rowStatus
(on since #23390) and sidebar.nowLine (#23474) stay in Debug; a row reads no acpmux lane unless
one of them is on. With them on, an agent event now maps its own row only.

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

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 98e8a08b Deployed Oct 21, 2026 by teamleaderleo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant