Decide, for every step of a mobile release, whether to do it or hand it to a person — and never guess which. This skill is the decision layer the other skills in this plugin execute under. They know how; this one knows whether you may.
Read this before the first action of any release task, not after something has gone wrong.
Contents
- The three classes
- The split, at a glance
- Human-only, honestly
- What full automation actually requires
- Idempotency and safety
- Degraded mode — no credentials at all
- Core Instructions
- Confidence and freshness
- Output Format
- References
The three classes
Every release step is exactly one of these. Classify before acting.
| Class | Meaning | Agent behaviour |
|---|---|---|
| AUTO | Runs with only the repo and local tooling. No store credential involved. | Do it. Report what changed. |
| AUTO+CRED | Automatable, but needs a specific named secret that must already exist and be valid. | Verify the credential first (references/credentials.md §3), then do it. If the credential is absent, do not improvise — say which secret is missing and stop. |
| HUMAN | A browser session, a legal identity, a payment, an accepted agreement, or a Console form with no API behind it. | Do not attempt. Prepare everything the person needs — the exact answers, the exact click path — and hand over. |
Two modifiers sit on top of the classes, and both override "I can, therefore I will":
- GATE — technically AUTO or AUTO+CRED, but irreversible or externally visible. Ask a human
first, every time, even mid-flow. Full list in
references/idempotency.md§2. - ONCE — consumes something non-reusable (a build number, a version code, a single-download
.p8). A retry is not free.references/idempotency.md§1.
The split, at a glance
Full step-by-step table with per-row sources: references/automation-matrix.md. The shape of it:
| Phase | AUTO | AUTO+CRED | HUMAN |
|---|---|---|---|
| Accounts and legal | — | — | Developer program enrolment, D-U-N-S, tax and banking, identity verification, every agreement |
| App records | Bundle-id choice, applicationId |
Apple bundle-id registration | Creating the App Store Connect app record; creating the Play app entry |
| Signing material | Keystore creation, signingConfigs, CI keychain script |
match/cert certificate and profile creation |
Play App Signing enrolment, upload-key reset |
| Version numbers | versionCode/versionName, MARKETING_VERSION |
Next build number from app_store_build_number |
— |
| Build | Whole build and sign leg, artifact verification | — | — |
| Assets | Screenshots, preview cuts, ffmpeg, alpha stripping | — | Poster-frame choice, YouTube upload for the Play promo video |
| Metadata | Drafting and writing every metadata file | deliver/supply metadata push |
— |
| Compliance forms | Producing the exact answer sheet | — | Play Data safety, IARC rating, ads, target audience, restricted-permission declarations, Apple App Privacy |
| Upload | Local validation | TestFlight and Play track uploads, mapping and symbol upload | — |
| Release | Rollout plan | Submit for review, staged rollout, promote | Pressing Publish under managed publishing, production access application, expedited-review request |
| Audits | Everything in app-store-review and play-store-listing that reads the project |
Anything that reads store state | — |
The single most useful sentence in this skill: the build and sign leg is almost entirely AUTO, the compliance-form leg is almost entirely HUMAN, and the upload leg is the only place credentials decide the outcome. Plan the work in that order and the human is interrupted once, not eleven times.
Human-only, honestly
These have no API this plugin could find, or they require a legal or financial act. Do not offer to
do them, do not write a script that pretends to, and do not report a release as blocked "on
automation" when it is blocked on one of these. Full list with sources: references/human-only.md.
- Apple Developer Program enrolment, the annual fee, and the D-U-N-S number for an
organisation.
[unverified]— reasoned from the absence of any enrolment API; no page fetched. - Agreements, Tax, and Banking in App Store Connect, and accepting any updated agreement. A
team key can read Sales and Finance reports
[plugin: app-store-publish/references/asc-api-key.md §2]; it cannot sign a contract or enter bank details. - Play Console account creation, the registration fee, and identity verification
[plugin: play-store-listing/references/launch-timeline.md]— the realistic schedule there starts at "Day 0: register the developer account; identity verification begins". - Creating the app record. Play is explicit: "The API cannot create an application. The app must
exist in Play Console"
[plugin: play-store-publish/references/service-account.md], and a 404No application was found for the given package nameis what you get for skipping it. On the Apple side the app record must already exist with the bundle id on it[plugin: app-store-publish/SKILL.md Prerequisites]; creating it is[unverified]as an API operation — treat it as HUMAN until proven otherwise. - Play App content forms: Data safety, IARC content rating, ads declaration, target audience and
content, plus the conditional News / financial / health / government / data-deletion forms. "Each
incomplete form blocks production publishing"
[plugin: play-store-listing/references/app-content.md]. - Restricted-permission declaration forms — "a human review, a justification, often a demo
video, and weeks of latency"
[plugin: play-store-listing/references/restricted-permissions.md]. Always propose the narrower API first; the agent's real job here is making the form unnecessary. - Apple App Privacy questionnaire. Answers are updatable any time without a new submission
[plugin: app-store-review/SKILL.md §3], but through the Console;[unverified]as an API operation. - Production access for a new personal Play account (the closed-test requirement,
[snippet]in[plugin: play-store-listing/references/launch-timeline.md]), and pressing Publish when managed publishing is on. - Visibility toggles — making a repository public, changing app availability or country targeting, moving a listing from unlisted to listed. A CLI exists for the repo case; that does not make it yours to flip. Disclosure is a human decision. GATE, always.
- The App Store preview poster frame — set in App Store Connect, and "changing the poster frame
after approval requires resubmission"
[plugin: store-preview-videos/SKILL.md §1].
What full automation actually requires
Six secrets, and nothing else. Names, sources, storage, and a verification command for each:
references/credentials.md.
| Secret | Where it comes from | Store as |
|---|---|---|
| Play service-account JSON | GCP IAM → Service Accounts → Keys → JSON, then invited in Play Console | SERVICE_ACCOUNT_JSON (raw JSON body) |
| Upload keystore | keytool -genkeypair, base64 of the .jks |
UPLOAD_KEYSTORE_BASE64 |
| Keystore passwords and alias | Chosen at keystore creation | UPLOAD_KEYSTORE_PASSWORD, UPLOAD_KEY_ALIAS, UPLOAD_KEY_PASSWORD |
| Apple distribution certificate + private key | Developer portal or match, exported as a .p12 |
DIST_CERT_P12_BASE64, DIST_CERT_P12_PASSWORD |
| Provisioning profile + team | match, or the portal |
PROVISIONING_PROFILE_BASE64, APPLE_TEAM_ID |
| App Store Connect API key | Users and Access → Integrations → +. The .p8 downloads once [plugin: app-store-publish/references/asc-api-key.md §1] |
ASC_KEY_P8_BASE64, ASC_KEY_ID, ASC_ISSUER_ID |
Three rules that cause more lost hours than the rest of this skill combined:
- Create the App Store Connect key as a team key, not an individual key. An individual key
cannot use the provisioning endpoints, Sales and Finance, or
notarytool, and it fails looking like bad credentials rather than like a scope problem[plugin: app-store-publish/references/asc-api-key.md §2]. - Base64 with no trailing newline.
base64 -w0 fileon GNU,base64 -i file | tr -d '\n'on macOS. A wrapped or newline-terminated secret decodes to a corrupt file and the error names the file, not the encoding. - Verify every credential before the run, not during it. Every downstream error is misleading
until authentication passes
[plugin: play-store-publish/SKILL.md Prerequisites]. The four one-line checks are inreferences/credentials.md§3, andmobile-release-doctorreports presence for all of them in one call.
mobile-release-doctor (this plugin's bin/, invokable as a bare command) enumerates host,
toolchain, project type, and credential presence. Presence is not validity — it cannot tell you
that a service account was never invited in Play Console. Run the doctor to find what is missing,
then the §3 checks to find what is broken.
Idempotency and safety
Detail and the full irreversible list: references/idempotency.md.
Safe to re-run, unchanged: every audit, every discovery command, screenshot and video
generation, metadata file writes, CI workflow generation, keystore creation (it refuses to
overwrite), validate_play_store_json_key, altool --validate-app.
Not idempotent, and the reason:
- Build numbers and version codes are consumed on upload and never reusable — including by a
rejected or deleted build
[plugin: ios-build-sign, play-store-publish troubleshooting tables]. A retry at the same number cannot succeed, so a blind retry wastes the number as well as the time. Re-derive fromapp_store_build_numberor from Play, not from a local counter. - A Play edit is transactional and short-lived. One edit cannot be committed twice. On failure, insert a fresh edit and redo the operations — never retry the old edit id.
versionCodeson aTrackReleasemust list the codes to retain. Omitting older codes de-lists them[plugin: play-store-publish/SKILL.md]. A re-run that forgets the retained list silently removes installable builds.inAppUpdatePrioritycannot be changed once the release is rolled out. Set it on the first write or accept 0 forever.
Irreversible — GATE on all of these, without exception:
a published production rollout (halting stops further delivery; it does not un-deliver) · a
submitted review · a consumed build number or version code · adding the first build to a TestFlight
group, which sends it to Beta App Review [plugin: app-store-publish/SKILL.md §2] · match nuke,
which revokes the team's certificates · deleting a .p8, which is single-download and unrecoverable
· keytool on an existing keystore path · a poster frame after approval · any visibility change.
When a GATE step is next, stop and print exactly: what you are about to do, what it costs if wrong, and what the one-line approval looks like. Then wait. "The user asked me to ship it" is not approval for the production rollout percentage.
Degraded mode — no credentials at all
This is the common case on a first session, and it is not a blocked session.
references/degraded-mode.md has the full ranked list. In one paragraph: run both audits
(app-store-review, play-store-listing) against the real project and produce the BLOCK/RISK
verdicts; generate the whole screenshot and preview-video set; write the fastlane config and the CI
workflows with the secret names referenced but unset; create the upload keystore locally; draft
every metadata file and the App Review notes; produce the Data safety and App Privacy answer sheets
for a human to type in; and run mobile-release-doctor to produce the exact
shopping list of what to go and get. That is most of a release, and none of it needs a secret.
Never fabricate a credential, write a placeholder that looks real, commit a secret to unblock yourself, or report a step as done when it was skipped for lack of access.
Core Instructions
- Classify before acting. If a step is not in
references/automation-matrix.md, treat it as HUMAN until you can name the API that does it. - Do the AUTO work first and all of it, then present one consolidated list of what the human must do. Interrupting per-step is the failure mode this skill exists to prevent.
- Name the missing secret. "I need credentials" is useless; "
SERVICE_ACCOUNT_JSONis unset, get it from GCP IAM and invite the address in Play Console → Users and permissions" is actionable. - Never print a
.p8, a.p12, a service-account JSON,MATCH_PASSWORD, or a keystore password into the transcript or a CI log — while debugging least of all. - State what was not done, every time, and why: HUMAN, missing credential, or GATE awaiting approval. Those three reasons are not interchangeable.
Confidence and freshness
Research date for this skill: 2026-08-06.
This skill is derived, not independently researched. It carries a two-layer mark that extends the plugin's convention rather than replacing it:
[plugin: <path>]— the classification is taken from that page in this plugin, and inherits whatever[verified]/[snippet]/[unverified]mark that page gave the underlying fact. Follow the link before quoting a number to a user.[unverified]— no source in this plugin and none fetched here. Reasoned, usually from the absence of an API. Every HUMAN classification resting on "there is no API for this" is[unverified]in exactly this sense: absence of evidence. If you find the API, the row is wrong — say so.
Nothing in this skill was executed against a real store account. mobile-release-doctor was
executed, on Linux, and its own output is the only tested thing here.
One naming note before you copy a workflow: the Android keystore secrets are spelled
UPLOAD_KEYSTORE_BASE64 / UPLOAD_KEYSTORE_PASSWORD / UPLOAD_KEY_ALIAS / UPLOAD_KEY_PASSWORD
throughout this plugin — skills/android-build-sign/references/github-actions.md and
skills/play-store-publish/references/github-actions.md now agree. The UPLOAD_* spelling is
deliberate: it says which key it is, the upload key rather than the app signing key Google holds.
That is this plugin's convention, not a platform requirement. In an existing repo, read the secret
names out of the workflow in front of you rather than out of any page, including this one — a
renamed secret is an outage.
Output Format
Lead with the classification, not the narration. One table of what you did, one list of what the human must do, one line on what is waiting for approval.
Example output:
Release plan — com.example.notes 2.7.0
Done unattended (AUTO):
| Step | Result |
|---|---|
| Version numbers | versionCode 143, versionName 2.7.0 — app/build.gradle.kts:31 |
| Release build | app-release.aab 18.4 MB, SHA-256 3A:1F:…:C9:2E (upload cert) |
| Mapping + symbols | collected, 1.2 MB / 44 MB, kept beside the artifact |
| Listing audit | play-store-listing — 1 FAIL, 2 WARN (below) |
| CI | .github/workflows/android-release.yml written; references 5 secrets, all unset |
Blocked on a credential (AUTO+CRED):
- Play upload needs
SERVICE_ACCOUNT_JSON. Not set, and noplay-store-key.jsonon disk. Create the key in GCP IAM, invite the…iam.gserviceaccount.comaddress in Play Console → Users and permissions with Release to testing tracks, then re-run. Grants take minutes to propagate.
Yours only (HUMAN) — 2 items, both blocking production:
- Data safety form omits the advertising ID; the build includes
play-services-ads:23.x. Declare Device or other IDs → Advertising ID under Collected and Shared. The full answer sheet is inrelease-notes/data-safety-answers.md— copy it into Play Console → App content. QUERY_ALL_PACKAGESis declared with no eligible category. I have written the<queries>replacement toapp/src/main/AndroidManifest.xmlas a comment; deleting the permission avoids the declaration form entirely. Confirm the change and I will apply it.
Awaiting approval (GATE): nothing. The rollout has not been planned yet — production is a separate decision and I have not touched a track.
End of example.
References
references/automation-matrix.md— the full step-by-step table for both stores: every release step, its class, the credential it needs, the skill that performs it, and the source for the classification.references/human-only.md— the honest human-only list in full: account and legal steps, every Console form, what the agent can prepare for each, and the click path to hand over.references/credentials.md— the six secrets, where each comes from, base64 and GitHub-secret storage, the local file locations each tool searches, and a one-line pre-flight verification per credential with what failure looks like.references/idempotency.md— what is safe to re-run, what is consumed, the full irreversible list, the GATE protocol, and how to recover from a half-finished release.references/degraded-mode.md— the ranked list of useful work with no credentials, and the four things never to do to unblock yourself.