fix: Cmd+Y crash and New Machine sheet layout

Merged
AU
10d ago
+212-32

Summary

Cmd+Y aborts nightly while the New Machine sheet animates open:
NSGenericException: The window has been marked as needing another Update Constraints in Window pass, but it has already had more Update Constraints in Window passes than there are views in the window (Sentry CMUXTERM-MACOS-3YDJ, 10 events today, all with the sheet at 440×284, all on nightlies 3731595686301 and 3733464696001).

Cause: the Cloud sheets set sizingOptions = [.preferredContentSize], so the window followed SwiftUI from inside AppKit's layout pass. The crash backtrace shows the cycle: layoutIfNeeded → _changeWindowFrameFromConstraintsIfNecessary → setFrameSize → NSHostingView.didChangeValue(forKey:) → invalidateSafeAreaInsets → setNeedsUpdateConstraints. Any control whose size depends on the width it is offered keeps that cycle going until AppKit throws. The same crash class has hit this sheet three times, and each earlier fix only guarded one control:

  • Sep 28 (6edb16da4aa): a labeled checkbox.
  • Today, #17392: the Base pop-up.
  • Today, #17417: this one set sizingOptions = [] but froze the window at its first size, so rows that appear after the sheet opens get clipped. Examples: the inherited-settings row after picking a Base machine, the loaded plan, and an expanded allowlist.

Fix: a new CloudSheetWindow (Sources/Cloud/CloudSheetWindow.swift):

  • The hosting controller has no sizing options, so AppKit never sizes the window from SwiftUI during layout.
  • The content reports its ideal size, which doesn't depend on the window's height.
  • The window takes that size with an explicit setFrame on a later main-queue turn, after the open animation and never inside a layout pass. The top edge stays anchored.

It is used by all three Cloud sheets that had the pattern: New Machine, Network, and Create Team.

Pop-up layout: today's fixes left the grid misaligned. The Base pop-up hugged the trailing edge (#17417), and the Size pop-up stretched across the whole column (#17392). Both now take their own width on the column's leading edge, like Network. They narrow, truncating the title, only when a long machine name or a locked size row would overflow the sheet. The Grid's first-baseline alignment still holds.

Testing

  • Red, then green: NewMachineSheetLayoutTests.presentedSheetTracksContentThatArrivesLater presents the real sheet through NewMachineSheetPresenter.present on a host window with beginSheet.
    • Data matches the nightly account: 8 base machines, a cold plan, and a nearly full pool.
    • Plan, pool, and machine-list refreshes land while the sheet is open, then a Base machine is picked.
    • The window must end at the content's ideal size.
    • Red commit c618c1f7549 (main with #17417 freezes the size); green commit 6beb6228f59. Hosted CI runs both; local xcodebuild test is not allowed.
  • Red, then green: NewMachineSheetLayoutTests.popUpsShareLeadingEdgeAtNaturalWidth requires the Base and Size pop-ups to share a leading edge, each no wider than its own size. Red commit 0d997c2efd6, green commit 8dc06cf58cf. The existing long-name test still guards overflow.
  • CloudSheetWindow.swift type-checks with swiftc -typecheck -swift-version 6.
  • scripts/check-pbxproj.sh and scripts/check-pbxproj-group-membership.py pass.
  • Not done yet:
    • The abort has not been reproduced on demand. A standalone harness of a simplified sheet did not abort, and reproducing with the real sheet is in progress.
    • No dogfood on a tagged build yet.

Changelog

  • Fixed: Cmd+Y (New Machine) no longer crashes cmux while the sheet opens, the Cloud sheets resize to fit content that appears after they open, and the New Machine pop-ups line up again.

Impact map

  • Source of truth: CloudSheetWindow.
  • Callers: NewMachineSheetPresenter.present (Cmd+Y, the Machines panel +, the command palette), CloudNetworkPolicySheetPresenter, CloudCreateTeamSheetPresenter.
  • NewMachineModel is unchanged.

🤖 Generated with Claude Code