cmux NEXT captures: a window snapshot shows an open popover with its glass, and waits behind a busy main actor
Summary
The ask (from the capture lane, reported by chief-ui and projects-50): "(a) the capture tool's window snapshot leaves out child panels and popovers (NSPanel/child windows), so no popover PR can attach a capture" and "(b) the fleet window snapshot timed out and was refused." Done when "a capture of an open popover shows the popover."
Three causes, three fixes, all in the DEBUG capture path (debug.window_snapshot, which pr-capture's driver.py uses for every still):
- The popover never opened in a capture slot. Slots run the app in the background (
CMUX_NEXT_NO_ACTIVATE=1), so there is no key window.home.toggleChiefSettingsthen picked the front visible window, and AppKit lists a child window before its parent when the app is not active. The front window was Chromium's page window (aCefNSWindowchild of the main window), no Home answered, and nothing opened.HomeChiefSettingsPopover.toggleTargetnow walks a child window up to its parent, as it already did for a key child panel. - A glass popover came out as a flat grey card. The snapshot composited the window, then the overlay panels on their own, then menus. A glass or vibrancy panel composited alone has nothing behind it to blur. The window, its child windows and the app's windows over it (menus, cards, popovers that aren't its children) are now one window-server composite (
NSWindow.compositedSnapshotWithOverlays()), so the glass blurs the window under it, as on screen. Where a Chromium page image is then painted over the composite, the overlays are cut from that same composite by their own coverage (overlays(cutFrom:includeChild:)) and painted back on top, so a popover over a web page stays on top too. As before, only this app's windows are composited, never another app's. - The snapshot timed out on a loaded capture mini.
debug.window_snapshotran under the 2 s control-plane deadline, and that deadline counts the wait for the main actor. The capture itself takes about 50 ms, but on cua-5 (load 30 to 44 on 14 cores, shared with builds and test suites) and during app launch, the wait passed 2 s. The driver then had no still, and pr-capture refused the capture. The deadline exists to catch a handler that hangs, but a snapshot queued behind main-actor work isn't hung, and the driver needs the still either way. So the method keeps its own 30 s deadline (DebugWindowSnapshot.controlMethod), which matches the driver's 30 s socket timeout. The route moves out ofAppControlintoDebugWindowSnapshotso a test can drive it through a realControlRouter.
cmux window|pane|tab screenshot (AppWindowImage, WindowImageLayer) is left as on base. Two open PRs (#23972, #23983) rework that path, and this one doesn't touch it.
Testing
Red first (471c8b8), then the fix:
DebugWindowSnapshotDeadlineTests.aSnapshotWaitsBehindABusyMainActor: a router with a 50 ms request deadline, the main actor busy for 400 ms. On base the call fails with a timeout. On the head it answers (no such windowfor a made-up window number).HomeChiefSettingsToggleTests.withoutAKeyWindowAChildInFrontStandsForItsWindow: with no key window and a visible child listed first, the toggle reaches the parent. On base it returned the child.WindowSnapshotOverlaysTests.theWindowAndItsOverlaysAreOneCompositeTopmostFirst: the composite's window order (on-screen order first, then unlisted windows, each once).WindowSnapshotOverlaysTests.aPopoverCutFromTheCompositeShowsOverAPageImage: pixel check. Where the popover covers the window, the cut shows the popover over a page image; elsewhere the page shows.
cmux-ci compile-check --app passed at the head d04352ff7e7f (job 29e40325ba693a068a630a72).
Observed / Not run
- Observed: a fleet build of this branch on capture slot cua-5 (load 22 to 40 on 14 cores while it ran).
home.showthenhome.toggleChiefSettingsfrom the CLI, with the app in the background. The window list showed theNativePopupPanelchild, anddebug.window_snapshotreturned in 18 to 51 ms withchild_windows: 5. The still shows the Chief Settings popover open with its glass over Home, in light and dark. Before the toggle fix, the same steps opened nothing. Before the composite change, the popover came out as an opaque grey card. - Observed: the pr-capture run in Proof below (fleet build, capture slot, OCR privacy check).
- Not run: a popover over a Chromium page (the cut path) live; the pixel test covers that compositing.
Changelog
none
Proof
Capture steps-108f6bc705 at d04352ff7e7f (fleet build, capture mini, text checked for private data)
| Light | Dark |
|---|---|
![]() | ![]() |
Steps: cmux action run home.show, then cmux action run home.toggleChiefSettings, with the app in the background as every capture slot runs it. Capture job (fleet build) bdb92487ed96664bf8987012.
Checklist
- Behavior changes have added or updated tests
- Reviewed with an independent reviewer before merge, and all bot and human review comments resolved
Agent session: cosmic-gray
š¤ Generated with Claude Code

