Expo Dev Loop
Prove the changed behavior in the running app. Static inspection, type checks,
unit tests, and a successful bundle support the result but do not replace device
verification.
Establish the target
Inspect the request, current diff, Expo config, and package manifest. Identify
the intended flow, platform, device, and observable result. When the request
does not require both platforms, use one available representative local target
and state that coverage; never infer iOS and Android parity from one run.
Use the installed CLI's version-matched guidance instead of remembered command
shapes:
agent-device help workflow
agent-device help react-native
If agent-device is unavailable, report that runtime verification is blocked
and give npm install -g agent-device@latest as the setup command.
Choose the runtime path
Classify the whole change before launching the app:
- Metro path: JavaScript or TypeScript behavior, React components, styles,
navigation code, and bundle-loaded assets that do not alter the native app.
Reuse the running development build and Metro server. Let Fast Refresh apply
the change or use
agent-device metro reload when a full JS reload is
needed.
- Native path: Expo app config that affects the binary, config plugins,
native modules or dependencies, permissions, entitlements, icons or splash
configuration, native project files, SDK or React Native upgrades, and
startup behavior. Use the repository-supported build command, normally
expo run:ios or expo run:android through the project's own package
manager, install the resulting development build, and relaunch it. npx
aborts with EBADDEVENGINES where package.json pins another package
manager through devEngines.
Use the native path for a mixed change or when native impact remains uncertain.
Both paths run on a development build of the project, so the classification
decides whether that build must be rebuilt rather than which client to use.
Expo Go is a fixed prebuilt shell whose native surface belongs to Expo rather
than to this project, so a result observed there is about a different binary
than the one being shipped; build a development build instead. Preserve the
project's managed or checked-in native workflow rather than regenerating native
directories as an incidental verification step.
Pass preflight before observation
Keep three gates separate and report the first one that fails:
- Runtime readiness: the intended target, development build, project-owned
Metro server, and observation session are usable, and the loaded bundle or
native build belongs to this checkout.
- Known initial state: app-local data, OS permissions, login or fixture
state, and any affected backend have the state the scenario requires.
- Behavior verification: the ready app is driven and its named outcome is
asserted. A successful launch or prepared state never satisfies this gate.
Use narrow readiness checks in the ordinary loop. Run agent-device doctor
only when the user asks for setup diagnosis or a failed readiness check suggests
an unhealthy device, app, development server, or runner.
Choose the least destructive state profile that proves the behavior:
- Known app state: prepare only the app data, explicit permissions, login,
and fixture state the flow needs. App-state clearing and permission reset are
separate boundaries; report which were changed and which were preserved.
- Fresh device: use only when the behavior depends on first-device state,
such as first launch, OS permission history, secure storage, application
identity, deep links, push behavior, native configuration, or another
device-level surface. A simulator or emulator is dedicated only when the
request or project allocation establishes that ownership; absence from an
advisory claim list is not proof. Fresh-device preparation requires
agent-device 0.20.10 or newer, whose device claims reject foreign local
mutations; earlier releases advertise claims without enforcing them. Acquire
the exact target through the loop's final named session with
agent-device open, confirm agent-device device status names that session
and workspace, and keep the claim through reset, boot, reinstall, Metro
reconnection, and observation. If the claim cannot be acquired or held
across the selected reset mechanism, stop before reset. Address only that
target, never all devices. After reset, re-establish any allowed backend
fixture before observation.
- Preserved or prior state: retain or seed the earlier app and backend state
required by returning-user, upgrade, or migration behavior. Do not clear away
the premise being tested.
Never erase a physical, user-owned, shared, or otherwise unowned target. When a
fresh-device check has no eligible virtual target, report the runtime-readiness
blocker. When a usable target exists but its required state cannot safely be
established, report the known-initial-state blocker. Do not substitute
destructive app or backend actions. Treat a stateful backend as a separate
affected surface: use only the project's deterministic local or test
preparation path, and never infer backend state from a ready client or
destructively reset a remote service under ordinary verification authority.
Prepare the observation loop
Keep state-changing device commands serial within one session. Open the actual
installed app identifier or development-client URL reported by the running
tools; do not invent one. A URL target rejects --relaunch, so open the app id
when startup or clean process state matters, then capture the initial
interactive snapshot.
Point the client at this project's own dev server rather than a default port
another checkout may already hold. Pass the port as a session runtime hint with
--metro-port, and confirm the loaded bundle belongs to this project: a
development build left pointing at a stale URL loads another project's bundle
and reports that project's source paths as if they were this one's.
When Expo MCP local capabilities are already available, use its Router sitemap,
current Expo documentation, or short app-log collection as framework-side
context. Treat them as optional discovery and diagnostics; agent-device
remains the device-side proof.
Verify after each edit
Exercise the smallest complete user flow affected by the edit and check four
layers:
- Loaded: the intended Metro update or native build is running on the
selected target, with no bundle, build, or incompatible-client error.
- Healthy: reproduce inside a focused log window and check for relevant
JavaScript errors, native crashes, RedBox or LogBox failures, and rejected
network requests.
- Correct: drive the flow with
press, fill, scroll, back, and other
appropriate commands using --settle; verify the named outcome with an
exact wait, is, get, or find assertion. A screenshot alone does not
prove a behavioral expectation.
- Sound at the claimed layer: inspect the React tree, props, hooks,
re-renders, native performance, network data, or traces only when the change
makes a claim about that layer.
Use refs from the latest snapshot or settled diff. A state-changing command
invalidates earlier refs; refresh the snapshot or use a stable id, label, role,
or testID before the next action. Prefer semantic selectors and use coordinates
only when the accessibility surface cannot expose the target, recording that
limitation with visual evidence.
Collect evidence proportional to the claim: a screenshot for visual output,
focused logs for runtime behavior, network output for request behavior,
performance artifacts for performance claims, and a recorded .ad replay for
a flow worth keeping as a regression check.
When a workaround such as clearing a cache or rebuilding leaves its root cause
open, or you observe an out-of-scope defect with evidence, record it at the
moment of discovery through the project-knowledge skill. If that skill is
unavailable, write the symptom, observed evidence, suspected cause, what was
tried, and a proposed next step to docs/follow-ups/<slug>.md yourself.
Reporting it only in conversation loses it.
Finish the loop
Finish only when the selected runtime contains the current change, the exact
user-visible expectation passes, relevant runtime errors are absent during the
reproduction, and every platform claim has device evidence. Report the target,
state profile, readiness evidence, prepared and preserved state boundaries,
flow, assertions, and artifact paths, separating observed results from remaining
inference or unverified coverage.
If verification is blocked, name the app, platform, session, failed gate, and
the exact next command or user action needed. Close the agent-device session
when finished. Leave a healthy Metro server running for the next edit loop
unless the user requested cleanup; in CI, release the device with
agent-device close --shutdown.
1---2name: expo-dev-loop3description: Verify Expo and React Native changes in a running app with agent-device. Use after editing screens, navigation, interactions, app configuration, config plugins, native dependencies, permissions, startup behavior, or performance, or whenever the user asks to confirm an Expo change on an iOS or Android simulator, emulator, or physical device. Establish target readiness and the scenario's required state, classify whether the change can use the Metro fast path or requires a native rebuild, then finish only with observable runtime evidence.4---56# Expo Dev Loop78Prove the changed behavior in the running app. Static inspection, type checks,9unit tests, and a successful bundle support the result but do not replace device10verification.1112## Establish the target1314Inspect the request, current diff, Expo config, and package manifest. Identify15the intended flow, platform, device, and observable result. When the request16does not require both platforms, use one available representative local target17and state that coverage; never infer iOS and Android parity from one run.1819Use the installed CLI's version-matched guidance instead of remembered command20shapes:2122```bash23agent-device help workflow24agent-device help react-native25```2627If `agent-device` is unavailable, report that runtime verification is blocked28and give `npm install -g agent-device@latest` as the setup command.2930## Choose the runtime path3132Classify the whole change before launching the app:3334- **Metro path:** JavaScript or TypeScript behavior, React components, styles,35 navigation code, and bundle-loaded assets that do not alter the native app.36 Reuse the running development build and Metro server. Let Fast Refresh apply37 the change or use `agent-device metro reload` when a full JS reload is38 needed.39- **Native path:** Expo app config that affects the binary, config plugins,40 native modules or dependencies, permissions, entitlements, icons or splash41 configuration, native project files, SDK or React Native upgrades, and42 startup behavior. Use the repository-supported build command, normally43 `expo run:ios` or `expo run:android` through the project's own package44 manager, install the resulting development build, and relaunch it. `npx`45 aborts with `EBADDEVENGINES` where `package.json` pins another package46 manager through `devEngines`.4748Use the native path for a mixed change or when native impact remains uncertain.49Both paths run on a development build of the project, so the classification50decides whether that build must be rebuilt rather than which client to use.51Expo Go is a fixed prebuilt shell whose native surface belongs to Expo rather52than to this project, so a result observed there is about a different binary53than the one being shipped; build a development build instead. Preserve the54project's managed or checked-in native workflow rather than regenerating native55directories as an incidental verification step.5657## Pass preflight before observation5859Keep three gates separate and report the first one that fails:60611. **Runtime readiness:** the intended target, development build, project-owned62 Metro server, and observation session are usable, and the loaded bundle or63 native build belongs to this checkout.642. **Known initial state:** app-local data, OS permissions, login or fixture65 state, and any affected backend have the state the scenario requires.663. **Behavior verification:** the ready app is driven and its named outcome is67 asserted. A successful launch or prepared state never satisfies this gate.6869Use narrow readiness checks in the ordinary loop. Run `agent-device doctor`70only when the user asks for setup diagnosis or a failed readiness check suggests71an unhealthy device, app, development server, or runner.7273Choose the least destructive state profile that proves the behavior:7475- **Known app state:** prepare only the app data, explicit permissions, login,76 and fixture state the flow needs. App-state clearing and permission reset are77 separate boundaries; report which were changed and which were preserved.78- **Fresh device:** use only when the behavior depends on first-device state,79 such as first launch, OS permission history, secure storage, application80 identity, deep links, push behavior, native configuration, or another81 device-level surface. A simulator or emulator is dedicated only when the82 request or project allocation establishes that ownership; absence from an83 advisory claim list is not proof. Fresh-device preparation requires84 `agent-device` 0.20.10 or newer, whose device claims reject foreign local85 mutations; earlier releases advertise claims without enforcing them. Acquire86 the exact target through the loop's final named session with87 `agent-device open`, confirm `agent-device device status` names that session88 and workspace, and keep the claim through reset, boot, reinstall, Metro89 reconnection, and observation. If the claim cannot be acquired or held90 across the selected reset mechanism, stop before reset. Address only that91 target, never all devices. After reset, re-establish any allowed backend92 fixture before observation.93- **Preserved or prior state:** retain or seed the earlier app and backend state94 required by returning-user, upgrade, or migration behavior. Do not clear away95 the premise being tested.9697Never erase a physical, user-owned, shared, or otherwise unowned target. When a98fresh-device check has no eligible virtual target, report the runtime-readiness99blocker. When a usable target exists but its required state cannot safely be100established, report the known-initial-state blocker. Do not substitute101destructive app or backend actions. Treat a stateful backend as a separate102affected surface: use only the project's deterministic local or test103preparation path, and never infer backend state from a ready client or104destructively reset a remote service under ordinary verification authority.105106## Prepare the observation loop107108Keep state-changing device commands serial within one session. Open the actual109installed app identifier or development-client URL reported by the running110tools; do not invent one. A URL target rejects `--relaunch`, so open the app id111when startup or clean process state matters, then capture the initial112interactive snapshot.113114Point the client at this project's own dev server rather than a default port115another checkout may already hold. Pass the port as a session runtime hint with116`--metro-port`, and confirm the loaded bundle belongs to this project: a117development build left pointing at a stale URL loads another project's bundle118and reports that project's source paths as if they were this one's.119120When Expo MCP local capabilities are already available, use its Router sitemap,121current Expo documentation, or short app-log collection as framework-side122context. Treat them as optional discovery and diagnostics; `agent-device`123remains the device-side proof.124125## Verify after each edit126127Exercise the smallest complete user flow affected by the edit and check four128layers:1291301. **Loaded:** the intended Metro update or native build is running on the131 selected target, with no bundle, build, or incompatible-client error.1322. **Healthy:** reproduce inside a focused log window and check for relevant133 JavaScript errors, native crashes, RedBox or LogBox failures, and rejected134 network requests.1353. **Correct:** drive the flow with `press`, `fill`, `scroll`, `back`, and other136 appropriate commands using `--settle`; verify the named outcome with an137 exact `wait`, `is`, `get`, or `find` assertion. A screenshot alone does not138 prove a behavioral expectation.1394. **Sound at the claimed layer:** inspect the React tree, props, hooks,140 re-renders, native performance, network data, or traces only when the change141 makes a claim about that layer.142143Use refs from the latest snapshot or settled diff. A state-changing command144invalidates earlier refs; refresh the snapshot or use a stable id, label, role,145or testID before the next action. Prefer semantic selectors and use coordinates146only when the accessibility surface cannot expose the target, recording that147limitation with visual evidence.148149Collect evidence proportional to the claim: a screenshot for visual output,150focused logs for runtime behavior, network output for request behavior,151performance artifacts for performance claims, and a recorded `.ad` replay for152a flow worth keeping as a regression check.153154When a workaround such as clearing a cache or rebuilding leaves its root cause155open, or you observe an out-of-scope defect with evidence, record it at the156moment of discovery through the `project-knowledge` skill. If that skill is157unavailable, write the symptom, observed evidence, suspected cause, what was158tried, and a proposed next step to `docs/follow-ups/<slug>.md` yourself.159Reporting it only in conversation loses it.160161## Finish the loop162163Finish only when the selected runtime contains the current change, the exact164user-visible expectation passes, relevant runtime errors are absent during the165reproduction, and every platform claim has device evidence. Report the target,166state profile, readiness evidence, prepared and preserved state boundaries,167flow, assertions, and artifact paths, separating observed results from remaining168inference or unverified coverage.169170If verification is blocked, name the app, platform, session, failed gate, and171the exact next command or user action needed. Close the `agent-device` session172when finished. Leave a healthy Metro server running for the next edit loop173unless the user requested cleanup; in CI, release the device with174`agent-device close --shutdown`.