Skip to main content

mobile-store-submission

Why It Exists

Every one of this skill's nine invariants was extracted from a single real submission cycle — one app, submitted across iOS, tvOS, and Android, that hit two separate rejections before shipping (commit 26f7a25). It exists specifically because mobile-expo-app stops at "a signed build exists" and only briefly touches store-record creation; this skill picks up from there, covering what actually happens between having a build and having a released app.

Two of the nine invariants are worth calling out as the kind of thing that only a real rejection surfaces. The App Store Connect "Update Review" button looks like it resubmits a rejected version — it doesn't. It only unlocks the version into a locked "Ready for Review" state, and in that state fastlane's upload_to_app_store fails immediately with You cannot create a new version of the App in the current state, because the API genuinely cannot touch a client-side-locked version. The actual finish line is a separate button, on the reviewsubmissions detail page for that specific rejected submission — and the version-level status label reads identically whether or not that click has actually happened, so there's no way to infer completion from the version page alone. Separately, an App Store Connect subscription can show "Ready for Review" at the product level while Apple still rejects the whole app with "Subscriptions were labeled Unavailable" — because the subscription's parent group has its own, entirely separate localization requirement, and a missing group display name alone is enough to trigger the rejection even when nothing about the product's own configuration is wrong.

What It Does

Covers the submission-and-review side of both stores: bumping the native build number every single submission (Expo has no local auto-increment), satisfying Play Console's release_status:draft requirement for a brand-new app's first release, correctly localizing both an App Store Connect subscription and its parent group, working around the sandbox-tester requirement for testing draft in-app purchases, actually finishing an App Store resubmission after a rejection, budgeting for Play Console's mandatory 12-testers-for-14-days closed-testing gate on personal developer accounts, and deciding a bundle-ID/platform strategy correctly before it becomes unfixable.

How To Use It

Trigger phrases: "App Store Connect", "TestFlight", "fastlane ios release", "fastlane android release", "Play Console closed testing", "subscriptions labeled Unavailable", "release_status:draft", "versionCode already used", "12 testers 14 days", "sandbox tester account", "Resubmit to App Review", or submitting/resubmitting a mobile app for store review.

skills add git@github.com:catesandrew/next-starters.git --skill skills/mobile-store-submission -g
npm install @next-starters/skill-mobile-store-submission
/plugin marketplace add catesandrew/next-starters
/plugin install mobile-store-submission@next-starters

Gotchas & Invariants

  • Bump the native build number (app.config.ts's android.versionCode, and the iOS build number fastlane reads) before every submission — Expo does not auto-increment it, and a stale value gets rejected as "already used," not flagged as a reminder.
  • A brand-new Play Console app's first release must pass release_status:draft explicitly, or the upload API rejects a completed release with "Only releases with status draft may be created on draft app."
  • Draft or unreviewed in-app-purchase products are invisible to StoreKit under a developer's own regular signed-in Apple ID — this needs a genuine Sandbox Tester account (created in ASC, signed into the device via Settings → App Store → Sandbox Account), and Apple's own guidance confirms IAP doesn't need prior approval to function in sandbox review.
  • A companion platform (tvOS, for example) can only be added to an existing app record if it shares that record's bundle ID — two app records with different bundle IDs can never be merged after the fact, so the bundle-ID/platform decision has to be made before registering the second platform, not after.
  • Apple app-record creation cannot be scripted end-to-end under any circumstance — produce's create_app step needs an interactive Apple-ID login that no API key can substitute for.
  • A misleading "dimensions are wrong" error on an ASC screenshot upload can actually be caused by the PNG's alpha channel rather than its pixel dimensions — check sips -g hasAlpha before re-cropping anything.
  • mobile-expo-app — the build/EAS/push side this skill picks up from once a signed build exists.

Sourced from: skills/mobile-store-submission/metadata.json, skills/mobile-store-submission/SKILL.md, git commit 26f7a25