Get an empty app all the way to TestFlight and Play's internal track before you write a single feature. Everything painful about mobile release — programme enrollment, identity verification, signing material, store records, review, the new-account testing window — has lead time that cannot be compressed by working harder, and every one of those problems is dramatically cheaper to hit against a blank shell than against a finished app. An agent that discovers them at submission time discovers them at the worst possible moment: under deadline, with a code change required, and with nothing left to cut.
This skill only sequences. Every mechanical step below belongs to another skill in this plugin — hand off by name and do not re-derive.
Confidence marks used throughout this skill and its references: [verified] — read off a primary
vendor source on the research date, or carried forward from a sibling skill in this plugin that
records the primary source and date. [snippet] — secondary source only, plausible but unconfirmed.
[unverified] — not confirmed anywhere; run the check given before relying on it. See Confidence
and freshness.
Process — eight phases, in order:
- Day one, before anything else. Start Apple Developer Program enrollment and create the Google
Play Console account, in parallel, on the same day. Choose the bundle identifier /
applicationIdonce. Register the bundle id and create both app records.references/accounts-and-enrollment.md,references/identifiers.md. - Signing, still before features. Upload keystore plus Play App Signing; Apple Distribution
certificate, provisioning profile, and an App Store Connect API key. Hand off to
android-build-signandios-build-sign. Back the material up before using it. - Prove the pipeline on a blank app. Build, sign, and upload a do-nothing shell to Play internal
testing and to TestFlight, and install it from both. Hand off to
play-store-publishandapp-store-publish. The definition of done is Phase gate: what "proven" means below —references/blank-app-proof.md. - CI from the first build, not retrofitted. Both platform skills ship a complete GitHub Actions
job:
../android-build-sign/references/github-actions.md,../ios-build-sign/references/github-actions.md,../play-store-publish/references/github-actions.md. Wire the secrets once —references/ci-and-secrets.md.
This is the boundary. Do not write feature code until phases 1–4 are done. A shell that reaches both stores through CI is worth more on day five than a working app is on day fifty, because the shell is what tells you which of the schedule risks in
references/accounts-and-enrollment.mdactually apply to this account.
- While building features. Look every platform question up rather than answering from memory —
apple-docsfor iOS,android-cli'sandroid docsfor Android. Take the decisions that are expensive to reverse deliberately and early: permissions requested, data collected, third-party SDKs, IAP versus external payment, minimum OS versions.references/expensive-decisions.md. - Run the review audits early and repeatedly.
app-store-reviewandplay-store-listingare not submission-day checklists — they surface findings that change your code (privacy manifests, required-reason API codes, purpose strings, restricted permissions, the Play Data safety form). Run both against the blank shell in week one, then on every milestone. Running them at the end forces a code change under time pressure. - Assets once the UI settles.
store-screenshotsandstore-preview-videos, automated from the first attempt so the set regenerates on demand rather than being hand-made once and going stale. - Submission and after. Staged rollout on Play, phased release on the App Store, what to watch,
and a rehearsed path to shipping a fix.
references/rollout-and-after.md.
Load only the reference a phase actually needs.
Contents
- Week one
- Phase gate: what "proven" means
- Cross-platform decisions to make once
- The biggest schedule risk
- Decisions that are expensive to reverse
- Skill map
- What this plugin does not cover
- Confidence and freshness
- Output Format
- References
Week one
Compressed schedule for a two-platform greenfield app. Days are working days and assume one person can do the account work while another does the shell.
| Day | Do | Why it cannot wait | Where |
|---|---|---|---|
| 1 | Start Apple Developer Program enrollment and create the Google Play Console account. Individual or organisation — decide now | Both gate on human verification you do not control. An organisation needs a D-U-N-S number, which is itself a lead time [snippet] |
references/accounts-and-enrollment.md |
| 1 | Choose the bundle identifier / applicationId. Write it in the README. Never change it |
It is the app's permanent identity in both stores and in every signing artifact | references/identifiers.md |
| 1–2 | Create the repo, a shell app that builds and launches on both platforms, and a .gitignore that blocks keystores, .p8, .mobileprovision, and service-account JSON |
The shell is the payload for the rest of the week | android-cli (android create), ios-build-sign |
| 2 | Register the bundle id in the Developer Portal; create the App Store Connect app record; create the Play Console app record | An upload is matched to an app by bundle id plus version [verified]. A missing record fails late and with a misleading error |
app-store-publish, play-store-publish |
| 2–3 | Create the upload keystore and enrol in Play App Signing. Create the Apple Distribution certificate, the distribution profile, and a team-scoped App Store Connect API key | Signing is the single largest source of multi-day stalls, and a lost app signing key on an app not enrolled in Play App Signing is unrecoverable [verified] |
android-build-sign, ios-build-sign |
| 2–3 | Back up both keystore passwords, the .p8, and the service-account JSON into a secret manager, in two places |
The .p8 downloads exactly once and Apple keeps no copy [verified] |
references/ci-and-secrets.md |
| 3–4 | Build, sign, and upload the do-nothing shell: Play internal testing and TestFlight internal group. Install from both | This is the proof. Everything before it is where the surprises live | references/blank-app-proof.md |
| 4–5 | Move both uploads into CI. Delete the local release build from your habits | Retrofitting CI onto a release process later means re-solving signing on a machine you cannot inspect | references/ci-and-secrets.md |
| 5 | If this is a new personal Play account: start the closed test and begin recruiting ≥ 12 real testers | The 14-day clock starts on tester opt-in, not on upload [snippet]. It is the longest pole in the schedule |
../play-store-listing/references/launch-timeline.md |
| 5 | Run app-store-review and play-store-listing against the shell. Fix nothing yet — record the findings as the project's compliance backlog |
Every finding that touches code is cheaper to design in than to bolt on | those two skills |
If day 3 slips because of something in phase 1 or 2, that is the schedule working correctly. It found the blocker in week one instead of week twelve.
Phase gate: what "proven" means
Phase 3 is complete when every line is true. Not "the build succeeded" — installed, from the store's own delivery channel, on a device that did not build it.
- Android: a signed
.aabbuilt from a clean checkout, with a certificate fingerprint quoted fromkeytool -printcert -jarfile, uploaded to theinternaltrack, edit committed - Android: the app installs from the Play internal-testing opt-in link on a physical device
and launches — not
adb install, notbundletool install-apks - Android:
mapping.txt(and native symbols if any) uploaded for thatversionCode - iOS: a signed
.ipawith itsAuthority=line quoted, uploaded, and finished processing in App Store Connect - iOS: the build appears in TestFlight and installs from the TestFlight app on a physical device and launches
- Both: version and build numbers came from a rule, not a person —
versionCodestrictly increasing,CURRENT_PROJECT_VERSIONstrictly higher than every build already uploaded for the marketing version[verified] - Both: the upload ran in CI, from a tag or a manual dispatch, with no secret on a laptop
- Both: a second upload at a bumped version succeeded without anyone touching a console
- Store records carry the final bundle id, and nobody has proposed changing it
The last line matters more than it looks. A bundle id that survives the first real upload has been validated by the only party whose opinion counts.
Detail, including how to produce the shell on each platform and what each failure at this gate
usually means: references/blank-app-proof.md.
Cross-platform decisions to make once
Things that differ between the stores in a way that forces a project-level decision. Decide each once, write it down, and stop re-litigating it per release.
| Decision | App Store | Google Play | Decide by |
|---|---|---|---|
| Account type | Individual or Organization. Organization needs a D-U-N-S [snippet] |
Personal or Organisation. Personal accounts created after Nov 2023 face the closed-testing requirement; organisation accounts are exempt [snippet] |
Day 1 — it is painful to change later and it changes the schedule |
| Account lead time | Enrollment plus identity/entity verification; days, sometimes longer [unverified] |
Registration plus identity verification; days [unverified] |
Day 1 |
| App identity | Bundle id, registered in the Developer Portal, explicit (non-wildcard) if the app has IAP or push [verified] |
applicationId in Gradle. Changing it creates a different app [snippet] |
Day 1, permanently |
| Artifact | .ipa, signed with an Apple Distribution identity |
.aab — mandatory for new apps since August 2021, and it requires Play App Signing [verified] |
Phase 2 |
| Key custody | You hold the distribution certificate and the .p8; Apple keeps no copy of the .p8 [verified] |
Google holds the app signing key under Play App Signing; you hold only the upload key, which is resettable [verified] |
Phase 2 |
| Versioning | MARKETING_VERSION (shown) + CURRENT_PROJECT_VERSION (build, must increase per marketing version) |
versionName (shown) + versionCode (integer, strictly increasing, never reusable — burned on any track, including deleted releases) [verified] |
Phase 3, as a script |
| Pre-release channel | TestFlight. Internal groups skip Beta App Review; external groups trigger it [verified] |
internal → alpha (closed) → beta (open) → production [verified] |
Phase 3 |
| First-release gate | Beta App Review for the first external build; full App Review for the first version | Play only accepts draft releases until the app has had a live release [verified]. New personal accounts additionally face closed testing |
Phase 1 — it sets the launch date |
| Rollout mechanic | Phased release for automatic updates to existing users; the version itself goes live in full | Staged rollout: status: inProgress plus a userFraction strictly between 0 and 1 [verified] |
Phase 8 |
| Halt / rollback | Remove from sale, or pause phased release; no rollback to a previous version | status: "halted", then resume or ship a higher versionCode. There is no un-shipping |
Phase 8, rehearsed before you need it |
| Review timeline | Hours to days for a routine update; longer for a first version [unverified] |
Hours to days routine; days to over a week for a first release; Google publishes no SLA [unverified] |
Phase 8 — never promise a date to a third party |
| Promo video | An app preview file you upload: 15–30 s, exact per-device upload resolution, footage captured from the app [verified] |
A YouTube URL you paste. There is no uploadable video asset [verified] |
Phase 7 |
| Screenshots | 6.9" iPhone required; 13" iPad required if the app runs on iPad; 1–10 per size per localization; no alpha [verified] |
Phone required; tablet sets needed for large-screen eligibility [snippet] |
Phase 7 |
| Privacy declaration | App Privacy answers + PrivacyInfo.xcprivacy + required-reason API codes + third-party SDK signatures |
Data safety form, and it must match what your SDKs actually do | Phase 5 — it constrains SDK choice |
| Payments | StoreKit IAP for digital goods, with narrow, entitlement-gated exceptions [unverified — volatile] |
Play Billing for digital goods, with its own narrow alternatives [unverified — volatile] |
Phase 5, before the paywall is built |
| Minimum OS | Deployment target; raising it later drops users, lowering it later multiplies test surface | minSdk; same tradeoff. Note the separate target API floor: from 2026-08-31, API 36 for phone/tablet [verified] |
Phase 5 |
Expanded rows, with what each one costs to change after launch:
references/cross-platform-decisions.md.
The biggest schedule risk
A new personal Google Play developer account may be required to run a closed test with at least 12 testers continuously opted in for 14 days before it can apply for production access.
[snippet] — corroborated across multiple secondary sources but not verified against a primary
Google source; Play Console Help article 14151465 was unreachable during this plugin's research. It
is recorded that way in ../play-store-listing/references/launch-timeline.md, which has the full
detail: the November 2023 effective date, the reduction from 20 testers to 12 in December 2024, the
fact that the clock starts on tester opt-in rather than upload, and that emulators and duplicate
accounts do not count.
Treat it as the default assumption for a personal account and plan around it, because:
- If it applies, it adds roughly three weeks minimum to the path to production, and no amount of engineering removes it.
- If it does not apply, you lose nothing — a closed test with real testers is what you wanted anyway.
- Organisation accounts are exempt
[snippet]. If the user has not registered yet and has a legitimate entity, raise this before they register. It is the single highest-leverage sentence in this skill.
The authoritative answer for a specific account is the Play Console Dashboard, which shows the requirement and live progress. Check there rather than reasoning about the policy, and never promise same-day publishing on a new personal account.
Decisions that are expensive to reverse
Raise all of these in phase 1 or 2 even though they are not needed until phase 5, because each one is cheap as a decision and expensive as a migration.
| Decision | What it costs to change later |
|---|---|
Bundle id / applicationId |
A new app record, a new store listing, zero installs carried over, and no upgrade path for existing users |
| Account type (personal vs organisation) | Re-registration, re-verification, and republishing under a new developer identity |
| Permissions you request | Restricted-permission declaration forms, demo videos, and possibly a narrower API rewrite. play-store-listing names the narrower alternative for each |
| Data you collect | Data safety and App Privacy answers, a privacy policy, a deletion path, and possibly PrivacyInfo.xcprivacy reason codes |
| Third-party SDKs | Apple's third-party SDK list requires a privacy manifest and a signature for named SDKs; a Data safety answer that contradicts an SDK is a top rejection cause |
| IAP vs external payment | The paywall, the server, the entitlement model, and the highest-risk rejection surface on both stores |
| Minimum OS version | Raising it drops installed users; lowering it multiplies the device matrix and the test bill |
| Account creation / login wall | Account deletion becomes mandatory on iOS if account creation exists (5.1.1(v)), and reviewers need a non-expiring demo account |
Full treatment, with the question to ask the user for each: references/expensive-decisions.md.
Skill map
Which skill owns each phase. Hand off by name; do not reimplement.
| Phase | Skill | Boundary |
|---|---|---|
| 1 | this skill | Accounts, identifiers, app records — nothing else here does them |
| 2 Android | android-build-sign |
Keystore, signingConfigs, Play App Signing, signed AAB, verification |
| 2 iOS | ios-build-sign |
Certificates, profiles, ASC API key, CI keychain, .ipa, verification. macOS + Xcode only |
| 3 Android | play-store-publish |
Service account, upload, tracks, rollout, mapping and symbols |
| 3 iOS | app-store-publish |
altool/Transporter/pilot, processing, TestFlight groups, review submission |
| 4 | references/ci-and-secrets.md, plus the github-actions.md reference of each of the three skills above |
Complete workflows already written; do not author a new one |
| 5 Android | android-cli |
Environment, android describe artifact discovery, emulators, android layout, android docs |
| 5 iOS | apple-docs |
Primary-source API, HIG, and policy lookup instead of recall |
| 6 iOS | app-store-review |
Purpose strings, privacy manifest, required-reason APIs, guidelines, review notes |
| 6 Android | play-store-listing |
Assets, metadata, Data safety, IARC, restricted permissions, launch timeline |
| 7 | store-screenshots, store-preview-videos |
Exact per-slot dimensions, deterministic capture, delivery paths |
| 8 | play-store-publish, app-store-publish |
Staged rollout, phased release, halt, promote |
What this plugin does not cover
Say this out loud early rather than letting the user discover it at phase 5.
- It is not a cross-platform framework guide, and it has no opinion on React Native versus Flutter
versus Kotlin Multiplatform versus two native codebases. Pick on team, hiring, and product
grounds. Every phase above applies unchanged either way — the store-facing work is identical, and
the only difference is which command produces the
.aaband the.ipa. - No architecture, state management, navigation, or UI guidance. Nothing here tells you how to build the app.
- No backend, API, auth server, or database guidance, beyond noting that a login wall obliges you to supply reviewers a non-expiring demo account.
- No analytics, crash reporting, feature flags, or A/B testing, beyond that adding such an SDK is a privacy-declaration decision (phase 5) and that shipping R8 mapping and dSYMs is what makes crash reports readable.
- No push notification, deep linking, or in-app purchase implementation. Their store consequences are covered — explicit bundle ids, entitlements, paywall review risk — the code is not.
- No marketing, ASO keyword research, pricing strategy, or localisation strategy. The listing
skills tell you what the fields are and what will be rejected, not what to write in them. The
marketplace lists third-party companion plugins for some of this — see
../../docs/companion-skills.md. - No macOS, watchOS, tvOS, or visionOS release paths. iOS and Android only.
Confidence and freshness
Research date for this skill: 2026-08-06.
This skill is a sequencing layer. It deliberately contains no new primary research: every
[verified] claim above is carried forward from a sibling skill in this plugin that names its own
primary source and research date (2026-08-05, or 2026-08-06 for apple-docs). When this skill and a
sibling disagree, the sibling wins — it is closer to the source.
What is genuinely weak here, and must be presented as weak:
- Enrollment and verification durations for both programmes are
[unverified]. They vary by country, entity type, and how the verification goes. Give the user a range and a dependency, never a date. - Fees are
[unverified]and deliberately not stated as numbers. Read them offdeveloper.apple.com/programsand the Play Console signup flow. - The D-U-N-S requirement for an Apple organisation enrollment is
[snippet]. - The 12-testers / 14-days rule is
[snippet]and gates the whole schedule. See The biggest schedule risk. - IAP and external-payment rules on both stores are the most volatile content in this plugin and
are marked
[unverified]. Resolve them at the moment you need them withapple-docs(the guidelines pages fetch as plain HTML) andandroid-cli'sandroid docs. - Review timelines on both stores are
[unverified]. Neither vendor publishes a commitment.
The week-one table is a plan, not a measurement. Nothing in it was executed — the authoring environment had no Android SDK, no Xcode, and no developer accounts.
Output Format
Open with the phase table so the user can see where they are and what is blocking. Then the critical path with the one thing that must start today, then the concrete next actions with the skill that owns each. Name the schedule risks explicitly and separate "we are waiting on a human" from "we have work to do". Never give a launch date — give a dependency chain.
Example output:
Ship plan — Tallyhoo · iOS + Android · greenfield
Target: both stores. Nothing exists yet: no repo, no accounts, no store records.
| Phase | State | Blocking |
|---|---|---|
| 1 Accounts and identity | NOT STARTED | ← start today, everything else queues behind it |
| 2 Signing | not started | Phase 1 (needs a team id and a Play account) |
| 3 Proven pipeline | not started | Phase 2 |
| 4 CI | not started | Phase 3 |
| 5 Feature work | do not start | the phase 1–4 boundary |
| 6 Review audits | not started | run against the shell in phase 3, not later |
| 7 Store assets | not started | UI does not exist |
| 8 Submission | not started | Phase 3 + the Play testing window |
Start today, in this order
-
Apple Developer Program enrollment. You mentioned Tallyhoo Ltd is a registered company — enrol as an Organization, not an Individual: the store listing then shows the company as seller, and it avoids a migration later. You will need a D-U-N-S number for the entity
[snippet]; if there is not one already, request it first, because it is its own lead time. Duration is[unverified]— plan on "days, possibly more", and treat the date as unknown until Apple confirms. -
Google Play Console account — and this is the decision that sets your launch date. Register as an organisation, not personal. A personal account created after November 2023 is reported to need a closed test with 12 testers continuously opted in for 14 days before it can even apply for production access, and organisation accounts are exempt
[snippet, see ../play-store-listing/references/launch-timeline.md]. If you register personally today, production moves out by roughly three weeks and no engineering decision brings it back. Confirm which applies to your account on the Play Console Dashboard, which shows the live requirement — do not reason from the policy text. -
Pick the bundle identifier now. Proposal:
com.tallyhoo.app— identical on both platforms (iOS bundle id and AndroidapplicationId). Confirm it and I will write it into the README as fixed. Changing it after the first upload means new app records on both stores and no upgrade path for installed users.
While that verification runs — parallel, no dependency
I can do all of this before either account is approved:
tallyhoo/
├── android/ # android create --name="Tallyhoo" -o ./android
├── ios/ # Xcode single-view shell, bundle id com.tallyhoo.app
├── .gitignore # *.jks *.keystore keystore.properties *.p8 *.mobileprovision *-key.json
└── .github/workflows/ # release-android.yml, release-ios.yml (secrets referenced, not yet set)
Both workflows come straight out of skills/android-build-sign/references/github-actions.md and
skills/ios-build-sign/references/github-actions.md. They will fail on missing secrets until phase 2 —
that is expected and it is why they exist now rather than in month three.
One hard constraint to flag: the iOS half needs macOS with Xcode. ios-build-sign has no
Linux path to a signed .ipa and neither does anything else. Either a macos-* GitHub Actions runner
or a Mac you control. Decide which before phase 2, because it changes how the signing secrets are
stored.
Definition of done for week one
Not "the app builds". This:
- Tallyhoo shell installed from the Play internal-testing opt-in link on a real phone
- Tallyhoo shell installed from TestFlight on a real iPhone
- Both uploads produced by CI, from a tag, with no secret on a laptop
app-store-reviewandplay-store-listingrun against the shell, findings recorded as a backlog
What I need from you
| Need | Why | Blocks |
|---|---|---|
Confirm com.tallyhoo.app |
Permanent app identity | everything |
| Organisation or individual/personal, both stores | Sets the launch date via the Play testing window | phase 1 |
| Whether a D-U-N-S exists for Tallyhoo Ltd | Apple organisation enrollment [snippet] |
phase 1 |
| A Mac or a macOS CI runner | No Linux path to a signed .ipa |
phase 2 iOS |
| Legal entity name and support URL | Both store records need them | phase 1 |
No launch date until Apple and Google have both confirmed their side. Once phase 3 passes, the remaining schedule is yours to control; until then it is not.
End of example.
References
references/accounts-and-enrollment.md— Apple Developer Program and Google Play Console registration side by side: individual vs organisation, D-U-N-S, identity and entity verification, what each account unlocks, roles to grant before you need them, the lead times that are genuinely unknown, and the new-personal-account closed-testing risk in planning terms.references/identifiers.md— choosing a bundle identifier /applicationIdonce: the rules, the reverse-DNS convention, wildcard vs explicit ids and what forces explicit, keeping the two platforms in sync, what is and is not changeable afterwards, and the app-record creation steps on both stores.references/blank-app-proof.md— how to produce a do-nothing shell on each platform, the full proven-pipeline checklist, what each failure at that gate usually means, and why installing from the store's own channel rather thanadb/bundletoolis the point.references/ci-and-secrets.md— the complete secret inventory for both platforms in one place, which sibling workflow to start from, what must never be printed, tag-driven release triggers, and the macOS-runner constraint.references/cross-platform-decisions.md— the expanded decision table: every row from the summary above plus what it costs to change after launch, and the store-by-store mechanics of rollout, halting, and shipping a fix.references/expensive-decisions.md— permissions, data collection, third-party SDKs, IAP vs external payment, minimum OS versions, and login walls: the question to ask, the cheap decision, and the expensive migration each one becomes.references/rollout-and-after.md— Play staged rollout and App Store phased release step by step, what to watch in the first 48 hours on each store, halting, and a rehearsed hotfix path.