Skip to content
Unverified: This page has not yet been anchored to automated code tests.

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”

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:

WhatEvidence
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 rewritecounter 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 importNavBar renders on every screen

Data fetches return empty/0 because no backend was running — the app degrades gracefully (the generated match Ok/Error handles the rejected fetch). A real run points EXPO_PUBLIC_VOX_API_URL at 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.)

Terminal window
# 0. Prereqs: Android SDK (emulator + platform-tools), Node 20+, an AVD.
export ANDROID_HOME="$LOCALAPPDATA/Android/Sdk" # Windows path shown
export 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/voxapp
npm install
npx expo start # Metro only (headless-friendly)
# 5. Launch on the emulator (in another shell):
adb reverse tcp:8081 tcp:8081
adb shell am start -a android.intent.action.VIEW \
-d "exp://127.0.0.1:8081" host.exp.exponent
adb exec-out screencap -p > screen.png # capture proof

Notes:

  • adb reverse lets the emulator reach the host’s Metro on 127.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 --android auto-opens but prompts interactively (Expo Go version) — the adb deep-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:

  1. Sign up at https://expo.dev/signup.
  2. npm i -g eas-cli && eas login.
  3. From a generated app dir (/tmp/voxapp), run eas init to create/link an EAS project (writes the projectId into app.json → extra.eas). Commit that projectId back into the generator if you want it baked in.
  4. Create a CI token: https://expo.dev/settings/access-tokens → “Create token”. Copy it.
  5. Add it as a GitHub Actions secret named EXPO_TOKEN (Repo → Settings → Secrets and variables → Actions → New repository secret).

.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 in ci/expo-bundle-shim/vox-runtime-rn, installs deps, and runs npx expo export. This is the real gate: it fails if the generated app doesn’t bundle (exactly the class of bug — import.meta, missing zod — that the emulator run surfaced). Active now.
  • eas-build — runs only on workflow_dispatch and when EXPO_TOKEN is set. Submits a cloud Android APK build via eas build. Skips cleanly until you complete §3, so the workflow is green from day one.

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.ts emitted import.meta.env (Hermes parse error) → target-aware base-URL resolution (process.env.EXPO_PUBLIC_VOX_API_URL for RN).
  • generated package.json missing zod + expo-asset; version skew on react-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.