Skip to content

cmux-next sidebar: workspaces marked Done fold into one Done (N) row - #23633

Merged
teamleaderleo merged 8 commits into
feat-cmux-nextfrom
leo/sidebar-done-fold
Oct 21, 2026
Merged

teamleaderleo merged 8 commits into
feat-cmux-nextfrom
leo/sidebar-done-fold

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

This is slice S4 of Leo's sidebar-organization ask (item A, the "Done (N)" fold).

What changes

Workspaces you marked Done (Mark as Done, Set Workspace Status → Done) leave their place and list behind one "Done (N)" row at the end of their group, or at the end of the loose rows. Click the row to show them below it; click again to fold them.

  • Order: nothing reorders the model. The other rows keep their manual order, and the Done ones keep theirs behind the fold.
  • Kept in place: the active workspace, even when it is Done. While filtering, every match shows where it is. With the session tree on, nothing folds (the tree nests rows under others).
  • What redraws: marking a workspace Done (or not) is now a structural change, so the list lays out again instead of patching the row (SidebarModel.movesRows). A group holding Done workspaces folds and opens by a full layout instead of the splice.
  • The row: a chevron and "Done (N)" in the folder header's quiet style, one line high, indented with the group's members. It is no drop target, has no rename, and right-click shows its group's or section's menu. VoiceOver reads it as a disclosure triangle.
  • While a drag runs, every Done workspace shows in its place (drops resolve by the drawn rows), and the fold comes back after the drop.
  • Selected from elsewhere: a Done workspace chosen from the palette, Cmd-number or the socket shows in its place (the selection patch hands over to a layout). After you leave a Done workspace by a click, it stays drawn until the next layout, as a collapsed group keeps the member you left, so nothing moves under the pointer.
  • State: which Done rows are open is view state of the list (SidebarListView.openDoneFolds), not saved.
  • Toggle: Debug toggle sidebar.doneFold, on by default.
  • Strings: one new string, "Done (%lld)", in all 21 locales.
  • Size: SidebarLayout.swift shrinks (474 → 461 lines): its row helpers visible, listsTabs and disclosure moved onto SidebarLayoutOptions, and the filter's path-to-match onto a static helper.

Tests

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

  • Done workspaces fold into one row at the end, in a group and in the loose rows;
  • the row counts its workspaces;
  • an open fold lists them in their order;
  • the active workspace keeps its place;
  • off, and while filtering, every row is in place;
  • a group with Done workspaces is not spliced;
  • marking Done is a structural change;
  • the string carries the count;
  • a Done workspace selected elsewhere shows.

Capture

Three workspaces marked Done (with the app's Mark as Done action) fold into one closed "Done (3)" row under the others.

Capture steps-386849ff85 at 7c19da8f8742 (fleet build, capture mini, text checked for private data)

Light Dark
light dark

Review

  • BLOCK at d5b9dd3: drops near a closed Done row, the drop gap, and a Done workspace selected elsewhere. Fixed in 87531eb, with a test.
  • SHIP at 87531eb. Follow-ups from it: the first event of a drag from outside the sidebar can name the wrong row until the next move; rows jump when a drag starts while Done rows are folded; a dragged group's slot is taller than its card.
  • After it: the chevron sat over the count (moved before it), and turning sidebar.doneFold on or off now lays the list out again. Fleet compile passed at 7c19da8.

Changelog

  • Added: Workspaces marked Done fold into one "Done (N)" row at the end of their group or list.
    Agent session: cosmic-gray

🤖 Generated with Claude Code

teamleaderleo and others added 2 commits October 21, 2026 11:28
…at the end of their list

A group's members and a section's loose rows keep their manual order; the ones marked Done
(Mark as Done, SidebarTaskStatus.done) list behind one "Done (N)" row at the end, which a
click opens below it. The active workspace keeps its place. No fold while filtering or with
the session tree. Debug toggle sidebar.doneFold, on by default.

The row helpers visible/listsTabs/disclosure move from make onto SidebarLayoutOptions so
SidebarLayout.swift shrinks.

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
@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 1a7130a: 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=pr23633 -f ref=leo/sidebar-done-fold -f platform=macos -f backend_mode=local

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

CI passes on 1a7130aef1 (run 39674440777 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.

red: cmux-next checks (god files, concurrency, crash safety, l10n); base red: the same failure on the base cb27a38; does not block

@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
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

@blacksmith-sh

This comment has been minimized.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (claude-review-subagent): BLOCK at d5b9dd3

I read the full diff against merge-base 7771639, plus the drop resolver (cmux-tui/crates/cmux-layout-reducer/src/sidebar_drop.rs), SidebarListView+SelectionPatch.swift, SidebarView+Observation.swift and SidebarEmptyArea.swift. I didn't build or run anything.

Blocking

1. Drops near a folded "Done (N)" row land in the wrong place, and you can no longer drop below a group's end.
RustRow.resolvable leaves out .doneFold rows. The resolver's hit() picks the last resolvable row whose y <= pointer and clamps the fraction to 1.0, so a pointer over a Done row resolves against the visible row above it.

  • Group fold: the group's last visible member (g2 in the test fixture) now has isLastInGroup == false, because the fold row is last. The group_exit_fraction branch in workspace_target never fires. A drop on the lower part of g2, on the group's Done row, or in the group's bottom padding stays inside the group at g2.siblingIndex + 1. Before this change, the bottom edge of the last member moved the drop out of the group to parent_index + 1.
  • Section-level fold placed after a group (the fixture's order: … g, g2, Done(g), Done(loose)): a drop over the loose "Done (N)" row also resolves to g2, so the workspace goes into group G between g2 and the hidden g3. The person dropped on a row in the loose list and the workspace ended up inside the group. When the group is the section's last node, no position remains that drops after the group at section level.
  • Suggested fix: make the fold row resolvable as the end of its list. For a group, map it to its last member, or send it as a member row with isLastInGroup and siblingIndex = members.count - 1 so the exit fraction still works. For the loose list, map it to nodes.count. Alternatively, give the last shown member isLastInGroup = true when the fold row is closed. Add a resolver test for both rows.

2. A drop gap never opens when the target index belongs to a hidden Done member.
openGapIfNeeded matches on the exact model index. With the fold closed, the resolver's usual target "after the visible row X" is X.siblingIndex + 1. In the fixture that is b, d or g3, which are hidden, so gapY stays nil. This affects internal drags (o.gap = position) and external new-workspace proposals. Dragging below a or c in the loose list, or below g2 in the group, shows no gap at all, even though the drop still commits. Open the gap at the first drawn entry whose model index is >= gap.index (or before the fold row) instead of requiring an exact match.

3. Selecting a hidden Done workspace from somewhere other than the list leaves it undrawn.
Cmd-1…9 use itemOrder, which is model order and still includes the folded workspaces. The palette and the socket can also select them. The resulting selection change, with no row change, goes through patchSelection. Its only "needs layout" check is model.isInCollapsedGroup(id) && displayed.row(for:) == nil. A folded Done workspace isn't in a collapsed group, so newKey becomes nil, the patch returns true and nothing reloads. The active workspace isn't drawn or revealed, which breaks the PR's "the active workspace keeps its place" contract until some unrelated reload runs. Make the guard also return false when the new active workspace is a Done workspace with no displayed row (or, more generally, any listed workspace with no row while not filtering), and add a test next to the collapsed-group one.

Checked and fine

  • visible, listsTabs and disclosure moved onto SidebarLayoutOptions unchanged. pathToMatch is the same algorithm, only computed while filtering, as before.
  • foldingDone doesn't run while filtering, with the session tree, with the toggle off, or when nothing is Done. Order is kept: shown entries, then the fold, then the Done entries when the fold is open. isLastInGroup is right for the last Done row after an open fold.
  • SidebarLayout+Fold: refusing to splice a group with Done members (falling back to the full layout) is safe. listOptions() never sets sessionTree, so with the tree on the splice is just refused more often than needed: conservative, not wrong. Groups without Done members splice exactly as before.
  • movesRows returning true on a Done flip (when the toggle is on) makes patchWorkspaces lay out again, and markingDoneMovesTheRow covers it.
  • All updated switch sites are exhaustive. SidebarDocDrag skips the fold row. The context menu targets the fold row's group or section. Drag start and rename ignore the fold row. Clicking it toggles openDoneFolds and reloads.
  • SidebarDoneFoldRowView: DecorativeImageView chevron, disclosure-triangle role with label and expanded state, Palette colours under performWithTheme, iconRowSize exists. Fine.
  • sidebar.doneFold.title: 21 locales, all %lld, all translated.

Nits

  • appendDoneFold gives the fold row siblingIndex: 0. It is never resolved today, but any future consumer that reads siblingIndex from rows that have a group would treat it as member 0. Use the end index (or -1) instead.
  • The SidebarDoneFoldTests.markingDoneMovesTheRow test changes the shared TunableStore without restoring the store's activation state. The suite is .serialized, but the global can still leak into other suites.
  • When the person clicks another workspace, the previously active Done workspace stays drawn in place until the next reload. That's intentional calm movement, but the description should say so.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next crash-class rules / cmux-next crash-class rules is red on 9 of 9 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.

teamleaderleo and others added 2 commits October 21, 2026 11:46
…pace selected elsewhere shows

Review of #23633: drops resolve by the drawn rows' model indexes, so a drag lays the list out
with every Done workspace in its place. A selection patch whose new active workspace is listed
but not drawn hands over to a layout, which keeps the active workspace in place.

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (claude-review-subagent): SHIP at 87531eb

All three findings from the earlier BLOCK are fixed. Read with local git only (incremental diff d5b9dd3..87531eb, plus the drag, tab-drop, settle and selection paths). Nothing was built or run.

Drag layouts. Every list layout built during a drag goes through options(includeGap:):

  • internal drag: displayLayout and its reorder branch, updateDrag base, updateDocDrag geometry base
  • tab drop: externalDragMoved and externalDragRefusal bases
  • settle: shownDrag covers settling?.drag

So the resolver, the gap and the displayed rows all see the unfolded layout while drag, settling or external is set. With the Done rows back in their places, the BLOCK's (1) and (2) no longer apply. The list folds again on the first reload after the drag ends:

  • plain drop and cancel: self.drag = nil, then reload
  • document drop: settleDrop() clears settling before displayLayout() in the same reload
  • external drag: endExternalDrag sets external = nil, then reload

The model-level layouts (VisibleRows, ItemOrder) stay folded, which is correct because they are not drag geometry.

Drag start. beginDrag reads the folded displayed (rowFrame, lift content, restFrame, grabOffset), then showDragLayout() switches to the unfolded layout before any updateDrag. After that, every hit test uses the new displayed/base pair. Values kept from the folded layout are used only for size, x and grab offset, not as y positions, so nothing resolves against stale frames. land takes its destination from the live row views or dropped(). If the dropped item folds away, the card finishes where it is.

Selection patch. The new early-out returns false only when the new active workspace is listed but not drawn. lists() is O(1) through rowIndex. Hidden-workspace mode already returned false via displayed.rows.isEmpty. The only other new reloads come from selecting into a collapsed section or a leaving row: these are rare, happen once per selection change, and an extra reload there is harmless. The fold keeps the active workspace in place (foldingDone), so the reload shows it.

Test. aDoneWorkspaceSelectedElsewhereShows is sound:

  • the TunableStore activate, register and set pattern matches CompactTabStripTests
  • the defer resets the override
  • with external/drag/settling/landing all nil, patchSelection reaches the new check, and the test then reloads and checks the row

Optional: #require that the set returned .bool(true). The tunable already defaults to true.

Non-blocking notes:

  1. externalDragMoved, first event: the code sets self.external and then, before any reload, maps point.y through the folded displayed but resolves it against an unfolded base. For that one event, the proposal and highlight can name the wrong row. A nil proposal also triggers no reload, so the mismatch lasts until the first non-nil proposal. It corrects itself on the next draggingUpdated. A simple fix: reload(animated: true) when external is first set and the fold hides rows.
  2. Every drag start now unfolds every Done list. The fold defaults to on, so rows below jump at lift and fold back at drop. That is a lot of movement under "calm sidebar movement"; consider unfolding only while the pointer is near a fold, or mapping hidden indexes. This is a design choice, so it does not block.
  3. A group drag: the card is the folded block (header plus non-Done members), but the slot from displayLayout holds every member. The slot is N rows taller than the card. This is cosmetic.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / cmux-next localization debt ratchet is red on 13 of 15 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.

@blacksmith-sh

This comment has been minimized.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (claude-review-subagent): SHIP at 7c19da8

Scope: git diff 87531ebf0ef1..7c19da8f8742 (two commits since the last SHIP).

  • Observation: foldsDone = SidebarTunables.doneFold.value is read in RenderState.init, which runs inside the ObservationStream { ObservationTrigger(RenderState(model, design)) } closure, so changing the tunable fires a new state. This is the same wiring as workspacesAsTabs. render counts a foldsDone change as rowsChanged, and the layout pass reads SidebarTunables.doneFold.value into SidebarLayoutOptions.foldsDone (SidebarModel.swift:526), so the list is laid out again with the new value.
  • Layout: the chevron sits at titleLeading - labelCellPadding and the count starts at chevron.maxX + space1, so they no longer overlap. The name width is clamped with max(0, …), so it never goes negative. The old max(0, x - icon.width - space1) clamp is no longer needed because start doesn't depend on the icon width.
  • Isolation: nothing new. The read happens in the same place, under the same isolation as the existing workspacesAsTabs read.

No blocking findings. Not built or run, per the review scope.

…bar-done-fold

# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextSidebar/Logic/SidebarTunables.swift
@blacksmith-sh

blacksmith-sh Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Found 9 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmp2m5pe5dy/summary-fail.json (live):/summary-fail.json (live): View Logs
/tmp/tmpx7ityqkw/summary-missing.json (live):/summary-missing.json (live): View Logs
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows/
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows
View Logs
[new_workspaces] the window draws no frames/[new_workspaces] the window draws no frames View Logs
[tabs] debug.tab_switch timed 0 of 40 switches/
[tabs] debug.tab_switch timed 0 of 40 switches
View Logs
[tabs] the window draws no frames/[tabs] the window draws no frames View Logs
convention violations found (ERROR lines above)./
convention violations found (ERROR lines above).
View Logs
test_a_file_a_test_reads_selects_that_test (main.PullRequestTiers.test_a_file_a_tes
t_reads_selects_that_test)/
test_a_file_a_test_reads_selects_that_test (main.PullRequestTiers.test_a_file_a_tes
t_reads_selects_that_test)
View Logs
test_a_shared_module_selects_its_dependents (main.PullRequestTiers.test_a_shared_mo
dule_selects_its_dependents)/
test_a_shared_module_selects_its_dependents (main.PullRequestTiers.test_a_shared_mo
dule_selects_its_dependents)
View Logs

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

…d.title added)

The catalog had come out with its keys reordered, a +4082/-3951 diff for one added key.

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 1a7130a

Scope: only the new commit since the last SHIP (d77cefa..1a7130a). Read with local git and a Python json comparison; nothing built.

  • git diff d77cefa..1a7130a touches only CmuxNextSidebar/Resources/Localizable.xcstrings (842/842 lines, a reorder).
  • The parsed catalog at 1a7130a equals the one at d77cefa: same keys, same values, same top-level fields. Only key order changed.
  • Against the merge base cb27a38 (git diff --numstat origin/feat-cmux-next...1a7130a): 131 additions, 0 deletions, one hunk. The only new key is sidebar.doneFold.title (21 localizations, en "Done (%lld)"). No base key was removed or changed, and base key order is kept.
  • The file parses as JSON and ends with a newline, like base.

No findings.

@teamleaderleo teamleaderleo added the ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds label Oct 21, 2026
@teamleaderleo
teamleaderleo merged commit a4c9533 into feat-cmux-next Oct 21, 2026
@teamleaderleo
teamleaderleo deleted the leo/sidebar-done-fold branch October 21, 2026 23:58
teamleaderleo added a commit that referenced this pull request Oct 22, 2026
Conflicts with the Done fold (#23633), every row's PR badge (#23495) and machine marks (#22763):
- Loose rows: the tree's rows (depth, chevron, roll-up) go through foldingDone, which leaves
  them as they are while the tree is on, so the Done fold and the tree compose.
- SidebarSessionTree keeps base's pullRequests beside the folds; the list and observation read
  both `drawn` (the tree with folds) and `pullRequests`.
- WorkspaceRowView adds base's machineMark and this branch's fold button and roll-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d2c-88e4-7fc2-b58e-9a55abdaced8&host=cosmic-gray
teamleaderleo added a commit that referenced this pull request Oct 22, 2026
Brings in the Done fold (#23633). Group by Status keeps working with it:
- every SidebarRowKey switch takes both .status and .doneFold;
- status rows use the base's SidebarLayoutOptions.visible, listsTabs and disclosure
  (the port's own copies are gone);
- movesRows keeps the Done fold's move (a row marked Done, or no longer);
- the Done fold does not apply inside status sections: a workspace marked Done shows
  under its Priority state.

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 added a commit that referenced this pull request Oct 22, 2026
…w N more (#23671)

* test(cmux-next sidebar): Done workspaces fold into one Done (N) row at the end of their list (red)

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

* cmux-next sidebar: workspaces marked Done fold into one Done (N) row at the end of their list

A group's members and a section's loose rows keep their manual order; the ones marked Done
(Mark as Done, SidebarTaskStatus.done) list behind one "Done (N)" row at the end, which a
click opens below it. The active workspace keeps its place. No fold while filtering or with
the session tree. Debug toggle sidebar.doneFold, on by default.

The row helpers visible/listsTabs/disclosure move from make onto SidebarLayoutOptions so
SidebarLayout.swift shrinks.

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

* test(cmux-next sidebar): a Done workspace selected elsewhere shows in its place (red)

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

* cmux-next sidebar: Done rows show while a drag runs, and a Done workspace selected elsewhere shows

Review of #23633: drops resolve by the drawn rows' model indexes, so a drag lays the list out
with every Done workspace in its place. A selection patch whose new active workspace is listed
but not drawn hands over to a layout, which keeps the active workspace in place.

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

* cmux-next sidebar: the Done row's chevron sits before its count, not over it

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

* cmux-next sidebar: turning the Done fold on or off lays the list out again

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

* test(cmux-next sidebar): a long group shows its first rows then Show N more (red)

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

* cmux-next sidebar: a long group or list shows its first rows then Show N more

Past sidebar.groupRowLimit rows (Debug tunable, 10 by default, 0 shows all) a group's members
or a section's loose rows show their first rows, the ones that need you and the active one,
then a "Show N more" row; a click lists every row with a "Show fewer" row. Done workspaces
fold first and count toward neither. The rows show whole while a drag runs, a group past the
limit folds by a full layout, and needing you is a row move past the limit.

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

* test(sidebar): groups past a section's row limit stay shown (red)

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

* fix(sidebar): a section's row limit counts its loose workspaces only; its groups always show

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

* sidebar strings: keep the catalog in base order (only sidebar.doneFold.title added)

The catalog had come out with its keys reordered, a +4082/-3951 diff for one added key.

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

* sidebar strings: keep the catalog in base order (only the Show more strings added)

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

* sidebar: Show more is off by default (sidebar.groupRowLimit 0)

A limit of 10 by default hid rows that existing behavior depends on
(SidebarCloseWhileScrolledTests scrolls to row 20 of 40). Ship the limit as an
opt-in Debug tunable; turning it on is its own change.

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

* cmux NEXT sidebar: say why a click keeps the old active row and why Group by Folder has no row limit

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Shipped in:

teamleaderleo added a commit that referenced this pull request Oct 26, 2026
Leo's ask-13d9bbd5e3: a drag must not reposition other rows. Since #23633 a drag
unfolds every "Done (N)" row at the lift (every Done workspace shows in its place and
the rows below move down), then folds them again at the drop. Red on base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray
teamleaderleo added a commit that referenced this pull request Oct 30, 2026
…ow no longer move down and back (#24361)

* cmux-next sidebar: test that a drag keeps the Done rows folded (red)

Leo's ask-13d9bbd5e3: a drag must not reposition other rows. Since #23633 a drag
unfolds every "Done (N)" row at the lift (every Done workspace shows in its place and
the rows below move down), then folds them again at the drop. Red on base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray

* cmux-next sidebar: a drag keeps the Done rows folded; a drop gap at a folded index opens where the row will show

The lift and the drop moved every row below a Done fold (the fold opened for the drag
and closed at the drop). Drops already resolve by model indexes, which a fold keeps, so
the drag keeps the folds; a drop gap whose index the fold hides opens before the next
drawn row, or before the Done row. Show N more (off by default) still opens for a drag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray
Sidebar-Look: no visual change

* cmux-next sidebar: the older drop resolver ends a group at its last drawn member before its Done row

With the Done rows folded during a drag, the group's last drawn member was not its last
row (the Done row is), so the older resolver never let a drop past it leave the group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray

* cmux-next sidebar: review fixes: a group a Done row ends reads as unfolded to the older resolver; an open fold's gap opens above it

Codex review of #24361 at 7ef5886:
- An all-Done group drew only its header, so a drop below it went into it: the header
  reads as folded, so its bottom leaves the group.
- A last drawn member with tab rows: the tab rows join the member, so below them leaves
  the group (the resolver's tab rows never leave one).
- An open Done fold draws its rows below it, out of model order: a gap at one of their
  indexes opens above the fold, where the new row will show.
Tests resolve real drops through the Rust resolver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray

* cmux-next sidebar: drag-keeps-done-fold test sets the tunable inline (main-actor calls outside a nonisolated closure)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray

* cmux-next sidebar: test that a drag an open Done fold would split draws its rows together

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray

* cmux-next sidebar: a drag an open Done fold would split (Done and unfinished rows together) unfolds, so its rows slide as one slot

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray
Sidebar-Look: no visual change

* cmux-next sidebar: any drag that carries a Done row draws unfolded (a lone Done row would add a fold row the base lacks)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray
Sidebar-Look: no visual change

* cmux-next sidebar: tests for an open fold's trailing tab rows and a group drag's Done row

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray

* cmux-next sidebar: an open Done fold's last workspace and its tab rows end the group for the older resolver

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16040-fd0b-7dd0-9487-1a3a198d9f5c&host=cosmic-gray

* test(sidebar): preserve external tab targets around Done folds

Adds the external tab drag regression found by independent review. Native red execution is pending hosted verification; no native tests ran on GCP.

Sidebar-Look: no visual change
Agent-Session: https://cmux.com/deeplink/agent?harness=codex&session=01a1754c-2766-7023-bf29-cd2c77d2b6be&host=gcp-codex-lane-10

* fix(sidebar): keep external tab insertion targets during drags

Normalize folded group endings only for internal workspace drops. External tab drags keep their existing unfolded geometry and preserve each visible tab target. This avoids losing before-tab targets or trapping the bottom group exit. Native execution remains pending hosted tests.

Sidebar-Look: no visual change
Agent-Session: https://cmux.com/deeplink/agent?harness=codex&session=01a1754c-2766-7023-bf29-cd2c77d2b6be&host=gcp-codex-lane-10

* fix(sidebar): a folded group that still draws items stays open for the drop resolver

A group whose Done fold hides every workspace but still shows link, tab or
app rows read as folded, so a drop on its header appended after the items
instead of going to the group's top. Review P2 at e1a0ba1.

Sidebar-Look: no visual change

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-8ac862c5f65d08f98445

* fix(sidebar): a link below a folded group's last workspace keeps that workspace in the group for the resolver

Codex review (BLOCK at 40bbd1c): endingGroup marked the last drawn workspace isLastInGroup
even when a link or app row of the group is drawn below it, so a drop on that workspace's lower
edge left the group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-8ac862c5f65d08f98445

* docs(sidebar-owner): log #24361's drop-resolver commits (no visual change)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-8ac862c5f65d08f98445

* release-notes: give #24361's highlight a way to try it

test_the_repository_highlights_validate needs a tryIt action or a docs link per entry.

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

* fix(sidebar): a group that draws an open subgroup stays open for the drop resolver

A Done fold that hides every direct workspace no longer reads the header as
folded while a nested subgroup still shows below it, so a drop on the header's
middle goes into the group (Sol review on #24361).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-8ac862c5f65d08f98445

* docs(sidebar): log the subgroup resolver fix in the sidebar owner note

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-8ac862c5f65d08f98445

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 1a7130ae 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

ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant