storebound/skills/store-preflight/SKILL.md

107 lines
10 KiB
Markdown

---
name: store-preflight
description: Run and read the storebound readiness checks before submitting a mobile app or game - `storebound scan` (offline rejection-risk scan of the project files, SDKs and listings with guideline, evidence and fix), `storebound check` (offline config, copy limits, files), `storebound check --aso`, `storebound validate` (read-only App Store Connect + Google Play report with ordered blockers) and `storebound privacy-manifest`. Use before every submission, when the user asks "is my app ready to submit", or after changing listings, builds, IAPs or SDKs.
---
# Store preflight
CLI: `storebound` (or the MCP tools `scan`, `check`, `check_aso`, `validate`, `privacy_manifest`). All are read-only on
the stores; run them freely.
## Order
0. `storebound scan`: reads the project itself (Xcode project, Info.plist, entitlements, privacy manifest, Podfile.lock /
Package.resolved, Gradle, AndroidManifest.xml, package.json / app.json / capacitor.config, pubspec, Unity and Godot
settings, Unity ProjectVersion.txt, CMake / ndk-build files, res/xml and strings.xml, and the merged manifest under
build/intermediates when the project was built locally) and the listing files. Prints the stack, the SDKs (version, file:line) and every rejection risk with
guideline, evidence, fix, severity (BLOCKER, HIGH, MEDIUM, low) and the skill to open; then the Data safety / App
Privacy / ATT / privacy manifest proposal, each item "detected (file:line)" or "ask owner", as a diff against the
config. Exit 1 on an open blocker. `--write` adds only the detected answers to the config (show the diff and ask
the owner first; MCP never writes). `--store apple|play`, `--json`. A finding the owner accepts goes into
`scan.ignore: [{ rule, reason, match?, evidence? }]` (`match` = text in the finding title, to accept one check of a rule with several; `evidence` = text in one evidence line, e.g. `src/main.ts:12`); never add one without the owner's reason. `scan` and `check` name an entry that matches no finding. The Play rules also read
`play.targetAges` (Families), `play.accountType` / `play.accountCreated` (closed-testing requirement) and
`play.billingPrograms` (payments): ask the owner for them, never guess.
1. `storebound check`: config, both listing files (limits are checked locally: title 30, subtitle 30, keywords 100
bytes, Play short 80, notes 500 ...), screenshot copy, Data safety answers, IAP texts (Apple description 45),
files the config points at, `.storebound/` gitignored, and the app profile: accounts need `accounts.deletionUrl`
(+ `inApp`), a sign-in for review needs the demo account in `.storebound/`, subscriptions need `app.termsUrl`.
Fix everything it lists; it exits 1 on problems.
2. `storebound check --aso`: keyword field use, repeats, stuffing (`aso-basics`). Errors exit 1, warnings are advice.
3. `storebound privacy-manifest --ios ios/App` (iOS): app manifest, required-reason APIs, SDKs without manifests
(`privacy-manifest` skill).
4. `storebound validate` (or `--apple` / `--play`): talks to both APIs read-only and prints findings in fix order,
plus the open scan findings (codes `scan-<rule>`; `--no-scan` skips). Exit 1 while there is a blocker. `--json`.
5. With in-app products or subscriptions: `storebound catalog diff` (read-only): both stores against the config,
prices per region, offers. Exit 1 on missing or mismatched items.
6. One list for the version you ship: `storebound release plan <version>` (MCP `release_plan`, read-only) puts the
check problems, the version's state on each store and the validate blockers in fix order, then names the owner's
next step (submit, promote, release). Report it as it is; the owner does the owner steps.
Many apps (a `storebound.workspace.json` above the app folders, `storebound ws init` writes it): `storebound ws check`
and `storebound ws validate [--apps a,b]` run the same checks over every app, a few at a time, each with its studio's
credential profile; `storebound ws run <command> --apps a,b` runs any other read command (writes only as dry runs).
## Share the report: `storebound report`
For the owner, a teammate or a client who should not need a terminal: `storebound report` runs `scan`, `check` and
`validate` (read-only; a store without credentials shows "Not checked" with the reason) and writes one self-contained
page, `.storebound/reports/readiness-<date>.html`, then opens it in the browser. A verdict per store, every finding
with its fix and console link, and the questions only the owner can answer. Secrets are masked. `--md` for Markdown,
`--json` for the data, `--no-open` in scripts. MCP: `report`; desktop: Checks > Report.
## Reading `scan`
| finding | fix | skill |
|---|---|---|
| 2.1(a) sign-in but no demo account | demo account in `.storebound/credentials.json`, `apple review-details` | `review-access` |
| 5.1.1(v) / Play account deletion | in-app delete + `accounts.inApp`, a web page in `accounts.deletionUrl` | `account-deletion` |
| 4.8 Google / Facebook login without Sign in with Apple | add Sign in with Apple (or an equivalent private login) | `review-rejections` |
| 3.1.1 no Restore Purchases / outside payment | a visible restore button; digital goods through IAP | `store-iap-setup` |
| 3.1.2(c) subscription info | `app.termsUrl`, Terms + renewal wording in the description, links in the app | `store-subscriptions` |
| 5.1.2(i) ATT | NSUserTrackingUsageDescription + the prompt before SDKs start, or no tracking | `ads-mediation-checks` |
| 5.1.1(ii) purpose strings | a specific sentence per used permission | `review-rejections` |
| privacy manifest (ITMS-91053 / 91061) | PrivacyInfo.xcprivacy with reasons; update old SDKs | `privacy-manifest` |
| 2.3.10 / 2.3.7 / 2.1(a) metadata | no other platforms, competitor names, prices or template text | `aso-basics` |
| export compliance | ask the owner | `export-compliance` |
| Play target SDK, foreground services, exact alarm, QUERY_ALL_PACKAGES, SMS / call log | manifest / Gradle changes + declarations | `play-first-release` |
| Play background location, photo / video, All files access, AccessibilityService, install packages, Health Connect, full-screen intent, VpnService | remove the permission (tools:node="remove" when an SDK adds it) or fill its App content declaration with a video | `store-declarations` |
| Play prominent disclosure / privacy policy in the app | a disclosure right before the prompt that names the data and its use; the policy linked inside the app | `store-declarations`, `privacy-policy` |
| Play Families / advertising ID | certified ad SDKs only, no AD_ID or location in child-only apps; AD_ID declared on targetSdk 33+ | `ads-mediation-checks`, `store-declarations` |
| Play 16 KB page sizes | newer Unity / React Native / NDK r28+ / AGP 8.5.1+, check prebuilt .so files | `play-first-release` |
| Play testing for new personal accounts | closed test, 12 testers for 14 days, then apply for production | `play-first-release` |
| Play metadata / impersonation | title 30, no emoji, caps, claims, keyword repetition, other brands | `aso-basics` |
| Play ads at launch / payments | interstitials only at natural breaks; digital goods through Play Billing or `play.billingPrograms` | `ads-mediation-checks`, `store-iap-setup` |
| privacy labels vs SDKs | `scan --write` after the owner agrees, then App Privacy / `play data-safety` | `store-declarations` |
## Reading `validate`
Each line: number, level (`BLOCKER`, `warn`, `ok`), store, message, and a `fix:` with the command or skill.
| finding | means | fix |
|---|---|---|
| no build attached / build PROCESSING | the version has no usable binary | wait for VALID, then `apple listing --version x --build N` (explicit number) |
| export compliance not answered | the build has no encryption answer | `export-compliance` skill |
| `<locale>`: missing description / keywords / subtitle | text not pushed for a locale | edit `store/appstore.md`, `apple listing` |
| no APP_IPHONE_67 screenshots | 6.9" set missing (required) | `apple screenshots`; iPad apps add the 13" type to `apple.screenshotTypes` |
| review contact missing | App Review information incomplete | `apple review-details` |
| age rating unanswered | new questions Apple added | `apple appinfo` |
| no app price schedule / availability | pricing never set | `apple appinfo`; if the API refuses the first availability record, set it once in App Store Connect > Pricing and Availability |
| IAP MISSING_METADATA / no review screenshot | product incomplete | `apple iap`, `apple iap-review` |
| privacy policy HTTP 404 / unreachable | URL broken | `privacy-policy` skill |
| subscription MISSING_METADATA / no review screenshot / no group localization | subscription incomplete | `apple subscriptions` (`store-subscriptions`) |
| Play subscription base plan DRAFT | not active | `play subscriptions` |
| accounts.deletionUrl not set / unreachable | Play needs a delete-account page | `account-deletion` skill |
| sign-in required but no demo account | App Review cannot get in (2.1) | `review-access` skill, `apple review-details` |
| no Terms of Use URL / description without a Terms link | subscriptions need the EULA link (3.1.2) | `store-subscriptions` skill |
| Play `<lang>`: phoneScreenshots 1/2 | graphics missing | `shots render`, `play images` |
| app-ads.txt lacks lines / 404 | ad networks cannot verify you | `ads-mediation-checks`; the file lives at the root of the developer website |
| no release on any track | nothing uploaded | `play release <aab>` |
| product purchase option DRAFT | not active | `play products` |
| user-perceived crash / ANR rate over 1.09 % / 0.47 % (live apps) | Play's bad behavior threshold is crossed | `store-health` skill: `storebound health`, fix the top issues, ship a higher versionCode |
| Apple metric rated poor / heavy diagnostic signature | hangs, launches, disk writes | `store-health` skill (Xcode Organizer for the stack) |
Vitals need the Play Developer Reporting API enabled for the service account; without it `validate` only notes
"vitals not readable". `--no-health` skips these checks.
## What validate cannot see (UI-only: check them yourself in the console with your browser tool)
App Privacy labels published, Play App content declarations (ads, target audience, content rating, advertising ID,
App access with the demo account), in-app account deletion working, first subscriptions added to the version,
agreements / tax / banking, countries for production on Play. (TestFlight groups and testers: `storebound apple
testflight status`.) Fix what you can; tell the owner only what is an owner step (agreements, tax, banking).