Mobile: emulator run proof + EAS CI / Expo account setup
Mobile: emulator run proof + EAS CI / Expo account setup
Section titled “Mobile: emulator run proof + EAS CI / Expo account setup”1. Proof of life (2026-05-30)
Section titled “1. Proof of life (2026-05-30)”The mental-tracker app, generated by vox build --target=mobile, was run on a
live Android emulator (Medium_Phone_API_36.1, Expo Go / SDK 52). Every
RN-codegen fix landed this cycle was verified visually and interactively:
| What | Evidence |
|---|---|
App compiles + bundles (Metro → Hermes 3 MB .hbc) | npx expo export succeeds |
| Home screen renders natively (heading, subtitle, styled buttons) | assets/mobile-proof-2026/home-screen.png |
Interactivity: useState + onPress + set_x rewrite | counter 0 → 3 after taps — assets/mobile-proof-2026/home-counter-3.png |
Navigation: link(href) → expo-router <Link> | tap “Timeline” routes index → timeline — assets/mobile-proof-2026/timeline-screen.png |
Lifecycle: on mount: async load (awaited) | Timeline shows “Events stored: 0” (the awaited health_event_count() result) |
| Cross-component import | NavBar renders on every screen |
Data fetches return empty/0 because no backend was running — the app degrades gracefully (the generated
match Ok/Errorhandles the rejected fetch). A real run pointsEXPO_PUBLIC_VOX_API_URLat a Vox server.
2. Run the generated app on an Android emulator (reproducible)
Section titled “2. Run the generated app on an Android emulator (reproducible)”This path uses Expo Go + a JS shim for @vox/runtime-rn, so it needs no
native build — it proves the UI/navigation/lifecycle codegen. (The native
runtime ships via a dev client / EAS Build; see §4.)
# 0. Prereqs: Android SDK (emulator + platform-tools), Node 20+, an AVD.export ANDROID_HOME="$LOCALAPPDATA/Android/Sdk" # Windows path shownexport PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH"emulator -avd <your_avd> -no-snapshot-save & # boot emulator
# 1. Generate the Expo app.vox build apps/vox-mental-tracker/src/main.vox --target=mobile -o /tmp/voxapp
# 2. Swap the native runtime for the CI JS shim (in this repo).cp -r ci/expo-bundle-shim/vox-runtime-rn /tmp/voxapp/shims-vox-runtime-rn# package.json: set "@vox/runtime-rn": "file:./shims-vox-runtime-rn",# and drop "@vox/runtime-types" (types only; not needed at runtime).
# 3. Placeholder app icon/splash (the generator references ./assets/splash.png).mkdir -p /tmp/voxapp/assets # drop any PNG named splash.png / icon.png here
# 4. Install + run.cd /tmp/voxappnpm installnpx expo start # Metro only (headless-friendly)
# 5. Launch on the emulator (in another shell):adb reverse tcp:8081 tcp:8081adb shell am start -a android.intent.action.VIEW \ -d "exp://127.0.0.1:8081" host.exp.exponentadb exec-out screencap -p > screen.png # capture proofNotes:
adb reverselets the emulator reach the host’s Metro on127.0.0.1:8081.- If port 8081 is held, kill the stale Metro (
Get-NetTCPConnection -LocalPort 8081 | Stop-Process …on Windows) before restarting. npx expo start --androidauto-opens but prompts interactively (Expo Go version) — theadbdeep-link above avoids that in non-interactive shells.
3. Create an Expo account (manual — one-time)
Section titled “3. Create an Expo account (manual — one-time)”EAS Build (cloud APK/IPA builds) needs a free Expo account. These steps are interactive and must be done by a human:
- Sign up at https://expo.dev/signup.
npm i -g eas-cli && eas login.- From a generated app dir (
/tmp/voxapp), runeas initto create/link an EAS project (writes theprojectIdintoapp.json→extra.eas). Commit thatprojectIdback into the generator if you want it baked in. - Create a CI token: https://expo.dev/settings/access-tokens → “Create token”. Copy it.
- Add it as a GitHub Actions secret named
EXPO_TOKEN(Repo → Settings → Secrets and variables → Actions → New repository secret).
4. CI runner — mobile-eas-build.yml
Section titled “4. CI runner — mobile-eas-build.yml”.github/workflows/mobile-eas-build.yml has two jobs:
bundle— runs on every mobile-touching push/PR, no account needed. It generates the Expo app, swaps inci/expo-bundle-shim/vox-runtime-rn, installs deps, and runsnpx expo export. This is the real gate: it fails if the generated app doesn’t bundle (exactly the class of bug —import.meta, missingzod— that the emulator run surfaced). Active now.eas-build— runs only onworkflow_dispatchand whenEXPO_TOKENis set. Submits a cloud Android APK build viaeas build. Skips cleanly until you complete §3, so the workflow is green from day one.
Remaining to fully automate device E2E
Section titled “Remaining to fully automate device E2E”- iOS is unverified (no macOS host) — see
vox-runtime-rn-mobile-cross-compile.mdTODO(ios-cross-compile)and the#[ignore]d gate incrates/vox-cli-tests/tests/mobile_cross_compile.rs. - On-emulator E2E in CI (Maestro/Detox driving the booted app) is the
natural next step on top of the
bundlegate; tracked inmobile-e2e-testing-strategy-2026.md.
5. Codegen gaps this run surfaced (now fixed)
Section titled “5. Codegen gaps this run surfaced (now fixed)”The emulator run found bugs that static builds didn’t, all fixed in the RN
codegen this cycle (see git log on mobile/rn-expo-foundation):
vox-client.tsemittedimport.meta.env(Hermes parse error) → target-aware base-URL resolution (process.env.EXPO_PUBLIC_VOX_API_URLfor RN).- generated
package.jsonmissingzod+expo-asset; version skew onreact-native/expo-status-bar→ pinned to SDK-52-compatible versions. link(href)rendered inert; on-mount data never loaded; endpoint calls sent positional args — all fixed earlier in the cycle.