Skip to content

Fix hidden right-sidebar FileExplorer mount loop on launch - #4937

Merged
austinywang merged 7 commits into
mainfrom
issue-4931-tahoe-fileexplorer-launch-hang
Jun 12, 2026
Merged

austinywang merged 7 commits into
mainfrom
issue-4931-tahoe-fileexplorer-launch-hang

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Preserve the restored right-sidebar mode, but do not mount right-sidebar mode content while the sidebar is hidden.
  • This keeps hidden FileExplorer/Find content out of the launch tree while the shell still stays mounted for width animation.
  • Adds a behavior-level unit test for the hidden-find launch state and an E2E launch check for the persisted hidden Find mode.

Issue triage

Proof

  • Red commit: cbbb37e, cmuxTests/WindowDragHandleHitTests/testHiddenRightSidebarDoesNotMountFileExplorerPanelContent failed on AWS with XCTAssertNil failed because FileExplorerContainerView was mounted while hidden.
  • Green: same focused AWS test exits 0 after the fix. Initial rerun hit runner disk exhaustion, then passed after deleting stale cmux DerivedData/result bundles on that runner.
  • macOS 26 E2E: https://github.com/manaflow-ai/cmux/actions/runs/26564291975 passed for BonsplitTabDragUITests/testLaunchCompletesWithHiddenRightSidebarRestoringFindMode.

Notes


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


Note

Medium Risk
Changes SwiftUI mounting for the main right sidebar on launch and toggle paths; behavior is policy-gated and covered by unit/UI tests, but affects File Explorer/Find lifecycle.

Overview
Fixes a launch-time issue where persisted Find/Files mode still mounted heavy right-sidebar panels (FileExplorerPanelView, etc.) while the sidebar was hidden, which could trigger a SwiftUI update loop.

RightSidebarContentMountPolicy gates contentForMode: on a cold hidden launch it renders Color.clear instead of mode panels; once the sidebar has been shown, content stays mounted when hidden again so width animation and mode state stay smooth. The outer sidebar shell in ContentView remains in the tree unchanged.

Adds HiddenRightSidebarContentMountingTests (policy + no FileExplorerContainerView when hidden) and a Bonsplit UI test that launch succeeds with hidden sidebar restoring Find mode (search field absent).

Reviewed by Cursor Bugbot for commit 92ee8c6. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Fixes #4931 by stopping right-sidebar panel content from mounting while the sidebar is hidden to prevent a launch-time SwiftUI update loop. The restored mode and shell remain, and after the first show the content stays mounted when hidden to avoid churn.

  • Bug Fixes
    • Gate Files/Find/Sessions/Feed/Dock mounts behind RightSidebarContentMountPolicy (requires fileExplorerState.isVisible or a prior mount); render Color.clear otherwise while keeping the shell for width animation.
    • Tests: added cmuxTests/HiddenRightSidebarContentMountingTests.swift (policy + hidden-mount regression) and a UI test that launch completes with a hidden sidebar restoring Find mode.

Written for commit 92ee8c6. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

Bug Fixes

  • Right sidebar content is now efficiently managed and only loaded when the sidebar is visible, improving performance when hidden.

Tests

  • Added regression and UI tests to ensure the right sidebar behaves correctly across different visibility states and configurations.

Review Change Stack

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@vercel

vercel Bot commented May 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 12, 2026 7:03am
cmux-staging Building Building Preview, Comment Jun 12, 2026 7:03am

@coderabbitai

coderabbitai Bot commented May 28, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces RightSidebarContentMountPolicy to conditionally mount right-sidebar panel content only when the sidebar is visible. The policy is integrated into RightSidebarPanelView.contentForMode to prevent heavy content rendering when the sidebar is hidden, with unit and UI tests validating the behavior.

Changes

Right-Sidebar Content Mount Policy

Layer / File(s) Summary
RightSidebarContentMountPolicy and content rendering refactor
Sources/RightSidebarPanelView.swift, Sources/ContentView.swift
RightSidebarContentMountPolicy.mountedMode returns the sidebar mode only when visible, otherwise nil. contentForMode is refactored to use this policy, rendering Color.clear when content should not mount. Documentation comment updated to explain the shell-kept-mounted, content-conditionally-mounted strategy.
Unit test for conditional mounting
cmuxTests/WindowAndDragTests.swift
Test instantiates RightSidebarPanelView with hidden sidebar state and verifies via view-hierarchy inspection that FileExplorerContainerView is not mounted, while sidebar mode/visibility state is preserved in UserDefaults.
UI test with helper parameter support
cmuxUITests/BonsplitTabDragUITests.swift
launchConfiguredApp helper gains optional rightSidebarMode parameter, which is wired to set launch arguments -rightSidebar.mode and file-explorer visibility flags. New UI test launches with sidebar mode "find" while hidden, verifies launch completes, and confirms the search field is not exposed.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

  • Related to dev-artifacts issue reporting the UI test behavior for hidden sidebar mode restoration
  • Related to dev-artifacts issue for the RightSidebarContentMountPolicy implementation validating conditional mounting behavior
  • Related to dev-artifacts issue tracking the new UI test for sidebar launch state preservation

Possibly related PRs

  • manaflow-ai/cmux#4785: Modifies right-sidebar content selection logic in RightSidebarPanelView.swift (removes .history mode); this PR changes when sidebar mode content is mounted based on visibility.

Poem

🐰 A shell stays mounted, content only when shown,
No files to explore when the sidebar's alone,
Mount policies dance with visibility's grace,
Heavy views rest in their rightful place! ✨


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Swift File And Package Boundaries ❌ Error New RightSidebarPanelView.swift (639 lines) violates boundaries: exceeds 400-line threshold, mixes UI/state/platform code without clear single responsibility or extraction path. Extract RightSidebarContentMountPolicy and RightSidebarMode into SwiftPM package, or split RightSidebarPanelView and add to budget file.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (16 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PR introduces no actor isolation issues. Only new type is RightSidebarContentMountPolicy, a nonisolated enum with pure static utility; existing types properly marked nonisolated/Sendable.
Cmux Swift Blocking Runtime ✅ Passed Production changes introduce no blocking synchronization: RightSidebarContentMountPolicy is deterministic; ContentView only comment update; no semaphores, sleeps, locks, or polling in focused changes.
Cmux No Hacky Sleeps ✅ Passed PR only modifies Swift files. Rule scope is TypeScript, JavaScript, shell, and non-Swift build/runtime scripts (excludes Swift). Check not applicable.
Cmux Algorithmic Complexity ✅ Passed New policy method is O(1). Refactored contentForMode has constant-time operations on fixed 5-case enum. No nested loops, rescans, or unbounded collections.
Cmux Swift Concurrency ✅ Passed PR introduces no legacy async patterns: new RightSidebarContentMountPolicy enum is pure synchronous; contentForMode refactor uses only SwiftUI ViewBuilder; tests are test-only code.
Cmux Swift @Concurrent ✅ Passed All Swift changes comply with @concurrent rules: RightSidebarContentMountPolicy properly annotated as nonisolated; no invalid @concurrent usage found.
Cmux Swift Logging ✅ Passed PR contains no logging violations. All debug logging uses #if DEBUG guards and dlog/NSLog is not used in unguarded production code paths.
Cmux User-Facing Error Privacy ✅ Passed PR changes are limited to implementation code and tests; user-facing strings use proper localization with safe generic terms; no sensitive data, raw error messages, or upstream vendor details exposed.
Cmux Full Internationalization ✅ Passed PR contains no new user-facing strings; all existing localized strings use String(localized:) with complete translations already in place.
Cmux Swiftui State Layout ✅ Passed Only a nonisolated enum helper added with no state patterns, and conditional rendering in existing @ObservedObject views without layout violations.
Cmux Architecture Rethink ✅ Passed Pure policy function gates sidebar content mount based on visibility. No timing, dispatch, locks, observers, or side channels. Clear ownership and invariant with unit+E2E tests.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds no new user-visible windows; RightSidebarContentMountPolicy is a utility enum, not a window; test fixtures are explicitly allowed.
Title check ✅ Passed The title accurately summarizes the main change: fixing a mount loop issue with hidden right-sidebar FileExplorer content on launch.
Description check ✅ Passed The PR description is comprehensive and follows the template structure with all required sections completed.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-4931-tahoe-fileexplorer-launch-hang

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented May 28, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Gates right-sidebar panel content (Files, Find, Sessions, Feed, Dock) behind a new RightSidebarContentMountPolicy so that cold hidden launches no longer mount heavy SwiftUI subtrees, avoiding the update loop seen in #4931. The sidebar shell (mode bar + frame) stays in the tree for width animation; only the panel content is suppressed until first show.

  • RightSidebarContentMountPolicy.shouldMountContent(isRightSidebarVisible:hasMountedContent:) returns true once the sidebar has ever been visible, keeping ephemeral panel state alive across hide/show cycles after first mount while blocking the cold-hidden path.
  • hasMountedRightSidebarContent: @State is set to true in onAppear (when already visible) and in onChange(of: fileExplorerState.isVisible) (on first open); it resets naturally when the view is recreated.
  • Regression coverage: a Swift Testing suite asserts (false, false) → false and (false, true) → true; a new BonsplitTabDragUITests path verifies the app stays running and doesn't expose the search field with a persisted hidden Find mode.

Confidence Score: 5/5

Safe to merge; the change narrows the mount boundary to a single, well-tested guard and leaves all existing visible-sidebar paths unchanged.

The fix is narrowly scoped to the cold-hidden launch path. hasMountedRightSidebarContent is transient @State that initialises to false on every fresh view instantiation, so there is no risk of stale persistence across sessions. The onAppear and onChange handlers are both on MainActor and set the flag consistently. RightSidebarContentMountPolicy is a trivially-correct pure function covered by unit tests. No actor isolation issues, no blocking primitives, no user-facing strings, and no production logging changes were introduced.

No files require special attention.

Important Files Changed

Filename Overview
Sources/RightSidebarPanelView.swift Adds RightSidebarContentMountPolicy nonisolated enum and hasMountedRightSidebarContent @State flag; gates contentForMode behind the policy check; both onAppear and onChange(of: isVisible) set the flag on first show. Logic is correct and contained within the view boundary.
Sources/ContentView.swift Comment-only update to terminalContentWithRightSidebarPanel clarifying the new deferred-mount behavior; no logic changes.
cmuxTests/HiddenRightSidebarContentMountingTests.swift New Swift Testing suite covering (false,false)→false and (false,true)→true policy cases, plus a hosting-view integration test asserting FileExplorerContainerView is absent when the sidebar is hidden on cold launch. Correctly marked @mainactor .serialized; UserDefaults save/restore is solid.
cmuxUITests/BonsplitTabDragUITests.swift Adds testLaunchCompletesWithHiddenRightSidebarRestoringFindMode E2E test and extends launchConfiguredApp with rightSidebarMode/showRightSidebar launch-argument injection. The new parameters are additive and backward-compatible.
cmux.xcodeproj/project.pbxproj Registers HiddenRightSidebarContentMountingTests.swift in the test target with a fresh UUID-based file/build ref. Mechanical xcodeproj update; no structural issues.
.github/swift-file-length-budget.tsv Budget updated for BonsplitTabDragUITests (1058→1093) and RightSidebarPanelView (685→699); both remain within the 800-line production cap.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[App Launch] --> B{fileExplorerState.isVisible?}
    B -- false --> C[Cold hidden path\nhasMountedRightSidebarContent = false]
    B -- true --> D[Visible path\nhasMountedRightSidebarContent = true]

    C --> E[contentForMode:\nRightSidebarContentMountPolicy.shouldMountContent]
    D --> E

    E --> F{isVisible OR\nhasMountedContent?}
    F -- false --> G[Color.clear\nNo FileExplorer/Find/Sessions/Feed/Dock mounted]
    F -- true --> H[Switch on mode\nMount FileExplorerPanelView / SessionIndexView etc.]

    I[User opens sidebar] --> J[onChange isVisible = true]
    J --> K[hasMountedRightSidebarContent = true]
    K --> E

    H --> L[User hides sidebar]
    L --> M[isVisible = false\nhasMountedContent = true]
    M --> E
    E --> F
    F -- true --> H
Loading

Reviews (6): Last reviewed commit: "fix: preserve mounted sidebar content af..." | Re-trigger Greptile

Comment thread Sources/RightSidebarPanelView.swift
Comment thread Sources/RightSidebarPanelView.swift
kevinsslin added a commit to kevinsslin/cmux that referenced this pull request Jun 10, 2026
…top macOS 26 hang

The right-sidebar FileExplorer pinned the main thread at 100% CPU on macOS 26
(Tahoe), beachballing the app until force-quit. A captured .hang shows every
main-thread sample in one SwiftUI transaction loop:

  FileExplorerPanelView.updateNSView
    -> FileExplorerContainerView.updateSearchLayout
       -> -[NSView _setHidden:] -> KVO didChangeValueForKey: -> re-enter graph

updateNSView mutated AppKit state on every SwiftUI pass: updateSearchLayout,
updateVisibility, and the header reassigned NSView.isHidden, a constraint
constant, an NSImage, and label strings unconditionally. Assigning isHidden to
its current value still emits KVO and setNeedsDisplay, which on macOS 26
re-enters the graph through updateNSView and never settles.

Guard every write so the pass mutates (and requests layout) only when a value
actually changes. Reproduces via Cmd+Shift+F (Find in Directory) with the
sidebar visible, and on launch with a persisted Find sidebar.

Complements manaflow-ai#4937, which stops the hidden panel mounting at launch, by removing
the loop at its source.
austinywang pushed a commit to kevinsslin/cmux that referenced this pull request Jun 11, 2026
…top macOS 26 hang

The right-sidebar FileExplorer pinned the main thread at 100% CPU on macOS 26
(Tahoe), beachballing the app until force-quit. A captured .hang shows every
main-thread sample in one SwiftUI transaction loop:

  FileExplorerPanelView.updateNSView
    -> FileExplorerContainerView.updateSearchLayout
       -> -[NSView _setHidden:] -> KVO didChangeValueForKey: -> re-enter graph

updateNSView mutated AppKit state on every SwiftUI pass: updateSearchLayout,
updateVisibility, and the header reassigned NSView.isHidden, a constraint
constant, an NSImage, and label strings unconditionally. Assigning isHidden to
its current value still emits KVO and setNeedsDisplay, which on macOS 26
re-enters the graph through updateNSView and never settles.

Guard every write so the pass mutates (and requests layout) only when a value
actually changes. Reproduces via Cmd+Shift+F (Find in Directory) with the
sidebar visible, and on launch with a persisted Find sidebar.

Complements manaflow-ai#4937, which stops the hidden panel mounting at launch, by removing
the loop at its source.
@austinywang

Copy link
Copy Markdown
Contributor

Merged current origin/main into this branch and resolved the only content conflict in Sources/RightSidebarPanelView.swift. The resolution keeps this PR's hidden-sidebar mount gate (hidden sidebar renders Color.clear instead of mounting the selected panel), while preserving main's newer FileExplorerRootSyncPolicy, RightSidebarDirectoryContext, and Dock root-directory behavior by using dockRootDirectory for Dock mode.

@austinywang
austinywang merged commit 249a16d into main Jun 12, 2026
21 checks passed
austinywang added a commit that referenced this pull request Jun 12, 2026
) (#5786)

* Add failing test: FileExplorer update passes must stay idempotent

Drive the container to a steady Find state and assert that redundant
updateVisibility and updatePresentation(.find) passes leave needsLayout
unchanged, with a positive control that a genuine change still invalidates.
Fails today because updateSearchLayout sets needsLayout on every call.

* Fix #4931: make FileExplorer panel updates idempotent to stop macOS 26 hang

The right-sidebar FileExplorer pinned the main thread at 100% CPU on macOS 26
(Tahoe), beachballing the app until force-quit. A captured .hang shows every
main-thread sample in one SwiftUI transaction loop:

  FileExplorerPanelView.updateNSView
    -> FileExplorerContainerView.updateSearchLayout
       -> -[NSView _setHidden:] -> KVO didChangeValueForKey: -> re-enter graph

updateNSView mutated AppKit state on every SwiftUI pass: updateSearchLayout,
updateVisibility, and the header reassigned NSView.isHidden, a constraint
constant, an NSImage, and label strings unconditionally. Assigning isHidden to
its current value still emits KVO and setNeedsDisplay, which on macOS 26
re-enters the graph through updateNSView and never settles.

Guard every write so the pass mutates (and requests layout) only when a value
actually changes. Reproduces via Cmd+Shift+F (Find in Directory) with the
sidebar visible, and on launch with a persisted Find sidebar.

Complements #4937, which stops the hidden panel mounting at launch, by removing
the loop at its source.

* Bump Swift file length budget for FileExplorer idempotency fix

The #4931 fix adds idempotency guards to FileExplorerView.swift (+26
lines) and a regression test to FileExplorerStoreTests.swift (+50
lines), pushing both past their tracked budgets. Refresh the two
entries to accept this debt.

* Fix FileExplorer tests to use Swift Testing

* Fix FileExplorer Swift Testing discovery

---------

Co-authored-by: austinpower1258 <austinwang115@gmail.com>
hhsw2015 pushed a commit to hhsw2015/cmux that referenced this pull request Jun 12, 2026
…naflow-ai#4931) (manaflow-ai#5786)

* Add failing test: FileExplorer update passes must stay idempotent

Drive the container to a steady Find state and assert that redundant
updateVisibility and updatePresentation(.find) passes leave needsLayout
unchanged, with a positive control that a genuine change still invalidates.
Fails today because updateSearchLayout sets needsLayout on every call.

* Fix manaflow-ai#4931: make FileExplorer panel updates idempotent to stop macOS 26 hang

The right-sidebar FileExplorer pinned the main thread at 100% CPU on macOS 26
(Tahoe), beachballing the app until force-quit. A captured .hang shows every
main-thread sample in one SwiftUI transaction loop:

  FileExplorerPanelView.updateNSView
    -> FileExplorerContainerView.updateSearchLayout
       -> -[NSView _setHidden:] -> KVO didChangeValueForKey: -> re-enter graph

updateNSView mutated AppKit state on every SwiftUI pass: updateSearchLayout,
updateVisibility, and the header reassigned NSView.isHidden, a constraint
constant, an NSImage, and label strings unconditionally. Assigning isHidden to
its current value still emits KVO and setNeedsDisplay, which on macOS 26
re-enters the graph through updateNSView and never settles.

Guard every write so the pass mutates (and requests layout) only when a value
actually changes. Reproduces via Cmd+Shift+F (Find in Directory) with the
sidebar visible, and on launch with a persisted Find sidebar.

Complements manaflow-ai#4937, which stops the hidden panel mounting at launch, by removing
the loop at its source.

* Bump Swift file length budget for FileExplorer idempotency fix

The manaflow-ai#4931 fix adds idempotency guards to FileExplorerView.swift (+26
lines) and a regression test to FileExplorerStoreTests.swift (+50
lines), pushing both past their tracked budgets. Refresh the two
entries to accept this debt.

* Fix FileExplorer tests to use Swift Testing

* Fix FileExplorer Swift Testing discovery

---------

Co-authored-by: austinpower1258 <austinwang115@gmail.com>
hhsw2015 pushed a commit to hhsw2015/cmux that referenced this pull request Jun 12, 2026
…ai#4937)

* test: cover hidden find sidebar launch

* fix: skip hidden right-sidebar content mounts

* fix: update right sidebar merge follow-ups

* fix: migrate right sidebar regression to Swift Testing

* fix: move hidden sidebar regression test

* fix: preserve mounted sidebar content after open

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
Co-authored-by: austinpower1258 <austinwang115@gmail.com>

This branch was successfully deployed

1 active deployment
Preview – cmux — 92ee8c6b Deployed Jun 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

App hangs at 100% CPU on launch — FileExplorer SwiftUI update loop on macOS 26 (Tahoe)

2 participants