feat(screenshot): select the XCTest runner capture with --capture-backend runner
VI
15h ago+126+126+012345678901234567890123456789-16-16-01234567890123456789
Summary
On an iOS simulator with a Dynamic Island, simctl io screenshot draws the island into every capture. No --mask policy (ignored, alpha, black) removes it: the island is in the framebuffer. XCUIScreen, which Maestro and other XCTest tools capture with, leaves it out. So a baseline made by such a tool cannot be matched by agent-device's default capture: the pill lands in the top rows.
The runner capture already existed for the Maestro port, behind the internal flags.maestro.screenshotCaptureBackend. This PR makes it public:
agent-device screenshot page.png --capture-backend runner
await client.capture.screenshot({ path, captureBackend: 'runner' }); // MCP: captureBackend
- Refused on non-Apple targets (
UNSUPPORTED_OPERATION), with--pixel-density(INVALID_ARGS: the runner encodes the device density and skips normalization), and for any value butrunner. - The Maestro port uses the public flag;
flags.maestro.screenshotCaptureBackendis removed. - 17 files. Docs:
website/docs/docs/commands.md. Not recorded in.adscripts.
Validation
Tested SHA 4b8e1fdd1.
- New/changed cases (projections, CLI parse, SDK/MCP reader and writer, daemon context, admission refusals, Maestro port) were red before the change; the 7 affected files pass (139 tests).
check:affected --run --head 4b8e1fdd1: format, lint, typecheck, layering, di-seams, fallow, native-dupes, mcp-metadata, build and package passed.integration-nodefailed onsmoke-daemon-clean"forced retirement…". It also fails 2/3 runs onmainon this host. Later checks did not run, so this is a draft; CI is the authority.- On a real iPhone 17 Pro simulator (iOS 26.2): default capture vs runner capture against a Maestro baseline, pill region RMSE 0.246 vs 0.021.
🤖 Generated with Claude Code