Repository navigation
feat(screenshot): select the XCTest runner capture with --capture-backend runner - #4184
Conversation
|
@vitalyiegorov is attempting to deploy a commit to the Callstack Team on Vercel. A member of the Team first needs to authorize it. |
|
The code at 4b8e1fd needs changes before merge. The screenshot admission policy accepts runner capture for macOS desktop, menubar, frontmost-app helper surfaces and native app sessions, but runAppleScreenshot returns the helper capture before reaching the runner branch. CLI, SDK and MCP can therefore report success using a different backend than requested. The owning admission and binding policy must account for both the selected surface and app backend: honor runner where supported and reject incompatible helper captures. Validate that an explicit runner request cannot succeed through the helper path. The changed public device route still lacks reproducible live evidence; the supplied simulator RMSE claim has no linked capture artifacts. Attach commands attributed to the reviewed head, device and OS details, exit status, artifact output and session cleanup. Run public screenshot commands with the default backend and --capture-backend runner on a Dynamic Island simulator, with viewable PNGs and a comparison showing the runner capture has no island. Run the same public runner request on an Android session and show UNSUPPORTED_OPERATION, then run simulator runner capture with --pixel-density and show INVALID_ARGS. Could the shared flag and projection extension stay as written, with runner capability declared at the owning Apple surface binding and enforced during screenshot admission, instead of treating every Apple surface as runner-capable? The only failing CI entry is Vercel deployment authorization, which explicitly requires team authorization and appears unrelated to the code changes; the diff changes website docs but no deployment authorization configuration. The local integration-node retirement failure remains unattributed without logs, because the changes overlap daemon request and context paths. No conflicts are known. Before merge, resolve the silent macOS backend override, supply the exact-head live evidence, complete outstanding validation and obtain Vercel authorization. |
There was a problem hiding this comment.
All reported issues were addressed across 17 files
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
|
This conflicts with main now. Please rebase it so review can continue. The changes requested in the review at 4b8e1fd still apply. |
4b8e1fd to
19a1423
Compare
|
Rebased and ready for another look at 19a1423. Helper override: the shared flag and projection stay as written. Runner capability is declared at the Apple binding from the surface and app backend, and Live evidence at this head (iPhone 17 Pro simulator, iOS 26.2, macOS 15.7.4, Xcode 26.3): transcript with commands, exit codes and session close. Left is the default capture, right is Still open:
|
|
The Android item from your list is now done, at the same head The session opened first, no file was written, and the session was closed afterwards. The provider-backed iOS question above is still open. |
|
At 19a1423, the earlier conflict is fixed and no conflicts are known, but provider capture still needs a fix before merge. Could screenshot admission require explicit runner support from every selected screenshot owner? An iOS provider binding can leave that capability undefined, so Limrun can report success after using its provider endpoint for an explicit runner request. This can invalidate backend comparisons. Preserve Maestro behavior with an explicit provider-default request. Could each screenshot owner declare its supported backends so the existing admission check rejects unsupported selections in one place, including providers, while each binding owns its platform explanation? Would the first step be to make runner support explicit for every binding and distinguish Maestro's provider-default capture from an explicit runner request? The helper surfaces thread can be resolved because admission already rejects them. The option forwarding thread can be resolved because dispatch restores the option before bound capture. The schema description thread is fixed at head. The false assertion thread, provider exclusion thread, and dispatch description thread are fixed at head. The only failing check is the Vercel deployment authorization, which is not a code problem. Before merge, please enforce the requested backend for provider captures and show that a provider iOS runner request is refused instead of returning a provider screenshot. |
|
Updated to 9feb269. Backends are declared per owner. Maestro: the internal Provider iOS evidence is at the admission boundary, not live: no Limrun or Revyl account is available to me. The test binds the real Please check these limits:
The helper surfaces thread is resolved, as you said it could be. |
There was a problem hiding this comment.
All reported issues were addressed across 27 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
|
At 9feb269, the explicit flag forwarding, backend admission, option description, and recording assertions are fixed since the earlier review, but the preference precedence issue remains: on the daemon path an own After fixing preference selection, please provide current-head CLI runs through an opened provider iOS session showing explicit runner returns UNSUPPORTED_OPERATION with screenshot-capture-backend-unsupported and takes no provider screenshot, and that provider Maestro comparison capture succeeds with its default backend. Repeat Android explicit runner refusal through its owner binding and local iOS Maestro comparison through runner. Include the SHA, device and OS, commands, exit statuses, observable backend or artifacts, and session cleanup. The supplied live runs used 19a1423; binding unit tests do not establish daemon routing or Maestro backend selection. The author reports no live provider run or current-head repeat. Could the runtime input type own preferredCaptureBackend, with explicit and preferred values resolved once in the binding and only resolved ScreenshotOptions passed to interactors? This would retain the owner declarations, admission helper, screenshot registry, and Apple helper-surface policy while removing the preference from ScreenshotOptions and avoiding undefined-spread precedence. The preference precedence and Android forwarding coverage threads still apply, as do three lower-priority threads. The other earlier threads are fixed or no longer apply. The only failing check is the Vercel deployment authorization, which is not a code problem. No conflicts are known. Before merge, please fix preference resolution, cover the daemon-to-binding route, post the live runs above, and get a passing affected gate or show that its failure also happens on main. |
58d7bd8 to
9feb269
Compare
|
Updated to 58d7bd8, with the live runs at this head. Preference: adopted your design. Route coverage: daemon tests now run the daemon's real output through the real binder, including the Android refusal at the capture owner. Android, live: Pixel 9 emulator, API 35 No file was written, and the session was closed. Local iOS Maestro comparison, live: iPhone 17 Pro simulator, iOS 26.2, slim, Xcode 26.3 (transcript, runner log, events). The runner log accepted exactly 3 Provider iOS has not run live, and awaits credentials. No provider account is available to me. Both cases you asked for, explicit runner refused with no provider screenshot and a provider Maestro comparison succeeding on its default backend, remain open. What exists is the admission-boundary test on the real Limrun binding with a fake interactor (output, from 9feb269). That is not a live run. Gate: The three lower-priority threads are fixed, and I replied on each. |
There was a problem hiding this comment.
All reported issues were addressed across 7 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
|
At 5c8305d, the code looks good. Maestro's runner preference now survives the daemon path, and the new test asserts the capture's destination and options, so the destination thread is fixed. Your live run at 58d7bd8, the last production change, covers the Maestro side. The comparison replay passed, and the runner log shows its captures going through the runner. The Android refusal is covered too. The one gap is the provider iOS refusal. As you noted, it has only run at the admission boundary because it needs provider credentials, so we will run The only failing check is the Vercel deployment authorization, which is not a code problem. No conflicts are known. |
…kend runner On iOS simulators with a Dynamic Island, simctl io screenshot draws the island into every capture (no --mask policy removes it), while XCUIScreen, which Maestro and other XCTest tools capture with, leaves it out. A visual baseline made by such a tool therefore cannot be matched by agent-device's default capture. The runner capture already existed for the Maestro port behind an internal flag. screenshot --capture-backend runner (SDK and MCP: captureBackend: 'runner') selects it on Apple targets. It is refused on other platforms and with --pixel-density, since the runner encodes the device density. The Maestro port now uses the public flag; flags.maestro.screenshotCaptureBackend is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The screenshot admission accepted --capture-backend runner on every Apple surface, but runAppleScreenshot returns the macOS helper capture before it reaches the runner branch, so the request reported success with another backend. The Apple binding now declares runner support from the selected surface and app backend, and assertScreenshotCaptureSupport refuses it. --normalize-status-bar was dropped the same way, so it is refused with the runner like --pixel-density. The MCP input gains its schema description. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An owner that declared nothing accepted --capture-backend runner, so a provider iOS session such as Limrun reported success with a provider screenshot. Every screenshot binding now has to declare its backends and its own explanation, and the existing admission check refuses a backend the owner does not list. The daemon's Apple-only check moves into that one place. Maestro's comparison capture asked for the runner through the public flag, which would now fail on a provider. It goes back to its internal maestro.screenshotCaptureBackend, read as a preference: the runner where the owner declares it, otherwise the owner's default capture, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The daemon passes an own captureBackend key, undefined for a Maestro request, and it overrode the preference in the binding, so a local iOS comparison capture fell back to simctl and drew the Dynamic Island back in. An explicit backend still wins. The preference moves off the shared ScreenshotOptions onto the binding input, since the binding strips it before the interactor. The daemon-level Android refusal case is back, now at the capture owner, and WebDriver's refusal has a case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5c8305d to
6d237c3
Compare
|
Rebased onto main The live runs at 58d7bd8 stand for this PR's logic, but main changed the capture path under them. #4213 reworked how a runner capture's file is copied back from the simulator, and those runs predate it. I did not repeat them, so that copy is covered here only by unit tests. #4213 also adds Gate: no single receipt at this head. One run stopped on the |
|
At 6d237c3, the rebase looks right. Main's The provider iOS refusal is still the one open item, and it stays on our side: a Not blocking, take or leave: #4213 rewrote the simulator file copy-back the runner capture uses (screenshot.ts:384), so one simulator run at this head showing a valid PNG at the output path would close that gap. The default capture under a runner-backed provider also passes No conflicts are known. |
… a runner-backed provider Under a tool provider whose simulator screenshots go through the runner, the default capture asked the runner for fullscreen, but an explicit --capture-backend runner did not. The default now lives in the runner capture, so both routes share it. An explicit fullscreen value still wins, and captures outside such a provider are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Both optional items are done, at 8797656. Copy-back after #4213, live at this head: iPhone 17 Pro simulator, iOS 26.2, slim, macOS 15.7.4, Xcode 26.3 (transcript, PNG, checker). A second capture in the same session, which takes the cached-bundle branch, passed the same checks. The runner log shows both accepted and completed with Alignment: the default now lives in Gate: The provider iOS refusal stays with you, as you said. |
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
…screen default The default checked only device.kind, so a macOS device shaped as a simulator got fullscreen under a runner-backed provider. It now uses the same predicate as the capture path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Updated to b3da8c0: a one-line fix so the runner |
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
At fe817f6, the code looks good. The fullscreen default for an explicit runner capture under a runner-backed provider is now decided in one place, captureScreenshotViaRunner, and the macOS case is gated by The provider route has only mocked coverage, so our provider run on our side now checks two things on a runner-backed iOS session. The All checks pass, and no conflicts are known. |
|
We are accepting this at fe817f6. If you have Limrun access, a run of |
Keeps main's Limrun facts split. The runner-refusal test now passes the facts main's bindLimrunInteractionOperations requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged main at The only conflict was the imports of Run locally at this head: the Limrun, screenshot, capture and runtime tests touched here (384 passed), Written by an agent. |

Summary
On a Dynamic Island simulator,
simctl io screenshotdraws the island into every capture, and no mask removes it.XCUIScreen, which XCTest tools capture with, leaves it out, so their baselines cannot match the default capture.The Maestro port's internal runner capture becomes public:
UNSUPPORTED_OPERATION): non-Apple targets, provider sessions, and macOS surfaces the helper captures.--pixel-densityand--normalize-status-barare refused with it (INVALID_ARGS): the runner skips the simctl pipeline.website/docs/docs/commands.md. Not recorded in.adscripts.Validation
Tested SHA
b3da8c0d3a2a3f7e7ea602ef9bb38729abf8c0c1, on main66438647e.check:affected --run --head b3da8c0d3: all ran and passed through the gate orpnpm gateexceptreplay-compatanddaemon-wire-compat, which need full history and did not run here (both passed at6d237c3).8797656, unchanged since: transcript, PNG.58d7bd8ec, before refactor(apple): share simulator tools and runner operations #4213: Android refusal through its owner binding; iOS Maestro comparison through the runner, by the runner log.🤖 Generated with Claude Code