Skip to content

Photograph one device per runner, and build the app once - #170

Merged
andiwand merged 4 commits into
mainfrom
claude/screenshot-runner-limits
Aug 17, 2026
Merged

Photograph one device per runner, and build the app once#170
andiwand merged 4 commits into
mainfrom
claude/screenshot-runner-limits

Conversation

@andiwand

@andiwand andiwand commented Aug 17, 2026

Copy link
Copy Markdown
Member

The screenshot job of the 1.41 dry run failed, and nothing in the test or the
app was wrong with it. This makes the run correct, then quicker.

What was wrong

Four attempts, two devices:

attempt iPhone iPad
1 ✗ 660.072s ✗ 660.112s
2 ✓ 358.668s ✓ 360.885s
3 ✓ 557.958s ✗ runner failed to initialize
4 ✗ 118.574s ✓ 377.776s

Both devices pass and both fail, so it is neither device. Attempt 1 died on both
within forty milliseconds of each other at 660 seconds - a stopwatch, not a bug.
XCTest kills a test that runs longer than 600 seconds, and nothing here
raised it. Not one of our own assertion messages appears anywhere in that log.

The runner is macos-26-arm64: three cores, seven gigabytes, photographing two
simulators at once. Six screens take under a minute on a desktop and were taking
ten there. Attempt 3's iPad never started at all - Timed out while loading Accessibility, which is what a simulator does when it cannot get what it needs.

The changes

One simulator at a time. concurrent_simulators: false gives each device its
own xcodebuild instead of handing one invocation two destinations.

A runner per device. The matrix takes that further: the two never share a
machine, so the memory they were fighting over is not shared to begin with, and
the halves photograph at the same time.

Room to finish. executionTimeAllowance = 1800 - a ceiling, not a target.

Built once. snapshot builds and tests on every run, and there are eighteen.
scan(build_for_testing:) builds once into build/screenshots, which CI keeps
between runs, and the runs only photograph. That step takes 77 seconds against
the nine to eleven minutes the old first build did.

The simulator speaks the language too. Only the app knew before, so an iPad -
which shows the date up there - read 9:41 AM Mon Aug 17 over a Russian
document. Now 09:41 Пн 17 авг., in that locale's own clock.

The lane cannot check half a set, so screenshot-set puts the two together and
checks them once both are in. timeout-minutes: 180 bounds a job whose default
was six hours, and the raw captures are archived even when the lane failed.

Checked

A dry run of all nine languages on this branch: 108 screenshots in all 9
captured locales
, 54 at 1320x2868 and 54 at 2064x2752, merged from
framed-iphone and framed-ipad and validated as one set. 62 minutes against
76 before.

Every change was run locally first, both ways round: ODR_SCREENSHOT_DEVICE=iphone
as CI does it, and with nothing set as a person does - both devices, framed,
checked. A local run is unchanged.

Known, and not fixed here

The test itself got slower between dry runs - 85s, then 156s and 166s - which is
the wrong direction for a change that removed contention. The boot is not it
(26s against 27s), and the spread within one run is 124s to 241s, so runner noise
cannot be ruled out from two runs. It does not fail anything and the set is
right; it is worth a look when the next release gives another sample.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E6tn8P25cBDmrA67zrhfwN

andiwand and others added 4 commits August 17, 2026 17:25
The screenshot run failed on whichever device was behind. Nothing was
wrong with it: two simulators on a runner with three cores and seven
gigabytes took ten minutes over the six screens, and XCTest allows a
test ten. Both devices were killed at the same second.

So one device per xcodebuild instead of both at once, and a ceiling high
enough that only a real hang reaches it. The job gets a bound as well,
and a failed run now keeps whatever it managed to photograph.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E6tn8P25cBDmrA67zrhfwN
snapshot builds and tests on every run, and there are eighteen of them:
two devices in nine languages. The build is the same every time and
checking it costs about two minutes each on a runner, so it is built
once up front and the runs only photograph. CI keeps the build between
runs as well, which is why it goes somewhere with a name rather than the
temp directory snapshot picks.

The simulator is also told which language it is in. It was only ever the
app that knew, so an iPad - which shows the date up there - said Mon Aug
17 above a Russian document.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E6tn8P25cBDmrA67zrhfwN
Two simulators never sat on one machine again by accident: they are on
two now, a device to a runner, and the two halves photograph at the same
time. What used to be an hour and a quarter is about half that.

Neither runner can see the other's pictures, so the set is checked once
they are back together rather than by the lane. A run started by hand is
unchanged - both devices, every language, checked at the end.

The edit screen also stopped waiting two seconds for a keyboard tutorial
that the launch argument had already turned off.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E6tn8P25cBDmrA67zrhfwN
Both runners saved under the same key, so the second one found it taken
and the two halves shared whichever build got there first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E6tn8P25cBDmrA67zrhfwN
@andiwand andiwand changed the title Photograph one simulator at a time, and give the test room to finish Photograph one device per runner, and build the app once Aug 17, 2026
@andiwand
andiwand merged commit 90e77d9 into main Aug 17, 2026
4 checks passed
@andiwand
andiwand deleted the claude/screenshot-runner-limits branch August 17, 2026 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant