From f7a4e94a71349e5488c165f1c7bb6aaa3c674492 Mon Sep 17 00:00:00 2001 From: Robert-Jan Huijsman <22160949+rjhuijsman@users.noreply.github.com> Date: Fri, 11 Sep 2026 19:58:51 +0000 Subject: [PATCH 1/2] CI: run `Check Code Style` on a Depot runner The style checker builds the dev container before it can lint anything, so it pays the full container build cost. Before this change the job ran on a GitHub-hosted 4-core runner, the only job in the workflow still doing so - the 2025-05-22 change that onboarded our Depot runners moved the others over and left this one behind. Nothing documents a reason to keep it there: unlike the Maestro job, which needs `/dev/kvm` that Depot does not expose, the style checker has no GitHub-hosted-only requirement. That mattered because whenever a pull request changes a `Dockerfile` `ARG` above the first `FROM`, the build starts from scratch and re-downloads a few hundred packages. On the 4-core GitHub-hosted runners the `clang-20` layer's fetches from `apt.llvm.org` failed often - DNS returning an IPv6-only answer on a runner with no IPv6 route, or plain "Could not resolve 'apt.llvm.org'" partway through the downloads - and the style checker never ran at all. The job died that way on four consecutive runs on 2026-09-10 and 2026-09-11, for example run 34584281746. The same layer built cleanly every time on the Depot runners the sibling jobs already use, and in `copilot-setup-steps`. The job now runs on the same `depot-ubuntu-24.04-32` runner as the Linux x86_64 build, which also makes the container build itself several times faster. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_0143bFU89gPnVXRJ1BQFgX1A --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index c2cfa6881..985a4aaba 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -97,7 +97,7 @@ jobs: secrets: inherit with: cancel-workflow-on-failure: true - runs-on: 4_core_GitHub_hosted + runs-on: depot-ubuntu-24.04-32 run: | # Run check code style from dev-tools on all of our files using our # codespace image. This ensures that the version of code checking tools From b5fca3eb74176bf1875b57901eac876424e70418 Mon Sep 17 00:00:00 2001 From: Robert-Jan Huijsman <22160949+rjhuijsman@users.noreply.github.com> Date: Mon, 14 Sep 2026 10:22:03 +0000 Subject: [PATCH 2/2] `tests`: open the Expo project only once the boot broadcasts drain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bank-pydantic Maestro flow failed twice in a row on a PR that touched nothing near it: once with `sign-in-button` never becoming visible, and once with `Assertion is false: "Alice" is visible` right after `Tap on id: sign-in-button` had "completed" — the screenshot still showing the signed-out app, with no browser tab in sight. The second run's `maestro-debug` logcat told the story. The flow had dismissed Expo Go's onboarding sheet as designed, and 0.4s later Android relaunched Expo Go's `ExperienceActivity` (`finishDrawing of relaunch`), which re-ran the app and re-opened the onboarding sheet two seconds later. The sign-in tap then landed on the sheet's backdrop and merely closed it, so the OAuth tab never opened. That relaunch coincided with the delayed package events for Expo Go's own fresh install (`onPackageAdded: host.exp.exponent`), and all of it happened while the system was still delivering `BOOT_COMPLETED`: `ActivityManager` logged `Finished processing BOOT_COMPLETED` a full 30 seconds after `sys.boot_completed` had flipped to 1 and the harness had already run `expo start --android`. The harness's readiness check is the problem. `sys.boot_completed` is set when the system sends `BOOT_COMPLETED`, not when the dozens of manifest receivers on this `google_apis` image are done with it, so the flow starts on a device that is still mid-boot. Whether the project then survives is a matter of timing: the chat-room flow in the same job began 45 seconds after the property flipped and passed, bank-pydantic's began 15 seconds after and did not. Wait for the broadcast queues to go idle (`am wait-for-broadcast-idle`) after `sys.boot_completed`, in both harnesses, so Expo Go is installed and the project opened on a settled system. The wait is bounded at two minutes, so a device that never goes quiet costs a fixed delay that still outlasts the boot storm instead of the whole test budget. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01DvoLW5RiQ6EWUKzZQoC4oX --- .../frontend/mobile/.tests/maestro_test.sh | 14 ++++++++++++++ .../frontend/mobile/.tests/maestro_test.sh | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/reboot/examples/bank-pydantic/frontend/mobile/.tests/maestro_test.sh b/reboot/examples/bank-pydantic/frontend/mobile/.tests/maestro_test.sh index d2ecc72ea..b783beea0 100755 --- a/reboot/examples/bank-pydantic/frontend/mobile/.tests/maestro_test.sh +++ b/reboot/examples/bank-pydantic/frontend/mobile/.tests/maestro_test.sh @@ -193,6 +193,20 @@ adb wait-for-device until [ "$(adb shell getprop sys.boot_completed 2> /dev/null | tr -d '\r')" = "1" ]; do sleep 1 done +# `sys.boot_completed` flips when the system sends `BOOT_COMPLETED`, +# not when its receivers are done with it. On this `google_apis` image +# that broadcast fans out to Play Services and dozens of other manifest +# receivers, which keeps the broadcast queues busy for another ~30s, +# and package events for anything installed meanwhile queue up behind +# it. Opening the project inside that window has had Android relaunch +# Expo Go's activity mid-flow, as the delayed package events for its +# own fresh install landed: the app re-ran, the onboarding sheet the +# flow had just dismissed re-opened, and the flow's next tap hit the +# sheet instead of the app. So wait for the queues to drain before +# opening the project. The wait is bounded so that a device that never +# goes quiet degrades to a fixed delay, which still outlasts the boot +# storm, rather than eating the whole test budget. +timeout 120 adb shell am wait-for-broadcast-idle > /dev/null || true # Skip Chrome's first-run screen. Signing in hands the OAuth # authorization URL to an in-app browser tab, which Chrome serves; on diff --git a/reboot/examples/chat-room/frontend/mobile/.tests/maestro_test.sh b/reboot/examples/chat-room/frontend/mobile/.tests/maestro_test.sh index 0d927fcbd..28ac20c32 100755 --- a/reboot/examples/chat-room/frontend/mobile/.tests/maestro_test.sh +++ b/reboot/examples/chat-room/frontend/mobile/.tests/maestro_test.sh @@ -187,6 +187,20 @@ adb wait-for-device until [ "$(adb shell getprop sys.boot_completed 2> /dev/null | tr -d '\r')" = "1" ]; do sleep 1 done +# `sys.boot_completed` flips when the system sends `BOOT_COMPLETED`, +# not when its receivers are done with it. On this `google_apis` image +# that broadcast fans out to Play Services and dozens of other manifest +# receivers, which keeps the broadcast queues busy for another ~30s, +# and package events for anything installed meanwhile queue up behind +# it. Opening the project inside that window has had Android relaunch +# Expo Go's activity mid-flow, as the delayed package events for its +# own fresh install landed: the app re-ran, the onboarding sheet the +# flow had just dismissed re-opened, and the flow's next tap hit the +# sheet instead of the app. So wait for the queues to drain before +# opening the project. The wait is bounded so that a device that never +# goes quiet degrades to a fixed delay, which still outlasts the boot +# storm, rather than eating the whole test budget. +timeout 120 adb shell am wait-for-broadcast-idle > /dev/null || true # Load the app via Expo Go, pointed at the backend on the emulator's # host alias (`10.0.2.2`). `expo start --android` installs Expo Go and