Skip to content

fix(next): the sidebar's content hold changes the content's frame once; per-frame trace of both modes - #30064

Merged
teamleaderleo merged 6 commits into
feat-cmux-nextfrom
lane/sidebar-toggle-no-bounce
Nov 1, 2026
Merged

teamleaderleo merged 6 commits into
feat-cmux-nextfrom
lane/sidebar-toggle-no-bounce

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Toggling the sidebar no longer shifts the content and snaps it back in either sidebar mode. This PR fixes the opt-in content hold so it changes the content's frame once and leaves it still. It also adds a per-frame trace that proves both modes reach their landing with no bounce.

Leo:

when i toggle the sidebar on and off I STILL SEE THE ENTIRE OTHER PART OF THE APP SHIFT LAYOUT FOR NO REASON AND SNAP BACK

Cause

The slide's content hold (MainPaneCardView.contentHold) was added by the GPU audit of 2026-10-21 so panes lay out once per toggle, not on every frame. It kept the content at the widest width of the slide and pinned it to the sidebar's moving edge:

  • Show: the old wide layout rode the sidebar's edge across the window, its far side clipped, then laid out narrower when the slide landed. Centred and trailing content slid right and jumped back.
  • Hide: the content was laid out at its final width at once, but at the card's old edge, then rode back.

#29894 (a carry of #28894), which landed while this was in flight, made the default sidebar.slideHoldsContent: false. The content now follows the card on every frame, so the default no longer snaps. This PR keeps that default.

Fix

The hold, still in Debug Settings, now takes the card's landing width when the slide starts. It pins the content to the card's far edge, which doesn't move (ContentHold.pinsTrailingEdge: the trailing edge for a left sidebar, the leading edge for a right one). The content is laid out once, on the first frame, at its final size and place, and clearing the hold at the landing changes nothing. That's still one layout per toggle, so the GPU audit's saving holds when it's on. A reversal mid-flight lays out once more, at the new landing width. The rail style, the sidebar's own animation and the default path are unchanged.

Performance

Per-frame trace from SidebarToggleContentTraceTests: real display ticks, a 1100x700 window, a 300 pt left sidebar. Each sample is the content's frame in window coordinates on one display frame. Steps: red 8ee6ecb4d5e2d4e91b046149 at 0a4c827a92f (base plus tests), green d5b6044761f6125b5e255ae5 at c9f93278319.

Metric (one toggle) Before After Delta Harness
Hold on, show: content frame changes 15 (rides the edge 6 → 47 → 79 → … → 306 at 1088 wide, then snaps to 788) 1 (306+788 from frame 1) −14 trace, steps above
Hold on, hide: content frame changes 11 (241 → 204 → … → 6 at 1088 wide) 1 (6+1088 from frame 1) −10 same
Hold on: content layouts per toggle 1 1 0 same
Default (follow), show: frames that turn back toward the start 0 (13 steps 6+1088 → 71+1023 → … → 306+788) 0 (14 steps, 42+1052 → … → 306+788) 0 same
Default (follow), hide: frames that turn back toward the start 0 (11 steps) 0 (11 steps, 249+845 → … → 6+1088) 0 same
Default (follow): changes after the slide lands 0 0 0 same

The default is #29894's behaviour on both sides. The trace pins it: the content's minX, maxX and width only move toward where they land, and nothing changes after the landing. It does resize the content on every frame, the cost the GPU audit measured at 6.7–7.4 ms of main thread a frame. The hold avoids that.

Tests

Red first: 0a4c827a92f is the base's production code plus the tests; the fix is c9f93278319.

cmux-ci run --class light --script scripts/ci/package-test-lane.sh --display --env CMUX_TEST_REQUIRE_GUI=1 --arg=suite --arg=Packages/macOS/CmuxNext --arg=SidebarSlideHoldsContentTests,SidebarToggleContentTraceTests,SidebarToggleSettlementTests

  • Red at 0a4c827a92f: SidebarSlideHoldsContentTests fails 3 of 9:

    • hide: the content moved 208 pt on screen;
    • show: the content stayed 988 wide instead of its 780 landing width, and the landing changed its frame;
    • turnaround: also fails.

    SidebarToggleContentTraceTests fails for the hold, with 15 and 11 frame changes; its default cases pass.

  • Green at c9f93278319: SidebarSlideHoldsContentTests passes 9 of 9, SidebarToggleContentTraceTests passes all 4 cases (hold and default, show and hide), and SidebarToggleSettlementTests (fix(next): sidebar toggle content follows its frame, no landing snap (carry #28894) #29894) passes.

Observed / Not run

  • Observed: the runs above, on fleet display workers (Xcode 26.6, macOS SDK 26.5). The pre-push gate passes. An independent Opus review at c9f93278319 returned SHIP.
  • Not run: a full app session with a live transcript, terminal and Chromium page. The trace uses a plain content view. Only a left sidebar was traced; the right sidebar is covered by unit tests.

No capture: the change is motion and opt-in; the trace is the evidence.

Prior art: #29894 / #28894 (the default), #24342 (introduced the hold), #18840 (classic sidebar), #21125 (right-sidebar pane widths). Owner note in plans/cmux-next/sidebar-owner.md.

Changelog

Fixed: With "Hold content size in sidebar slide" on (Debug Settings), showing or hiding the sidebar no longer shifts the rest of the window and snaps it back.

🤖 Generated with Claude Code

Agent session: agent session

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5 medium): SHIP at 31512f8

The review ran at 525b63edb7adb4fc74261a0875472dda65441827. This head is the same change rebased onto newer base, with identical git patch-id --stable (a7e1f37e003d); the test commit was split only so it compiles on base for the red run.

Findings: the landing width contentHost.bounds.width + from - to matches the card's real landing width (the card's sidebar-side edge follows the sidebar one to one; the gutter and the far edge are fixed). Pinning to the far edge keeps the content still on screen for a left or right sidebar, and clearing the hold at the landing changes nothing. A reversal lays out once more, at the new landing width. The rail style and multi-view never hold. Terminals hold their grid through the slide as before. The CEF clip, overlay rings and shade are fine whether the content is wider (hide) or narrower (show) than the card. The tests fail on base and pass on the fix (fleet steps 5d0108c76b3d8e02b79a624d red, 86b58b194a8bd88bb5230d73 green).

Follow-ups:

  • [follow-up] During a show on non-floating panes, an empty strip shows between the card's moving edge and the narrower content (square content corners) for the length of the slide.
  • [follow-up] A window resize or right-panel width change mid-slide makes the held landing width stale, so the landing lays out a second time (the old code had the same class of problem).
  • [follow-up] The title row, pinned to the sidebar's edge, still lays out every frame of the slide; with bottom tab bars the card's top inset changes once per toggle (both predate this PR).
  • [follow-up] SidebarToggleContentTraceTests needs a display (CMUX_TEST_REQUIRE_GUI=1), so it's advisory display coverage only.
  • [follow-up] Doc comments in OverlayPlane.roundedClip and aSquareMainPaneClipsOnlyWhileItHolds still say "held wider than the card".

@teamleaderleo teamleaderleo added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 1, 2026
teamleaderleo and others added 2 commits November 1, 2026 03:16
Red on the base: the slide holds the content at its widest width and
rides the sidebar's moving edge, so a show slides the old wide layout
across and lays it out narrower at the landing, and a hide lays it out
wide at the card's old edge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-2e370042acb26e63cf31
…ys put

Leo: "when i toggle the sidebar on and off I STILL SEE THE ENTIRE OTHER
PART OF THE APP SHIFT LAYOUT FOR NO REASON AND SNAP BACK".

The content hold (sidebar.slideHoldsContent, opt-in since #29894) kept the
widest width of the slide and rode the sidebar's moving edge. It now takes
the landing width when the slide starts and is pinned to the card's far
edge, so the content's frame changes once per toggle, to where it lands.
Still one layout per toggle (GPU audit 2026-10-21). The default (content
follows the card every frame, #29894) is unchanged.

Sidebar-Look: when i toggle the sidebar on and off I STILL SEE THE ENTIRE OTHER PART OF THE APP SHIFT LAYOUT FOR NO REASON AND SNAP BACK
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-2e370042acb26e63cf31
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

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

Review c14846c: 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=pr30064 -f ref=lane/sidebar-toggle-no-bounce -f platform=macos -f backend_mode=local

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

CI passes on c14846c9c2 (run 41284716940 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.

web and size checks: green

Default changes

This pull request changes 1 default at c14846c9c2a3 (what everyone gets without configuring anything):

  • tunable-source.CmuxNextSidebar/Logic/SidebarTunables.swift:slideHoldsContent: "9176f914289c0b6cc2ff950c78845d7dbe4703fbf6b97136ba0b8073ad77f9c8" -> "6aa04903e685dd8d497b9c44c4b7cf23c5f69345148dc3e32acedb25d4730c6d"

Needs a review. It passes with an independent review receipt on this commit: a comment or review whose first line is Review (<reviewer>): SHIP at c14846c9c2a3745a40a0c7fe5f247f73edbf345f, from a member, not a self-review (the newest verdict naming the head decides; a later BLOCK beats an earlier SHIP), or with an approving review on this commit from @lawrencecchen or @austinywang (the call owners in .github/defaults-call-owners.json, never the author). A receipt posted as a review re-runs this check; after a plain comment, re-run it. The needs a call label alone does not pass, and a new push needs a new receipt.

Merge receipt: merged as 8e6652f5837c with these defaults changed, WITHOUT a SHIP receipt or an approving call owner review on its head.

Regenerate the snapshot with python3 scripts/ci/defaults_guard.py snapshot.

Shipped in:

@teamleaderleo teamleaderleo changed the title fix(next): sidebar toggle changes the content's frame once and leaves it still fix(next): the sidebar's content hold changes the content's frame once; per-frame trace of both modes Nov 1, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5 medium): SHIP at c9f9327

The geometry holds for a left and a right sidebar, for show, hide and a reversal. The held frame equals bounds at the landing, so clearing the hold changes nothing. Nothing conflicts with #29894's settlement flush: it runs while the hold is set at a frame equal to bounds, and in follow mode the hold is nil. The test commit 0a4c827a92f compiles on base, using only base APIs, and fails there for the right reasons. The follow-mode trace assertions can't fail a smooth slide.

Fleet: red 8ee6ecb4d5e2d4e91b046149 (fails as expected), green d5b6044761f6125b5e255ae5 (SidebarSlideHoldsContentTests 9 of 9, SidebarToggleContentTraceTests 4 of 4, SidebarToggleSettlementTests pass).

Follow-ups:

  • [follow-up] With the opt-in hold, a show opens a blank strip in the card between the sidebar and the narrower content, so "slides over its near edge" in the doc comment is true only for a hide.
  • [follow-up] Add a right-sidebar case to the trace test.
  • [follow-up] Reset tabBarPosition in the trace and settlement tests (bottom tab bars move the card's top inset with the sidebar).
  • [follow-up] Comments in OverlayPlane.roundedClip, CEFClipShape.cutsSide and aSquareMainPaneClipsOnlyWhileItHolds still say content is "held wider than" the card.

@blacksmith-sh

blacksmith-sh Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Found 12 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmpk0wzli63/summary-missing.json (live):/summary-missing.json (live): View Logs
/tmp/tmpuqitkoc8/summary-fail.json (live):/summary-fail.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
nightly must build and hand off the macOS 26 helper and Xcode 26.5 app before publishin
g/
nightly must build and hand off the macOS 26 helper and Xcode 26.5 app before publishin
g
View Logs
test_cmux_next_follows_the_committed_target_graph (main.FocusedTestMap.test_cmux_ne
xt_follows_the_committed_target_graph)/
test_cmux_next_follows_the_committed_target_graph (main.FocusedTestMap.test_cmux_ne
xt_follows_the_committed_target_graph)
View Logs
test_every_part_of_bun_run_check_runs_in_the_warn_branch (main.WebviewsWarnSplitCov
ersCheck.test_every_part_of_bun_run_check_runs_in_the_warn_branch)/
test_every_part_of_bun_run_check_runs_in_the_warn_branch (main.WebviewsWarnSplitCov
ersCheck.test_every_part_of_bun_run_check_runs_in_the_warn_branch)
View Logs
test_helper_never_takes_a_hosted_mac_and_skips_compile_on_hit (main.WorkflowTests.t
est_helper_never_takes_a_hosted_mac_and_skips_compile_on_hit)/
test_helper_never_takes_a_hosted_mac_and_skips_compile_on_hit (main.WorkflowTests.t
est_helper_never_takes_a_hosted_mac_and_skips_compile_on_hit)
View Logs

...and 2 more test failures. View all on Blacksmith

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

…, not only wider

Review follow-up on #30064: a held show is narrower than the card, so the
comments that said the hold keeps content wider than the card, and that the
sidebar slides over the content's near edge, now say what both legs do.
Comments only.

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

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5 medium): SHIP at 4a11eec

The new commit 4a11eeca3399 changes only comments: five // and /// lines, no code, declarations or strings. Its parent c9f93278319 already got SHIP, with fleet red 8ee6ecb4d5e2d4e91b046149 and green d5b6044761f6125b5e255ae5. The comments now say what the opt-in hold does on both legs: the content is held at its landing width, pinned to the card's far edge, so it's wider than the card on a hide and narrower on a show.

Follow-ups (carried from the review at c9f9327):

  • [follow-up] With the opt-in hold, a show opens a blank strip in the card between the sidebar and the narrower content.
  • [follow-up] Add a right-sidebar case to the trace test.
  • [follow-up] Reset tabBarPosition in the trace and settlement tests.

# Conflicts:
#	plans/cmux-next/sidebar-owner.md
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5 medium): SHIP at cb34395

This head merges feat-cmux-next into 4a11eeca3399, which already has a SHIP. The only conflict was plans/cmux-next/sidebar-owner.md. Both entries are kept in full: this PR's content-hold note and base's Archive-section note. The PR's own diff against base (10 files, +208/−51) is byte-identical to the one at 4a11eeca3399, and no base change is dropped. Fleet evidence carries over: red 8ee6ecb4d5e2d4e91b046149, green d5b6044761f6125b5e255ae5, both on the identical code.

Follow-ups: as listed in the review at c9f9327 (blank strip on an opt-in show, a right-sidebar trace case, reset tabBarPosition in the tests).

@cubic-dev-ai cubic-dev-ai 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.

1 issue found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarToggleContentTraceTests.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarToggleContentTraceTests.swift:101">
P2: The hold assertion only counts content-frame changes, so a single snap at landing would pass. Also assert that the change happens no later than the first sliding sample.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

+ "before=\(Self.r(before.minX))+\(Self.r(before.width)) landed=\(Self.r(after.minX))+\(Self.r(after.width)) "
+ "frames(sidebar:contentMinX+contentWidth)=\(trace.joined(separator: ","))")
if holds {
#expect(frames.count - 1 == 1,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The hold assertion only counts content-frame changes, so a single snap at landing would pass. Also assert that the change happens no later than the first sliding sample.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarToggleContentTraceTests.swift, line 101:

<comment>The hold assertion only counts content-frame changes, so a single snap at landing would pass. Also assert that the change happens no later than the first sliding sample.</comment>

<file context>
@@ -0,0 +1,127 @@
+              + "before=\(Self.r(before.minX))+\(Self.r(before.width)) landed=\(Self.r(after.minX))+\(Self.r(after.width)) "
+              + "frames(sidebar:contentMinX+contentWidth)=\(trace.joined(separator: ","))")
+        if holds {
+            #expect(frames.count - 1 == 1,
+                    "the content's on-screen frame changed \(frames.count - 1) times in one toggle (one is to where it lands): \(frames)")
+        } else {
</file context>

# Conflicts:
#	plans/cmux-next/sidebar-owner.md
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5 medium): SHIP at 533711b

This is a merge of feat-cmux-next into the branch. The only conflict was plans/cmux-next/sidebar-owner.md, and both entries are kept. The other 9 files match the reviewed head cb34395 by patch-id, and no base change was dropped.

…r-toggle-no-bounce

# Conflicts:
#	plans/cmux-next/sidebar-owner.md
@teamleaderleo
teamleaderleo merged commit 8e6652f into feat-cmux-next Nov 1, 2026
71 of 74 checks passed
@teamleaderleo
teamleaderleo deleted the lane/sidebar-toggle-no-bounce branch November 1, 2026 07:26

This branch was previously deployed

1 inactive deployment
ci-routing-agent — c14846c9 Deployed Nov 1, 2026 by teamleaderleo via cmux-tui tree preflight #94591
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