Run a fixed OTA regression flow for `examples/v0.81.0` with `agent-device`. Use when the caller wants a built-in end-to-end scenario instead of defining one manually: deploy a known-good OTA bundle, verify a visible UI change plus the deployed `bundleId`, then deploy an intentionally crashing OTA bundle and verify rollback to the previous stable bundle with `RECOVERED` and `crashedBundleId` evidence.
Use this skill for examples/v0.81.0 only.
Always load and follow $agent-device. Read ../e2e/references/runtime-targets.md before running anything.
This skill owns the scenario. Do not ask the caller to provide the test steps.
pnpm hot-updater deploy ....examples/v0.81.0/dist/manifest.json as the source of truth for each deployed bundle id. Capture it immediately after each deploy because the next deploy overwrites it.agent-device snapshot -i; do not use screenshots unless the caller explicitly asks.STABLE or RECOVERED.App, so the OTA bundle fails during JS module evaluation. Do not use
useEffect, component-body throws, or button-driven crashes for this skill.Run this exact two-phase flow per platform:
Before phase 1:
pnpm -w build.http://localhost:3007/hot-updater/version.<repo-root>/examples/v0.81.0.BUILTIN_BUNDLE_ID.Use these for one platform at a time.
agent-device ensure-simulator --platform ios --device "iPhone 16" --boot
cd <repo-root>/examples/v0.81.0/ios
pnpx pod-install
xcodebuild -workspace HotUpdaterExample.xcworkspace \
-scheme HotUpdaterExample \
-configuration Release \
-sdk iphonesimulator \
-destination 'id=<simulator-udid>' \
-derivedDataPath /tmp/hotupdater-v081-ios-e2e build
agent-device reinstall HotUpdaterExample \
/tmp/hotupdater-v081-ios-e2e/Build/Products/Release-iphonesimulator/HotUpdaterExample.app \
--platform ios \
--device "iPhone 16"
agent-device open org.reactjs.native.example.HotUpdaterExample \
--platform ios \
--device "iPhone 16" \
--session qa-ios-v081 \
--relaunch
cd <repo-root>/examples/v0.81.0/android
./gradlew :app:assembleRelease --rerun-tasks
agent-device reinstall com.hotupdaterexample \
<repo-root>/examples/v0.81.0/android/app/build/outputs/apk/release/app-release.apk \
--platform android \
--serial <serial>
agent-device open com.hotupdaterexample \
--platform android \
--serial <serial> \
--session qa-android-v081 \
--relaunch
Run deploy from <repo-root>/examples/v0.81.0.
pnpm hot-updater deploy -p ios -t 1.0.x
pnpm hot-updater deploy -p android -t 1.0.x
Run this from <repo-root>/examples/v0.81.0 immediately after each deploy:
node -p "require('./dist/manifest.json').bundleId"
agent-device snapshot -i
agent-device diff snapshot -i
<repo-root>/.agents/skills/e2e/scripts/inspect_ios_state.sh
<repo-root>/.agents/skills/e2e/scripts/inspect_android_state.sh
The example app is intentionally scrollable.
In this skill, capture one section at a time with a fresh snapshot.
Prefer agent-device scrollintoview "<section title>" over manual swipes when
the section heading text is available.
Use this default section order:
Runtime SnapshotLaunch StatusCrash HistoryOTA Asset Preview, Manifest Assets,
Runtime Details, and ActionsTypical navigation pattern:
agent-device snapshot -i
agent-device scrollintoview "Launch Status"
agent-device snapshot -i
agent-device scrollintoview "Crash History"
agent-device snapshot -i
If text lookup drifts, fall back to agent-device swipe ... or other explicit
scroll gestures and capture a fresh snapshot immediately after the motion.
For the first clean launch after install:
agent-device snapshot -i at the top of the scroll view.BUILTIN_BUNDLE_ID from the Bundle ID row in Runtime Snapshot.Manifest Bundle ID matches the same built-in id.agent-device scrollintoview "Crash History".agent-device snapshot -i.No crashed bundles recorded.After the stable OTA deploy and app relaunch:
agent-device snapshot -i at the top of the scroll view.Scenario MarkerE2E AUTO SUCCESSBundle ID = STABLE_BUNDLE_IDManifest Bundle ID = STABLE_BUNDLE_IDagent-device scrollintoview "Launch Status".agent-device snapshot -i."status": "STABLE".agent-device scrollintoview "Crash History".agent-device snapshot -i.No crashed bundles recorded.agent-device scrollintoview "Manifest Assets", snapshot that section, and
verify the visible asset hash there.After the crash OTA deploy and the recovered relaunch:
agent-device snapshot -i at the top of the recovered scroll view.Scenario MarkerE2E AUTO SUCCESSBundle ID = STABLE_BUNDLE_IDManifest Bundle ID = STABLE_BUNDLE_IDagent-device scrollintoview "Launch Status".agent-device snapshot -i."status": "RECOVERED" and
"crashedBundleId": "<CRASH_BUNDLE_ID>".agent-device scrollintoview "Crash History".agent-device snapshot -i.CRASH_BUNDLE_ID.Manifest Assets, Runtime Details, or Actions and capture another
snapshot there.agent-device diff snapshot -i immediately after each section change or
after recovery relaunch when you want a compact confirmation of what changed.Create a temporary visible marker in examples/v0.81.0/App.tsx.
Use this exact marker text:
const AUTO_E2E_SUCCESS_MARKER = "E2E AUTO SUCCESS";
Render it in the Runtime Snapshot section with:
<InfoRow label="Scenario Marker" value={AUTO_E2E_SUCCESS_MARKER} />
Recommended placement:
readRuntimeSnapshot.InfoRow next to the other bundle metadata rows in Runtime Snapshot.Then:
STABLE_BUNDLE_ID from dist/manifest.json.Phase 1 passes only if all of these are true:
Scenario Marker and E2E AUTO SUCCESS.Bundle ID equals STABLE_BUNDLE_ID.Manifest Bundle ID equals STABLE_BUNDLE_ID."status": "STABLE".metadata.json references STABLE_BUNDLE_ID as the stable bundle and is not verification-pending.Start from the clean baseline source with the success patch already reverted.
Add this temporary crash patch at module scope, outside App, with the real
values substituted:
const AUTO_E2E_SAFE_BUNDLE_IDS = new Set([
"<BUILTIN_BUNDLE_ID>",
"<STABLE_BUNDLE_ID>",
]);
const AUTO_E2E_CURRENT_BUNDLE_ID = HotUpdater.getBundleId();
if (!AUTO_E2E_SAFE_BUNDLE_IDS.has(AUTO_E2E_CURRENT_BUNDLE_ID)) {
throw new Error("hot-updater e2e-auto crash bundle");
}
Recommended placement:
function App().App, useEffect, or any UI event
handler. Those patterns do not reliably produce the crashing OTA bundle this
scenario needs.Then:
CRASH_BUNDLE_ID from dist/manifest.json.Phase 2 passes only if all of these are true:
Scenario Marker and E2E AUTO SUCCESS.Bundle ID equals STABLE_BUNDLE_ID, not CRASH_BUNDLE_ID.Manifest Bundle ID equals STABLE_BUNDLE_ID, not CRASH_BUNDLE_ID."status": "RECOVERED"."crashedBundleId": "<CRASH_BUNDLE_ID>".CRASH_BUNDLE_ID.launch-report.json reports RECOVERED with crashedBundleId = CRASH_BUNDLE_ID.metadata.json is no longer verification-pending and still points at STABLE_BUNDLE_ID as the stable bundle.Treat recovery to the built-in bundle as a failure for this scenario. The required behavior is rollback to the previous stable OTA bundle.
Use the platform-native JSON keys when checking raw files:
stable_bundle_id, staging_bundle_id, verification_pendingstableBundleId, stagingBundleId, verificationPendingstatus, crashedBundleIdIf the caller asks for a report, include:
Release.BUILTIN_BUNDLE_IDSTABLE_BUNDLE_IDCRASH_BUNDLE_IDstatuscrashedBundleId<repo-root> means the checked-out repository root.Bundle ID, Manifest Bundle ID, status JSON, and crash history in examples/v0.81.0/App.tsx.org.reactjs.native.example.HotUpdaterExample, even though hot-updater.config.ts uses com.hotupdaterexample for native build config.