chat-open click frames: ScreenCaptureKit when the step has no Screen Recording grant, so the chat page is in them
Summary
The chat-open click harness (scripts/measure/chat-open.sh, chat-open-bench.py click cases) times a sidebar click to the chat's final frame from debug.new_tab frame_strip pixels. On the fleet measure workers every frame after the click showed the chat pane empty, even for a kept page whose layout watch reported rows drawn, so the cold-click number timed the native pane switch, not chat text reaching the screen.
Root cause (measured on a measure worker with chat-open-bench.py --capture-check):
- The step's process, and the app the bench launches as its child, answer to the build worker as their responsible process. That binary is ad-hoc signed and holds no Screen Recording grant (
CGPreflightScreenCaptureAccess()is false), and SIP is on, so no agent path can grant one, and a person's grant would not survive the next worker update, since the binary's hash changes. - Without the grant,
CGWindowListCreateImage(frame_strip's capture) leaves out what Chromium's GPU process draws: the page window comes out as its background. (A WebKit page in a probe app on the same worker was captured fine; the redaction hits Chromium's out-of-process layers.) - ScreenCaptureKit with the current process's shareable content needs no grant and has the page. A same-step comparison showed the transcript through ScreenCaptureKit and an empty pane in the frame_strip frames.
Fix:
FrameStripRecording(CmuxNextDesign, DEBUG and bench builds only): withaboveand no Screen Recording grant, frame_strip reads ScreenCaptureKit's current-process content: the display showing only this app's windows, cut to the window's rect, every composited frame (an idle frame repeats the last picture). With the grant, withcapture: "window_list", or when ScreenCaptureKit fails, it uses the window list as before. The report says which:capture,capture_error.- The key waits for the first ScreenCaptureKit frame as well as the 120 ms lead, and the recording ends that much later, so
duration_msafter the key stays recorded. The reply'slead_mssays when the key went in. The bench uses it for the click time. - The
--capture-checkbench flag (screen_capture_check.py) reports whether the step may read the screen: the preflight, the responsible process and its signature, csrutil,screencapture, and the CuaDriver daemon's capture. DebugFrameStripshrinks by 2 lines. The new queuedebug.frame_strip.screenis listed inplans/cmux-next/threading-model.md.
Timers and cleanup: the lead, first-frame and end waits are one-shot DemandTimers, each cancelled or fired once. The stream stops at the end of the strip, and nothing outlives the request.
Testing
- App compile check at this head: passed.
prepush-gate.pyover the branch: no new violations.- Fleet proof: an exact-head
chat-open.sh --cases=click,click_againrun on a measure worker whose kept PNGs show transcript text (results on the owning issue).
Observed / Not run
- Observed: capture-check steps showed preflight false and the ad-hoc worker as the responsible process. A probe app's WKWebView page was captured by the window list and by ScreenCaptureKit. ScreenCaptureKit's picture of the real chat showed the transcript while the frame_strip frames were blank.
- Not run: a unit test. The capture needs a console session and a Chromium page. The fleet run is the regression check, and its JSON records
captureper open.
Changelog
none
Agent session: agent session
🤖 Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes the chat-open click benchmark on fleet measure workers, where every frame after the click showed the chat pane empty because the worker's process holds no Screen Recording grant and the window list's captures left out what Chromium's GPU process drew.
FrameStripRecordingreads ScreenCaptureKit's current-process content instead whenaboveis set and no grant exists (no grant needed, Chromium pages included), falling back to the window list with the grant or on failure; the report says which viacapture/capture_error.- The lead wait now also waits for ScreenCaptureKit's first frame, and the bench corrects the click time from the reply's
lead_ms. - New
--capture-checkflag (screen_capture_check.py) reports whether the step may read the screen: preflight, responsible process and signature, csrutil,screencapture, and the CuaDriver daemon's capture.