e2e · git:20260402.0d91469 · 2026-04-02 · sha256 c60e57092511fcd6

e2e git:20260402.0d91469A

Immutable. This exact content never changes and is served at /api/v1/blob/c60e57092511fcd6.

---
name: e2e
description: Run end-to-end OTA verification for `examples/v0.81.0` with `agent-device`. Use when validating iOS or Android release builds, deploying OTA bundles with `pnpm hot-updater deploy`, checking stable update application, reproducing rollback after a crash bundle, or reading bundle-store metadata and crash history for the v0.81.0 example app.
---

# Hot Updater V0.81 E2E

Use this skill for `examples/v0.81.0` OTA verification only.

Always load and follow [$agent-device](../agent-device/SKILL.md) for device interaction.

Do not encode a fixed test scenario in this skill. The caller provides the scenario. This skill only supplies fixed targets, guardrails, command templates, and inspection helpers for `examples/v0.81.0`.

## Rules

- Run iOS and Android sequentially. Do not overlap platform runs.
- Complete the full iOS flow or the full Android flow first, then move to the other platform. Never execute both platforms at the same time.
- Use `agent-device snapshot`; do not use screenshots unless the user explicitly asks.
- Do not register bundles directly in the standalone DB. Create test bundles only through `pnpm hot-updater deploy ...`.
- Use release binaries only for OTA validation. Do not use debug builds or Metro-attached runs for this skill.
- Rebuild the native app after native package changes. `pnpm -w build` alone is not enough for simulator/device validation.
- Treat `notifyAppReady` as read-only. It must not affect crash detection, rollback, or promotion.
- Public launch status should only be `STABLE` or `RECOVERED`.
- For crash-bundle scenarios, the crash must happen at module top level, outside
  the React component. Do not rely on `useEffect`, render-time component code,
  or UI-triggered throws for rollback validation.

## Fixed Targets

Read [references/runtime-targets.md](references/runtime-targets.md) before running anything.

## Prerequisites

Before running any caller-provided scenario:

1. Run `pnpm -w build`.
2. Confirm the standalone update server is running by checking `http://localhost:3007/hot-updater/version`.
3. Use the exact example workspace: `examples/v0.81.0`.
4. Use release artifacts only.
5. Choose one platform first. Finish that platform end-to-end before starting the other one.

## Command Templates

Use these as building blocks. Pick only what the caller's scenario needs.

Run these templates for one platform at a time. Do not keep iOS and Android sessions active in parallel.

### iOS Build And Install

```bash
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
```

### Android Build And Install

```bash
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
```

### OTA Deploy

Run deploy from `<repo-root>/examples/v0.81.0`.

```bash
pnpm hot-updater deploy -p ios -t 1.0.x
pnpm hot-updater deploy -p android -t 1.0.x
```

### Snapshot And State Inspection

```bash
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 UI is scrollable. Capture a top-of-screen snapshot first, then use
`agent-device scrollintoview "<section title>"` and re-snapshot as each target
section becomes visible. For most scenarios, inspect sections in this order:

1. `Runtime Snapshot`
2. `Launch Status`
3. `Crash History`
4. Optional deeper sections such as `Manifest Assets`, `Runtime Details`, and
   `Actions`

Typical section navigation examples:

```bash
agent-device snapshot -i
agent-device scrollintoview "Launch Status"
agent-device snapshot -i
agent-device scrollintoview "Crash History"
agent-device snapshot -i
```

## Assertions

Apply these assertions only when they match the caller's scenario:

- Only expect public status values `STABLE` or `RECOVERED`.
- Do not expect `PROMOTED`. Promotion is native-only and should not be surfaced to JS anymore.
- `reload` must not affect launch outcome state.
- `notifyAppReady` must be read-only.
- Prefer asserting both UI snapshot and bundle-store metadata when possible.
- For rollback checks, verify `crashedBundleId` and `crashed-history.json` together.

## Optional Crash Patch Pattern

If the caller explicitly asks for a crash bundle, use a temporary patch like this and revert it immediately after deploy:

```ts
const E2E_SAFE_BUNDLE_IDS = new Set([
  "<built-in-bundle-id>",
  "<current-stable-bundle-id>",
]);

const E2E_CURRENT_BUNDLE_ID = HotUpdater.getBundleId();

if (!E2E_SAFE_BUNDLE_IDS.has(E2E_CURRENT_BUNDLE_ID)) {
  throw new Error("hot-updater e2e crash bundle");
}
```

Place this patch at module scope, outside `App`, so the OTA bundle crashes
while the JS module is being evaluated. A crash patch inside `App`,
`useEffect`, or a button handler is not reliable enough for this rollback
scenario and may fail to produce the intended crashing bundle.

## Reporting

If the caller asks for a report, include:

- Platform
- Binary type used. This should always be `Release`.
- Relevant deployed bundle ids
- Final visible `status`
- Final visible `crashedBundleId`, if any
- Final metadata summary
- Crash history summary
- Whether the assertions came from UI snapshot, metadata, or both

## Notes

- In this skill, `<repo-root>` means the checked-out repository root.
- The example app already renders launch status and crash history in `examples/v0.81.0/App.tsx`.
- The iOS installed bundle id is `org.reactjs.native.example.HotUpdaterExample`, even though `hot-updater.config.ts` uses `com.hotupdaterexample` for build config.