plugins/development/skills/mobile-store-release/SKILL.md
Use when releasing an Expo/React Native mobile app to App Store and Google Play - covers eas submit, ASC "Submit for Review", Play promote Internal→Production, OTA update, and decoding common silent failures (Apple agreement expiry, missing English locale, Background Location declaration, web bundle failure on react-native-maps).
npx skillsauth add petrogurcak/skills mobile-store-releaseInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
End-to-end release of an Expo/React Native app: EAS build → store submit → store-side review submit → OTA update → post-deploy log. Most failures are silent — eas submit returns generic "Something went wrong" while the real cause is in expo.dev submission logs. ASC "Add for Review" silently fails when EN locale is missing. Web bundle for OTA blows up on native-only modules. This skill decodes those.
eas submit and stop).Before eas build/eas submit:
| Check | Command / location | Failure symptom |
|---|---|---|
| EAS production env vars set | eas env:list --environment production --include-sensitive | App ships with localhost API URL |
| Apple Developer agreements current | https://appstoreconnect.apple.com/business — look for "(New Agreement Available)" or yellow License Agreement banner | eas submit returns 403 FORBIDDEN.REQUIRED_AGREEMENTS_MISSING_OR_EXPIRED |
| Google Play Background Location declaration filled | Play Console → App Content → Sensitive permissions → Background location declaration | Play Promote dialog blocks with declaration error |
| Live store version vs build runtimeVersion | iTunes Lookup curl "https://itunes.apple.com/lookup?bundleId=<id>&country=cz" + Play Console production track | OTA reach mismatch (see Phase 5) |
If agreement banner present: just open https://developer.apple.com/account in a logged-in browser. Visiting auto-accepts in many cases (observed empirically). Re-check /business — if banner is gone, agreement is now active. ASC API may have a few-minute cache; first eas submit after acceptance can still 403, retry once.
cd mobile
eas build --platform ios --profile production --non-interactive
eas build --platform android --profile production --non-interactive
Run in parallel (two terminals or &). Capture build IDs from output for Phase 3.
eas submit --platform ios --id <build-id> --non-interactive
eas submit --platform android --id <build-id> --non-interactive
On failure: EAS CLI prints generic Something went wrong when submitting your app to Apple App Store Connect. Open the submission URL printed above the error (https://expo.dev/.../submissions/<id>), expand "Upload to App Store Connect" log step. Real HTTP error is there.
Common decodes:
| Log payload | Cause | Fix |
|---|---|---|
| 403 FORBIDDEN.REQUIRED_AGREEMENTS_MISSING_OR_EXPIRED | Apple agreement expired/updated | Phase 1 agreement check, retry |
| Invalid bundle: ...build number must be greater than ... | Build number not incremented in app.config.js | Bump ios.buildNumber and android.versionCode, rebuild |
| Asset validation failed - missing entitlement | Provisioning profile mismatch | eas credentials to refresh |
iOS post-submit verification: ASC TestFlight → Build Uploads should show the new build within ~10 min as Complete. If not visible after 15 min, EAS submit silently failed — check expo.dev submission detail (don't trust CLI exit code alone).
5 manual steps in Distribution:
1.1.6) → Create. Sidebar shows new entry as 🟡 Prepare for Submission.17) → Done.whatsNew is empty. Switch locale via dropdown above metadata (typical: Czech + English (U.S.)).1.1.6 (17). Click Submit for Review at bottom. Confirmation: 1 Item Submitted, up to 48 hours. Sidebar updates to 🟡 Waiting for Review.3 steps in Test and release → Internal testing:
/releases/N/prepare.Remove changes button = revoke option, confirming submission. Auto-rollout typically <24h post-approval.Background Location declaration (geofencing apps): if the manifest includes ACCESS_BACKGROUND_LOCATION (most apps using geofencing/auto-clock-in/region monitoring), Play requires a one-time Background Location Permission Declaration in App Content → Sensitive permissions. Without it, Promote Step 1 blocks with an error. Fill once per app — declaration persists across releases. Pre-flight check (Phase 1) catches this.
cd mobile
API_URL="https://api.<yourapp>.app" \
WEB_URL="https://<yourapp>.app" \
ENVIRONMENT="production" \
eas update --branch production --platform ios --message "<short message>"
# Then Android:
API_URL=... WEB_URL=... ENVIRONMENT=... \
eas update --branch production --platform android --message "<short message>"
Critical: per-platform separately, NOT --platform all. EAS bundles for all 3 platforms (iOS + Android + Web) when using all. If app.config.js imports any native-only module (react-native-maps, expo-secure-store, etc.) the web bundle fails with Importing native-only module ... on web and the entire OTA aborts. Per-platform avoids the web bundle.
Critical: eas update does NOT read EAS env vars. Must set them inline in the shell. Skipping this ships an OTA bundle with localhost URLs (broken app for users).
OTA reach mapping (runtimeVersion: { policy: "appVersion" }):
| Live store version | OTA target | Reach | |---|---|---| | 1.1.4 (old) | runtime 1.1.6 | TestFlight + 1.1.6 store users only — most prod users on 1.1.4 do NOT receive the OTA | | 1.1.6 | runtime 1.1.6 | All prod users |
Pre-OTA: check live store version via Phase 1 commands. If ≠ runtime, communicate to user that OTA reach is limited until store-side review completes.
Append to docs/release/YYYY-MM-DD-vX.Y.Z-N-handoff.md:
# Release v1.1.6 build 17 — 2026-05-06
## EAS
- iOS build: <build-id> (https://expo.dev/.../builds/<id>)
- Android build: <build-id>
## Store
- iOS: ASC Waiting for Review (submitted at <time>)
- Android: Play Sent for Review (submitted at <time>)
## OTA
- iOS update group: <id>
- Android update group: <id>
- runtimeVersion: 1.1.6 — reach limited to 1.1.6 store users until review completes
## Env vars (verified before build)
- API_URL: https://api.<yourapp>.app ✓
- WEB_URL: https://<yourapp>.app ✓
- ENVIRONMENT: production ✓
- SENTRY_DSN: configured ✓
## Outstanding
- (anything pending — store-side approvals, manual ASC steps, etc.)
| Symptom | Likely cause | Where to look |
|---|---|---|
| eas submit: "Something went wrong" | Generic — see actual log | expo.dev submission URL → expand log step |
| eas submit: 403 FORBIDDEN.REQUIRED_AGREEMENTS_MISSING_OR_EXPIRED | Apple agreement expired | ASC /business — Phase 1 |
| ASC "Add for Review" → "Unable to Add for Review" + locale error | Empty whatsNew in EN locale | ASC version → switch locale → fill |
| ASC "Add for Review" → red banner with no specific error | Build not associated with version | ASC version → Build section → Add Build |
| Play Promote Step 1 → "Background location declaration required" | App Content declaration missing | Play Console → App Content → Sensitive permissions |
| eas update --platform all → "Importing native-only module ... on web" | Web bundle + native module | Use --platform ios and --platform android separately |
| OTA published but users don't see fix | runtime mismatch with live store version | Phase 5 reach mapping table |
eas submit without checking ASC agreement banner state--platform allAPI_URL/WEB_URL/ENVIRONMENTAll five = will-fail-silently traps. Run the Phase 1 pre-flight check first.
development
Builds a pre-launch social proof strategy through structured beta programs using D'Souza Brain Audit interviews. Use when launching new products/services and need compelling testimonials, planning a beta cohort, designing interview questions to harvest objection-busting social proof, improving video testimonials for landing pages, or designing case studies with metrics. Trigger phrases include "beta tester program for testimonials", "pre-launch social proof", "Brain Audit testimonial framework", "case study harvest", "reverse testimonial", "video testimonial mechanics", "social proof landing page", "sběr referencí", "beta tester program", "testimonial pro landing page", "social proof před launchem", "rozhovor s klientem", "case study sběr", "reference před spuštěním". NOT for ongoing case study production (use growth-hacking case-study approach), offer design (use offer-creation), or conversion optimization (use ux-optimization).
development
Use when planning a product launch and the product type is unclear or could be either generic (SaaS/app/physical) or info-product. Routes between marketing:launch-strategy (generic launches) and marketing:info-product-launch (courses, memberships, ebooks, cohorts, communities). Trigger phrases - "launch", "spuštění", "go-to-market", "product launch", "release strategy", "uvedení na trh", "launch plan", "spuštění produktu", "launch sequence", "launch strategy". Do NOT trigger when product type is already clear (use specific skill directly).
testing
Specialized 8-week launch cadence for info-products — online courses, cohort programs, memberships, communities, ebooks, masterminds. Combines Jeff Walker's Product Launch Formula (Seed/Internal/JV variants, PLC sequence, open-cart day-by-day) with Stu McLaren's membership mechanics (closed cart, Success Path) and Hormozi Grand Slam Offer stacking. Use when planning "launch online kurzu", "info-product launch", "PLF launch", "course launch", "membership launch", "cohort launch", "ebook launch", "open cart close cart", "8-week launch of online course", "beta cohort to launch sequence", "spuštění kurzu", "launch členské sekce", "open cart strategie". Differentiates from marketing:launch-strategy (generic SaaS/app launches) — info-product-specific. NOT for SaaS launches, physical products, or services.
data-ai
Use when writing long-form video scripts that need retention engineering and narrative structure. Trigger phrases - "writing YouTube video script", "VSL (video sales letter)", "long-form video script", "course module video", "explainer video", "retention curve optimization", "video script for course", "scénář YouTube videa", "VSL skript", "long-form video", "scénář kurzového videa", "video script". Do NOT use for short-form TikTok/Reels under 60s — use copywriting:ig-content.