Skip to content

cmux NEXT Home: a lone Chief in a narrow column takes no avatar column; the chat gets the width - #24006

Merged
teamleaderleo merged 10 commits into
feat-cmux-nextfrom
chief-ui-lone-rail
Oct 22, 2026
Merged

teamleaderleo merged 10 commits into
feat-cmux-nextfrom
chief-ui-lone-rail

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

In a narrow Home column (list below 180 pt, the avatar-only list from 33224e0), a list that holds only the Chief drew a ~120 pt column with one lone avatar in it: an empty band beside the chat. Now the avatar-only list takes no column while it holds one conversation and nothing else (no Cloud note, no Message Requests): it's hidden with its divider, the chat spans the split, and compose stays at the chat's top-left as it already does for any avatar-only list. A second conversation, or a column wide enough for the list with names, brings the list back at its width. Everything else in the narrow-list feature (header hiding, compose moving, the 53c99aa clamp) is unchanged.

Debug Settings > Home > "Avatar list for one conversation" (home.loneConversationRail, off by default) keeps the avatar-only column for one conversation.

How: HomeSidebarView.holdsOneConversation (pinned + rows ≤ 1, no note, no requests; the New Message draft row doesn't count) and an onContentChange hook that re-lays out the split. HomeSidebarSplitView.layout() gets one line, if foldLoneConversation() { return }, before the existing placement; folding hides the list subview, shouldHideDividerAt hides its divider, sidebarWidth reports 0 while folded, and place / the divider double-click do nothing while folded. The split follows the tunable through Observation, so toggling it applies without a resize.

Regression test

cmux-ci run --class light --script scripts/ci/package-test-lane.sh --ref <sha> --arg=suite --arg=Packages/macOS/CmuxNext --arg=HomeLoneChiefRailTests --env CMUX_CI_XCODE_APP=/Applications/Xcode_26.6.app

  • Red on the test-only commit 2251a7f: running on the fleet (step 64106f5661539411807c77c8), result to follow
  • Green on the head 00fcd29: running on the fleet (step 2f3c5f3f93437ca4af6d4678), result to follow

The suite covers: one Chief in a 400 pt window (list folded, chat spans 400 pt, compose visible at the chat's top-left); three conversations keep the avatar list at 119 pt; one Chief in a 1000 pt window keeps the 320 pt list with names; fold and unfold as conversations come and go and the window widens and narrows; the Debug toggle keeps the column and turning it off folds the open page.

Observed / Not run

  • Observed: the fleet runs above; cmux-ci compile-check --ref 00fcd2953db --app passed; scripts/cmux-next/prepush-gate.py clean.
  • Not run: the full CmuxNext suite locally (no local builds). Real-app capture: to follow.

Changelog

Fixed: A narrow Home column with only the Chief no longer shows an empty avatar column beside the chat.

Agent session: cosmic-gray

🤖 Generated with Claude Code


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


Summary by cubic

Fixes a narrow Home column whose list holds only the Chief: the avatar-only list no longer draws a lone avatar in an empty band beside the chat. The list and its divider are hidden, the chat spans the split, and compose stays at the chat's top-left.

Behavior

  • The list folds only when the window squeezes the Chief's own width to avatars; a width dragged there keeps the column and its divider to drag it back out.
  • A second conversation, or a column wide enough for names, brings the list back at its width.
  • The fold runs an extra layout pass when hiding or showing the list, so no stale divider line stays at the list's old edge.
  • Debug Settings > Home > "Avatar list for one conversation" (home.loneConversationRail, off by default) keeps the avatar-only column for one conversation; turning it off folds the open page without a resize.

Tests

  • HomeLoneChiefRailTests covers folding and unfolding as conversations come and go and the window widens and narrows, plus the Debug toggle.
  • HomeSidebarSplitDividerTests and ResizeInvariantTests now list two conversations, since a lone Chief folds away in the shrinking tests; a new folded variant checks that no divider line stays at the old 320 pt place.

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

View guided diff Turn on auto-fix

teamleaderleo and others added 2 commits October 21, 2026 18:22
…column; the chat gets the width

The avatar-only list holding one conversation (and no note or requests) is hidden in
HomeSidebarSplitView, with its divider, so the chat spans the split and compose stays at its
top-left. A second conversation or a column wide enough for names brings the list back.
Debug Settings > Home > "Avatar list for one conversation" (home.loneConversationRail, off)
keeps the column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16057-0fea-7933-80f2-cec81447d873&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by chief-ui on cosmic-gray · session

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

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 3603281: 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=pr24006 -f ref=chief-ui-lone-rail -f platform=macos -f backend_mode=local

web and size checks: green

Tour of 3603281a38 (run)

The path-selected recorder failed for core-after, core-before; see the run artifact and failed manifests.

Path-selected interactions

Interaction Changed paths
home Packages/macOS/CmuxNext/Sources/CmuxNextHome/Sidebar/HomeSidebarSplitView.swift
Packages/macOS/CmuxNext/Sources/CmuxNextHome/Sidebar/HomeSidebarView.swift
Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeSidebarSplitDividerTests.swift
core Packages/macOS/CmuxNext/Sources/CmuxNextHome/HomeTunables.swift
Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeLoneChiefRailTests.swift
Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/ResizeInvariantTests.swift

Core UI (before): FAILED — 16 ok, 3 failed, 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 failed: exited 1 cmux action run splitRight
6 Split down failed: exited 1 cmux action run splitDown
7 New terminal tab failed: exited 1 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

Failed steps:

5. Split right
Split right

6. Split down
Split down

7. New terminal tab
New terminal tab

Screenshots (12)

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

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\): FAILED — 16 ok, 3 failed, 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 failed: exited 1 cmux action run splitRight
6 Split down failed: exited 1 cmux action run splitDown
7 New terminal tab failed: exited 1 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

Failed steps:

5. Split right
Split right

6. Split down
Split down

7. New terminal tab
New terminal tab

Screenshots (12)

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

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

### Home list header \(before\): PASS — 27 ok

tour

Video (mp4)

# Step Result Command
1 Launch ok
2 Go to Home ok cmux action run home.show
3 Six conversations, two pinned ok
4 Dark ok
5 Dark 320: header row at rest, a row selected ok
6 Dark 320: search focused ok
7 Dark 320: search ends ok
8 Dark 320: Unread only ok
9 Dark: All again ok
10 Dark 260: the field narrows, the buttons stay ok
11 Dark 260: search focused ok
12 Dark 260: search ends ok
13 Dark 180: the narrowest field ok
14 Dark 140: search is an icon, compact rows select with a disc ok
15 Dark 140: the search opens over the row ok
16 Dark 140: search ends ok
17 Dark 76: the narrowest list, three small buttons ok
18 Light ok
19 Light 320: header row at rest, a row selected ok
20 Light 320: search focused ok
21 Light 320: search ends ok
22 Light 260 ok
23 Light 180 ok
24 Light 140: search is an icon ok
25 Light 76 ok
26 Back to 320 ok
27 Back to the system appearance ok
Screenshots (15)

5. Dark 320: header row at rest, a row selected
Dark 320: header row at rest, a row selected

6. Dark 320: search focused
Dark 320: search focused

8. Dark 320: Unread only
Dark 320: Unread only

10. Dark 260: the field narrows, the buttons stay
Dark 260: the field narrows, the buttons stay

11. Dark 260: search focused
Dark 260: search focused

13. Dark 180: the narrowest field
Dark 180: the narrowest field

14. Dark 140: search is an icon, compact rows select with a disc
Dark 140: search is an icon, compact rows select with a disc

15. Dark 140: the search opens over the row
Dark 140: the search opens over the row

17. Dark 76: the narrowest list, three small buttons
Dark 76: the narrowest list, three small buttons

19. Light 320: header row at rest, a row selected
Light 320: header row at rest, a row selected

20. Light 320: search focused
Light 320: search focused

22. Light 260
Light 260

23. Light 180
Light 180

24. Light 140: search is an icon
Light 140: search is an icon

25. Light 76
Light 76

### Home list header \(after\): PASS — 27 ok

tour

Video (mp4)

# Step Result Command
1 Launch ok
2 Go to Home ok cmux action run home.show
3 Six conversations, two pinned ok
4 Dark ok
5 Dark 320: header row at rest, a row selected ok
6 Dark 320: search focused ok
7 Dark 320: search ends ok
8 Dark 320: Unread only ok
9 Dark: All again ok
10 Dark 260: the field narrows, the buttons stay ok
11 Dark 260: search focused ok
12 Dark 260: search ends ok
13 Dark 180: the narrowest field ok
14 Dark 140: search is an icon, compact rows select with a disc ok
15 Dark 140: the search opens over the row ok
16 Dark 140: search ends ok
17 Dark 76: the narrowest list, three small buttons ok
18 Light ok
19 Light 320: header row at rest, a row selected ok
20 Light 320: search focused ok
21 Light 320: search ends ok
22 Light 260 ok
23 Light 180 ok
24 Light 140: search is an icon ok
25 Light 76 ok
26 Back to 320 ok
27 Back to the system appearance ok
Screenshots (15)

5. Dark 320: header row at rest, a row selected
Dark 320: header row at rest, a row selected

6. Dark 320: search focused
Dark 320: search focused

8. Dark 320: Unread only
Dark 320: Unread only

10. Dark 260: the field narrows, the buttons stay
Dark 260: the field narrows, the buttons stay

11. Dark 260: search focused
Dark 260: search focused

13. Dark 180: the narrowest field
Dark 180: the narrowest field

14. Dark 140: search is an icon, compact rows select with a disc
Dark 140: search is an icon, compact rows select with a disc

15. Dark 140: the search opens over the row
Dark 140: the search opens over the row

17. Dark 76: the narrowest list, three small buttons
Dark 76: the narrowest list, three small buttons

19. Light 320: header row at rest, a row selected
Light 320: header row at rest, a row selected

20. Light 320: search focused
Light 320: search focused

22. Light 260
Light 260

23. Light 180
Light 180

24. Light 140: search is an icon
Light 140: search is an icon

25. Light 76
Light 76

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

@blacksmith-sh

blacksmith-sh Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Found 5 test failures on Blacksmith runners:

Failures

Test View Logs
main.DeveloperIdAppTests.test_download_prints_the_active_profile_only/
test_download_prints_the_active_profile_only
View Logs
stubs: /System/Library/Frameworks/Security.framework/Versions/A/Security has no stub/
Security has no stub
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_CFRetain']/
CoreFoundation, missing 1: ['_CFRetain']
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_log2']/
CoreFoundation, missing 1: ['_log2']
View Logs
test_base_passes (main.NoticesPairTests.test_base_passes)/
test_base_passes (main.NoticesPairTests.test_base_passes)
View Logs

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

… and divider

Fold the lone conversation's list only when the window squeezes the person's own width to
avatars; a width they dragged there keeps the divider to drag it back out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16057-0fea-7933-80f2-cec81447d873&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (claude-reviewer-lone-rail): BLOCK at 7cddc95

Read-only review of d072c32..7cddc95 (00fcd29 plus the own-width fix), REVIEW.md checklist applied.

Blocking

  1. An existing test goes red: ResizeInvariantTests.theHomeTranscriptKeepsItsMinimumThroughResizes (Tests/CmuxNextAppTests/ResizeInvariantTests.swift:61-84). Its HomeSidebarView(frame:) has the default empty model, and holdsOneConversation is true for 0 rows (<= 1, HomeSidebarView.swift:143-145). windowKey is the default "", so nothing is saved and own = standard = 320. At 420 pt, clamp(320, 419) = 139 is compact, 320 is not, and the tunable is off, so the list folds and stays folded at 380 and 300. At 300 the test expects split.arrangedSubviews[0].frame.width to be about 76 (line 82). The hidden list keeps its stale frame (239 from the 520 step) or gets 0. Either way it is not 76, so the test fails. If AppKit zeroes the frame, lines 77-79 (>= 75.5) fail too. The Swift tests were cancelled at 00fcd29, so CI has not shown this yet. Fix: give that test's list two conversations (HomeSidebarModel is public, and PerChiefAvatarTests.chiefRows is in the same target). Alternatively, assert the fold there (sidebarWidth == 0, transcript the full width). That test cannot set split.tunables because it imports CmuxNextHome without @testable.

(At 00fcd29 there was a second blocker: foldsLoneConversation used wantedWidth. A list the person dragged below 180 pt in a wide window would fold on the next layout, which every Chief update triggers through onContentChange. With the divider hidden and the double-click disabled, nothing in the UI could bring it back. That would also have broken HomeNarrowSidebarTests and HomeNarrowSidebarCaptureTests, whose empty-model lists are set to 76-179 pt with setWidth. 7cddc95 fixes this with the own width rule at HomeSidebarSplitView.swift:138-140, and aListDraggedToAvatarsKeepsItsColumn covers it.)

Checked and OK

  • Hidden arranged subview: NSSplitView treats it as collapsed, so adjustSubviews gives the chat the split. shouldHideDividerAt (line 191) returning list.isHidden takes the edge divider to zero thickness. listFolded asserts that the transcript spans the full width.
  • Layout loop: none. The fold transition calls adjustSubviews once. After that isHidden == fold, so layout() returns without setting needsLayout. place sets needsLayout only when hidden, and layout() never reaches place while folded.
  • Unfold happens in the same layout() call. foldLoneConversation unhides, sets placed = false, runs adjustSubviews, returns false, and then the same pass runs place(wantedWidth). The return happens early only on fold.
  • No 0 width is saved. While folded placed stays false, so splitViewDidResizeSubviews returns before save. The divider is hidden, so it cannot be dragged. During unfold placed = false (the adjustSubviews notification) and then placing = true (the setPosition), so neither saves.
  • setWidth (DEBUG) while folded saves the clamped width and marks layout, and the next pass decides fold or unfold with the new own. resetWidth is unreachable from the UI while folded.
  • Tunable follow: the [weak self] re-registering chain has no cycle, and the @MainActor Task reads the new value after willSet.
  • onContentChange fires from update (model, note) and showRequests, which covers every input of holdsOneConversation.
  • Compose: sidebarWidth returns 0 when folded, isCompact(0) is true, so the chat's compose shows, and hides while composing.
  • Keyboard: ⌘F and type-to-search reach the field only through the list's document in the responder chain, so a hidden list takes no keys. AppKit moves first responder off a view when it is hidden.
  • Drop: HomeSidebarSplitView+Drop targets the chat column, which hiding the list does not affect.
  • New tests: 400 pt gives clamp(320, 399) = 119, which is compact, so the fold tests and the 119 pt three-conversation test hold. The 1000 pt window gives 320. aListDraggedToAvatarsKeepsItsColumn fails at 00fcd29 and passes at 7cddc95. The red commit 2251a7f's four tests fail on base. The toggle test cannot compile on base, which is expected.
  • Sibling super.layout() after place(target): compatible. The fold return comes before it, and the two edits touch separate lines.

Non-blocking

  • dividerFrameInWindow (lines 65-74) still reports a 1 pt divider at x = 0 while folded, and DebugHome+Page publishes it as divider for debug.mouse drags. Return nil while list.isHidden.
  • Setting tunables again (lines 35-37) leaves two observation chains, because the shared store's chain re-registers on the new store. Harmless, and only tests do it.
  • onContentChange runs a split layout on every model change (each Chief preview or unread update). It is cheap, but it could fire only when holdsOneConversation flips.
  • In a folded narrow column, a non-empty New Message draft has no list row to go back to the Chief (Escape works only on an empty draft). The palette and widening the window still work.
  • theDebugToggleKeepsTheAvatarListForOneConversation uses a bounded Task.yield loop (REVIEW.md section 6). An explicit wait on the observation would be sturdier.

@teamleaderleo

teamleaderleo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Closed by mistake, now reopened. Leo's "leave it alone, design choice" was about the profile, not this lone-Chief avatar column. The rail fix is wanted: a narrow Home column whose list holds only the Chief should not keep a ~165 pt list column with one avatar in it. The review BLOCK above is fixed in 09a1455.

@teamleaderleo
teamleaderleo deleted the chief-ui-lone-rail branch October 22, 2026 02:01
ResizeInvariantTests.theHomeTranscriptKeepsItsMinimumThroughResizes used an
empty list, which now folds away once it would show avatars only (a lone
Chief takes no column). The test checks the list shrinking to its avatar
minimum, so it gives the list two conversations, as HomeLoneChiefRailTests
does for the list that keeps its avatar column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16057-0fea-7933-80f2-cec81447d873&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

HomeTunables.swift conflicted: feat-cmux-next added home.chatHeader where
this branch added home.loneConversationRail. Both are kept, and both are in
HomeTunables.all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16057-0fea-7933-80f2-cec81447d873&host=cosmic-gray
teamleaderleo and others added 3 commits October 21, 2026 21:30
Picks up 78c71ce (the agent pane's styles carry ui/wave.css): without it the
cmux-next web bundles do not build, so no fleet suite or capture can run.

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

HomeSidebarSplitDividerTests (from #23998) built the split around an empty
list. On this branch an empty list folds away once the clamp would leave it
avatars only, so the clamp test was really testing the fold. Its list now
holds two conversations, as in ResizeInvariantTests. A new folded variant
(one conversation, 400 pt) checks that the list folds and that no divider
line stays at the old 320 pt place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16057-0fea-7933-80f2-cec81447d873&host=cosmic-gray
After the merge of #23998, layout() runs NSSplitView's layout a second time
after place() so that the divider's layer follows a clamp. The lone-
conversation fold returned before that second pass, so the hidden divider's
layer could stay at the list's old edge. The fold now runs the same extra
pass when it hides or shows the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16057-0fea-7933-80f2-cec81447d873&host=cosmic-gray
teamleaderleo added a commit that referenced this pull request Oct 22, 2026
teamleaderleo added a commit that referenced this pull request Oct 22, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (claude-reviewer-24006): SHIP at c1f9a2f

What was checked. Earlier passes: SHIP at 09a1455 (the full PR, including the fix for the ResizeInvariantTests BLOCK above), then BLOCK at ee7f7f6 after the base merge. Two problems there: the base's new HomeSidebarSplitDividerTests built the split around an empty list, which the fold now catches, and the fold path skipped the base's second super.layout(). Since then:

  • fe6a80f merges feat-cmux-next cc027d8. It is clean and adds nothing of the PR's own: git diff cc027d8 fe6a80f is byte-identical to the PR's diff at ee7f7f6 against its old merge base.
  • 705dfc4 changes tests only.
  • c1f9a2f changes one line of source.

The full PR diff against cc027d8 was read (HomeTunables, HomeSidebarSplitView, HomeSidebarView, HomeLoneChiefRailTests, HomeSidebarSplitDividerTests, ResizeInvariantTests). Nothing was built or run.

The ee7f7f6 BLOCK is resolved.

  • Divider tests. split(...) now fills the list with 2 Chiefs by default (HomeSidebarSplitDividerTests.swift:28-31). So holdsOneConversation is false, and aClampedDividerLeavesNoLineAtItsOldPlace and aWidthSetOutsideLayoutMovesTheDividerLayer run the base's place path unchanged. setWidth(150) saves the person's own width, and a compact own width never folds.
  • Layout pass. HomeSidebarSplitView.swift:158 now runs super.layout() after adjustSubviews() when the fold flips. This mirrors the place path at :103.

Calling super.layout() from the helper is correct.

  • No recursion. The helper is private, and only layout() calls it. NSSplitView.layout resizes subviews, consults the delegate and posts splitViewDidResizeSubviews, and none of those re-enter the override.
  • No loop. The block only runs when list.isHidden != fold.
  • Saved width is safe on fold. placed = false comes first, so splitViewDidResizeSubviews (:215) saves nothing through a fold or a resize while folded.
  • Saved width is safe on unfold. Unfolding goes through place with placing = true. The person's saved width is never overwritten.

The new fold test is sound. aFoldedLoneConversationLeavesNoLineAtItsOldPlace goes from 1100 pt to 400 pt with no saved width:

  • own = 320 and clamp(320) = 119, which is compact, so the list folds and sidebarWidth == 0.
  • With shouldHideDividerAt true, the divider has zero thickness. Its layer may be removed, hidden or zero-width, and drawnDividerFrames filters out all three.

No other test is affected. Every test that builds a HomeSidebarSplitView or TopHomePageView with 0 or 1 conversations was checked:

  • HomeNarrowSidebarTests and HomeNarrowSidebarCaptureTests use setWidth (the person's own width) at 900 to 1000 pt.
  • The TopHomePageView tests stay at 1000 to 1200 pt.
  • ResizeInvariantTests uses 2 Chiefs.

Non-blocking:

  • Setting tunables leaves the observation chain on TunableStore.shared alive, which costs an extra needsLayout. Only tests set it.
  • The fold test reads TunableStore.shared, so a developer's local tunables file with home.loneConversationRail on would fail it locally.
  • The fold test could also assert drawn.isEmpty.

teamleaderleo added a commit that referenced this pull request Oct 22, 2026
…s test compile fix (evidence for #24006, not for merge)
teamleaderleo added a commit that referenced this pull request Oct 22, 2026
…ompile fix (evidence for #24006, not for merge)
Picks up #24219 (HomeChiefLinkInkTests compiles again), which unblocks the
CmuxNext test build on this PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 22, 2026
teamleaderleo added a commit that referenced this pull request Oct 22, 2026
…ividerTests (red evidence only, not for merge)
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (claude-reviewer-24006, sonnet): SHIP at 29dcefb

This is a different-model review (sonnet), read-only, from git diff eaccc63..29dcefb in the lane's worktree. I ran no builds or tests. I applied the REVIEW.md checklist and found no blocking issues.

  1. Fold rule. foldsLoneConversation depends only on holdsOneConversation, the saved or standard width, bounds.width (through clamp) and the tunable. It never reads the list's frame, so hiding the list cannot flip the decision. Layout settles in at most two passes.
    • The extra super.layout() is NSSplitView's own implementation, so it cannot recurse. It is the same pattern as the existing call after place.
    • The saved width is never overwritten. splitViewDidResizeSubviews saves only when placed && !placing and a divider index is given, and a fold or unfold sets placed = false.
  2. Divider. shouldHideDividerAt returns list.isHidden, and sidebarWidth is 0 while folded, so compose shows at the chat's top-left. The place() and double-click guards hold. A list dragged to avatar width keeps its column.
  3. Content changes. holdsOneConversation and onContentChange cover the model, the note and the requests button. The closure captures [weak self], so there is no cycle.
  4. Observation re-arm. Both closures capture [weak self]. The chain ends when the view goes away. The Task pattern is allowed in CmuxNextHome.
  5. Tunable registration. HomeTunables.all has both chatHeader and loneConversationRail.
  6. Tests.
    • The fold arithmetic matches: 400 pt gives a 119 pt clamp.
    • ResizeInvariantTests and HomeSidebarSplitDividerTests seed two conversations, so they still test the clamp.
    • The other narrow tests save a compact width themselves, and the TopHomePageView tests run at 1000 to 1200 pt, so none of them fold.
  7. User-visible. Compose stays reachable. The hidden list drops out of accessibility. The stale divider line is handled and tested.

Non-blocking:

  • An empty list (0 conversations) also folds, so a narrow column could flash once at launch while rows load.
  • The sidebar search field can't be reached while folded (⌘F). The comment at HomeSidebarView.swift:284 is stale in that state.
  • Setting tunables after init leaves the first observation chain armed. This only affects tests.
  • The debug-only setWidth(>=180) on a narrow lone-Chief page stays folded.

@cursor

cursor Bot commented Oct 22, 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.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / tooling-tests is red on 17 of 17 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.

@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/39788910738 at e70f2c3 failed: cmux-next fast tier.
Those jobs last passed at c04ccb2. This pull request is one of 4 merged since: #23926, #24114, #24347, #24006.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

Shipped in:

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 3603281a Deployed Oct 22, 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