ci(mobile): iOS simulator and Android emulator gates for the mobile benchmark, with a Control Inventory scenario
Draft
OK
1mo ago+2177+2177+0123456789012345678901234567890123456789-109-109-012345678901234567890123456789
Stacked on oskar/agentic-committed-caches. Draft: the first iOS run of these suites found engine defects (label ambiguity on every React Native text field, an unlabeled field's value dropped, React Native roles carried in the value text, closeApp leaving the app running, a lost session reported as ENGINE_FAILURE, foregroundApp reading the session), which land in the next pull request; until then the iOS job shows those failures, and the Android job runs for the first time.
flowchart LR
F[@expo/fingerprint] --> B{cached app?}
B -- hit --> R[repack JS with @expo/repack-app] --> D[deterministic suite]
B -- miss --> N[native build] --> D
D --> A{same repo and key?}
A -- yes --> G[agentic suite]
- A Control Inventory scenario (plain controls, every state exposed) plus
controlsanddevice-fixturesuites exercise every verb, read, matcher, refusal, anddevicemethod on a device; the flows gained state matchers. mobile.yml: iOS onblacksmith-6vcpu-macos-26, Android emulator on x64 Blacksmith with KVM..github/actions/expo-appkeys the Expo build by its native fingerprint, keeps it in the actions cache, repacks the JS on a hit, falls back to a native build.scripts/ci-run.shruns the deterministic suite and then the agentic one when the key is present (the emulator runner executes each script line separately, so the guard lives in the script). The booted simulator is handed to the config asdevice.- Skips that remain, each an upstream limit: agent-device delivers a double tap as one tap on iOS; no focused flag on iOS text fields; the snapshot carries no placeholder or attributes, so
getByPlaceholder,getAttribute, andtoHaveAttributematch nothing on a device; React Native flattens plain Views, sofilter({ has })into a View with only a test id matches nothing.