fix: deep split chains no longer freeze the main thread

Merged
TE
5d ago
+105-1

Summary

A workspace with a deep split chain (an agent team's column of teammates) froze the main thread when it mounted, and every time you switched to it (#10311, #16393).

A main-thread sample of the hang shows this sequence. Each time a pane mounts, SwiftUI's AppKitPlatformViewHost.viewDidMoveToSuperview rebuilds the key view loop of its subtree (-[NSView _setDefaultKeyViewLoop]). For each hosting view, SwiftUI answers acceptsFirstResponder by walking its focusable items, and that walk asks every nested Bonsplit pane host the same question, so the same nested walks repeat at every level of the chain.

  • Bonsplit (manaflow-ai/bonsplit#283, merged): the pane hosting view keeps its acceptsFirstResponder answer until the end of the current run-loop turn, so each host is walked once per turn. The submodule moves from 2e21c47 to bd94ed1, which is that one commit. It doesn't bring in bonsplit#282; #19593 bumps that separately.
    An AppDelegate change from #10315 by @Apptah was tried first: it kept AppKit's automatic key-view-loop recalculation off in every main window. It had no effect. The restore unit test showed the flag is back on by the time the window is created, and the deep-chain test stayed red with only that change. It's reverted, so AppDelegate and #11006's restore-only suppression are unchanged. @Apptah's diagnosis of the key-view-loop focus walk is what led here.

Testing

tests_v2/test_deep_split_chain_stays_responsive.py opens a workspace holding a 16-deep chain of downward splits, shows it, switches away and back, and checks that the socket keeps answering. The test builds the chain from a layout because runtime splits stop at the minimum pane size before the chain gets deep. All runs were on a whole capture mini.

BuildResultDetails
Main build 30d1f5aaRed, RESULT 2/5Once the deep workspace was shown, the main actor stopped answering. pane.list and switching away timed out at 10 s, and switching back took 8.93 s.
AppDelegate change only (e3193693)Red, RESULT 2/5Switching back took 5.71 s.
Bonsplit fix plus the ineffective AppDelegate change (d089d71f)Green, RESULT 5/5Opening, showing, switching away and switching back each answered in 0.08 s or less.

This head only reverts the AppDelegate change. The unit test shows that change never took effect, so I didn't rebuild for the e2e test; PR CI covers this head.

Also run: scripts/verify-local.py. Its only failure is swift-syntax on scripts/generate-computer-use-helper-icon.swift, which this PR doesn't touch. The Swift file length budget passes. No review subagent was run.

Changelog

  • Fixed: Workspaces with many nested splits no longer freeze cmux when shown or switched to.

🤖 Generated with Claude Code