Repository navigation
debug.capture_backdrop: a patterned window behind a window for capture snapshots - #27514
Conversation
…e snapshots A capture slot shares its mini's desktop with other slots, and debug.window_snapshot composites only the app's own windows, so a translucent window or its glass showed nothing behind it in a capture (the uniform dark backdrop): lifted-row glass translucency could not be verified. debug.capture_backdrop (DEBUG builds) puts the app's own patterned window (checker, stripes, or an image file read off main) just behind a window, covering its screen, and the window's snapshot composites it behind everything else, so the window server blurs it under the glass as on screen. `pattern: none` closes it; it goes with the app and is never the desktop picture. Tests: CmuxNextDesignTests.CaptureBackdropPatternTests (names, pattern cells, composite order) and the display suite CaptureBackdropSnapshotTests (a translucent window's snapshot shows the stripes behind it, and does not without the backdrop). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-a7306ef145c005ad24ab
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
red: cmux-next checks (god files, concurrency, crash safety, l10n); NEW: |
|
Owned by an agent session · session |
|
Bead |
|
Found 9 test failures on Blacksmith runners: Failures
|
|
Review (codex gpt-6-astra high, next-review): BLOCK at af2187e
|
…solated pattern A backdrop covers its window's screen and sits just behind that window, so window A's backdrop is above window B, and floating-overlay discovery (an app window above B over its frame) took it as an overlay of B: it covered B in B's snapshot (review of af2187e). Every capture backdrop is now skipped there; a snapshot composites only its own window's backdrop, behind it. CaptureBackdropPattern is nonisolated, so the async control route can parse it off the main actor (the app target did not compile: "loses global actor 'MainActor'"). Test: CaptureBackdropSnapshotTests.aSnapshotShowsOnlyItsOwnWindowsBackdrop (two windows, each with its own backdrop; each snapshot shows only its own). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-a7306ef145c005ad24ab
b4f7193 to
c893b33
Compare
The display tests captured in the same turn the windows were ordered in, before the window server composited them, so every snapshot was nil (fleet step e4e40db9). They now flush and capture until the window (and its backdrop) shows, bounded at 5 s, as WindowScreenshotTests does, and skip on a host that cannot show windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-a7306ef145c005ad24ab
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
…erride The panel kept itself alive until an overridden close(), and the crash-class selector rule wants every new super call probed on a Mac first. The window it is behind now keeps it (an associated object), so it goes with that window, and setCaptureBackdrop orders the old one out; no super call is added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-a7306ef145c005ad24ab
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Review (codex gpt-6-astra high, router-review): BLOCK at 71131c8 BLOCK
|
… closes Replacing or removing a capture backdrop only ordered the old panel out and dropped it, so repeated pattern changes left window-server windows behind, and closing the target window left its screen-sized backdrop on screen. The panel now closes when it is replaced or taken away, and observes its target's willClose (weakly, removed on teardown) to remove and close itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review (codex-review-box/gpt-6-astra high, lane-place): BLOCK at 71131c8 BLOCK
|
|
Review (codex gpt-6-astra high, router-review): SHIP at f31f3aa SHIP No correctness, regression, crash, race, security, or test-blocking defects found. (router-review gave this verdict for this head; its own --post hit the shared API quota, so the receipt is posted by the PR owner after checking the head has not moved.) |
|
Shipped in:
|
|
Shipped in:
|
… capture backdrop is DEBUG/bench only (#28263) * fix(design): the capture backdrop panel is DEBUG and bench only, as its only callers are An independent exception review (reward-hacking audit, arch-ratchet tip rises) found that CaptureBackdropPattern.swift (8fb4805, #27514) ships its CaptureBackdropPanel, its willClose observer and NSWindow.setCaptureBackdrop in Release, although its only callers (DebugWindowSnapshot.captureBackdrop, the debug.capture_backdrop route in AppControl+Input) are `#if DEBUG || CMUX_BENCH_HOOKS`, and its exception said "DEBUG-only". The panel, its view and the two NSWindow members that make or read it now sit in the same `#if`; Release keeps the two read helpers that NSWindow+Snapshot uses, as constants (no backdrop: none to skip, nothing behind). The pattern enum stays (a value type, no observer). Release behavior: unchanged, since nothing could make a backdrop there. * arch-ratchet.json: 12 reviewed exceptions for the tip rises an independent reviewer judged needed; 9 unreviewed pending entries removed Reward-hacking audit (handoff/followups.md "arch-ratchet tip rises (no exceptions)"). An independent exception reviewer (it wrote none of the code and none of the old reasons) judged 14 candidates at 2391c5e: 12 EXCEPTION-OK, 2 FIX (AppDelegate.swift:514's quit flush, on fix/arch-rise-quit-flush; the capture backdrop, gated in 2391c5e and then accepted). The reviewer wrote each entry's receipt itself; each has author, owner and expiry 2026-11-30, one site per entry (allow 1): global_mutable_statics PaletteScope lock (#28036), CrashWatcher descriptor (#22844); stateful_singletons KillSwitches.shared (#24212), BlockingViolationTable.shared (f668c89); observer_sites DesktopWallpaperWatch :144 :147 :148 :149 (#25308), IPhoneHostService wake (#27297), DebugWindowRecord willClose (#23680), CaptureBackdropPattern willClose (#27514); blocking_allow_escapes SingleInstanceGuard (#25918), plus its escape_reviews entry (read by the rule of #28195). Removed: the 9 PENDING entries with no review that these replace or that cover no site any more (CrashWatcher, BlockingViolationTable, DesktopWallpaperWatch +7, SingleInstanceGuard, HomeReduceMotion (a FIX), kill switches +2, PaletteScope +4 (statics gone) and +1, CaptureBackdropPanel). No applied entry, total or measured_sha changes; no ratchet number is raised. --update still refuses until the fix PRs land; it is run after them.
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
A capture of a translucent window could not show its translucency:
debug.window_snapshotcomposites only the app's own windows, and a capture slot shares its Mac's desktop with other slots, so glass and window opacity showed nothing behind them (a uniform dark backdrop).debug.capture_backdrop(DEBUG and bench-hook builds only) shows a patterned window of the app itself just behind one of its windows, covering that window's screen:pattern:checker,stripes,image:/absolute/path(read off the main thread), ornone(closes it).window/kind: as fordebug.window_snapshot(default the key window, else the active main window).pattern,window_number,backdrop_number.The window it is behind keeps it (an associated object), and floating-overlay discovery skips every capture backdrop, so a snapshot of one window never takes another window's screen-sized backdrop as an overlay over it.
Lifecycle: replacing a backdrop, taking it away (
none) or closing its window closes the panel (CaptureBackdropPanel.remove()), so no window-server window outlives it and a screen-sized backdrop never stays on screen after its window is gone.New observer: each backdrop observes
NSWindow.willCloseNotificationfor its one target window (MainActorObservation,object: target; at most one backdrop per window). It holds the panel weakly, so the window keeping the panel makes no cycle. Stop condition and cleanup: it is dropped inremove()(the backdrop is replaced, set tonone, or its window closes) and its token is removed when the panel is released. The arch-ratchetobserver_sites+1 carries that reason inplans/cmux-next/arch-ratchet.json.compositedSnapshotWithOverlays()now composites a window's capture backdrop behind everything else (always last in the window list, whatever the on-screen order), so the window server blurs it under glass as on screen. With no backdrop the snapshot is unchanged. The backdrop is an app window, never the desktop picture, so it goes with the app; nothing outside the app changes.The capture tooling side (a state's
"backdrop": "checker" | "stripes" | "image:<repo path>") is in the private capture tooling repo.Testing
CmuxNextDesignTests.CaptureBackdropPatternTests: pattern names, checker and stripe cells, composite order (backdrop always last, never in front).CmuxNextDesignTests.CaptureBackdropSnapshotTests(display suite, listed inscripts/ci/display-suites.txt): a translucent window's snapshot is uniform without a backdrop and shows the stripes with one;setCaptureBackdrop(nil)takes it away;aSnapshotShowsOnlyItsOwnWindowsBackdrop: two windows, each with its own backdrop, and each snapshot shows only its own.replacingOrRemovingABackdropClosesTheOldPanel: replacing a backdrop closes the old panel (itswillClosefires once),nonecloses the current one, and the window lets go of it.closingTheTargetWindowRemovesAndClosesItsBackdrop: closing the target window removes and closes its backdrop.WindowSnapshotOverlaysTestsrerun for the snapshot change.Observed / Not run
scripts/cmux-next/prepush-gate.pyclean;scripts/cmux-next/check-objc-selectors.py0 failures. Fleetpackage-test-lane.sh suiteon a gui-session-v1 mini at 0b98b4e: CaptureBackdropPatternTests, CaptureBackdropSnapshotTests and WindowSnapshotOverlaysTests all passed. Red: the same suite with the overlay skip removed failedaSnapshotShowsOnlyItsOwnWindowsBackdrop(B's snapshot took A's backdrop as an overlay:floating == 1, an orange stripe pixel). The head after that only replaces the panel's self-retain andclose()override with the target window keeping it.No capture: a DEBUG-only control method and a snapshot path that is unchanged unless a backdrop is set; nothing visible changes in the app. The display tests above check the composite pixels.
Changelog
none
risk:low (DEBUG-only control method; the snapshot path changes only when a backdrop is set)
🤖 Generated with Claude Code
Agent session: agent session