Onboarding live apps without owner questions for what the stores and the code prove: storebound onboard imports the live products and subscriptions with USD / base prices (catalog diff clean), contact values, the App Review contact and demo account (credentials.json, masked), sign-in for review from Apple's review detail and auth code, and the account deletion page from the app's site; App Store keywords count characters (live fields up to 265 bytes accepted); validate accepts the 6.5-inch iPhone set and Play's default-language graphics fallback; Play Reporting API off gives the project's enable link and apps list falls back to local apps; accounts default <profile>; store-only apps (apps add --store --app-id) shown read-only in the desktop (pitfalls 43-52)

This commit is contained in:
CRTkafa 2026-10-13 07:25:01 +08:00
parent d2ce185441
commit e5f148e534
55 changed files with 1648 additions and 158 deletions

View file

@ -105,7 +105,7 @@ answers the compliance facts no file proves, all in one batch at the start.
- Screenshot sizes: 6.9" 1320 x 2868 (`APP_IPHONE_67`) is required; 6.5" 1284 x 2778 (`APP_IPHONE_65`) recommended.
PNG without alpha. Up to 10 per set. Apple processes uploads async; failures show as FAILED later.
- App Store icon (1024) must have **no alpha channel** (`storebound icons` writes rgb24).
- Limits: name 30, subtitle 30, promotional text 170, keywords 100 bytes (UTF-8, no spaces after commas),
- Limits: name 30, subtitle 30, promotional text 170, keywords 100 characters (not bytes; no spaces after commas),
description 4000. No "What's New" on a first version.
- Guideline 5.1.1: the privacy policy link must be inside the app too (Settings > Privacy), not only in the listing.
- `xcodebuild` with Capacitor needs a UTF-8 locale (`export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8`).

View file

@ -58,7 +58,7 @@ true; never use competitor or other apps' or games' names, "best / #1", or unrel
`APPLE_ADS_*` credentials are set (`ads apple status` works), Apple Ads keyword suggestions. For each: relevance
(in your own text +1, in competitor names +1), competition (top apps with the word in their name, "3/16"),
popularity (Apple Ads' relative 0-100 score; "unknown" otherwise). Show the owner the proposed field (old -> new,
bytes) and the left-out words with the reason. Only after a yes: `storebound aso suggest --locale en-US --write`
characters) and the left-out words with the reason. Only after a yes: `storebound aso suggest --locale en-US --write`
(terminal); it writes the field with a draft line above it. The owner reviews, deletes the line, runs
`check --aso`. Never put words in that do not describe the app, other apps' names or brands.
8. Review: `storebound aso audit` (MCP: `aso_audit`, offline) puts it together: keyword field findings, free keyword

View file

@ -23,7 +23,7 @@ Local files only: nothing is sent to a store until the owner pushes with `apple
Google Play title 30, short description 80, full description 4000. Shorten, never cut mid-word.
- **Keywords are researched, not translated:** the words people in that market search for, comma separated,
no spaces after commas, no duplicates, no words already in the name or subtitle (they are indexed anyway), no
competitor or trademark names, 100 characters (bytes for CJK) used well (aso-basics).
competitor or trademark names, 100 characters (characters in every script, not bytes) used well (aso-basics).
- Subtitle and short description: the benefit in plain words, no "best / #1 / free", no prices, no other platforms.
- Arabic and Hebrew stay RTL; Chinese / Japanese / Korean need no spaces in keywords.
4. Optional provider hook (the owner's choice): `--translate "<command>"` runs the command once per field with the

View file

@ -73,6 +73,10 @@ Before pushing local texts over a console edit, pull the live ones and read the
Both print the diff against `store/appstore.md` / `store/listing.md` and write only with `--write` (MCP:
`apple_listing_pull` / `play_listing_pull` with dry_run false). New locales are appended as sections; a Play section
shared by several codes is only changed when the store has the same text for all of them.
- An app that was live before storebound: `storebound onboard` (then `--write`) imports the live products and
subscriptions with their USD / base prices, contact values, the App Review contact and demo account, sign-in for
review and the account deletion page; then `storebound catalog diff` is clean. Ask the owner only the questions it
lists at the end.
- `storebound catalog pull [--write]`: IAP and subscription titles / descriptions (Play: benefits) against the config.
`storebound.config.ts` is never rewritten: `--write` saves `.storebound/pull/catalog.json` as a proposal; merge the
listings into the config yourself (the agent), then `storebound check`.

View file

@ -1,6 +1,6 @@
---
name: store-pitfalls
description: Known pitfalls with App Store Connect, Google Play and storebound itself, learned from real releases, and what to do about each - first: do the work, don't ask the owner for every step; store lists that lag right after a create, dry runs that print a count instead of a plan, App Store names taken on a storefront, keyword limits in bytes, Data safety answers without code evidence, missing font glyphs in screenshots, console sign-in and sessions, Capacitor monorepo plugins, duplicates from retries and re-runs, search ranks that are only a trend, no Play search API, Play's Create app form (no API, opener and submit share a name, Free is final), App Privacy purposes over-mapped (Play advertising is Third-Party Advertising only), an unknown console page, a signed-in console read as a sign-in page, console forms driven agent-first, stale Capacitor web assets, a declined flow step logged ok. Read it before any store task, and when a store command does something you did not expect.
description: Known pitfalls with App Store Connect, Google Play and storebound itself, learned from real releases, and what to do about each - first: do the work, don't ask the owner for every step; store lists that lag right after a create, dry runs that print a count instead of a plan, App Store names taken on a storefront, keyword limits in characters (not bytes), Data safety answers without code evidence, missing font glyphs in screenshots, console sign-in and sessions, Capacitor monorepo plugins, duplicates from retries and re-runs, search ranks that are only a trend, no Play search API, Play's Create app form (no API, opener and submit share a name, Free is final), App Privacy purposes over-mapped (Play advertising is Third-Party Advertising only), an unknown console page, a signed-in console read as a sign-in page, console forms driven agent-first, stale Capacitor web assets, a declined flow step logged ok. Read it before any store task, and when a store command does something you did not expect.
---
# Store pitfalls
@ -41,11 +41,8 @@ testers) and answers, in ONE batch at the start, only the facts no file proves (
keep `app.name` for Play and the brand. Do not pick the suffix yourself.
- **Guard:** `apple.name` overrides the App Store name.
## 4. Keyword field "too long" at fewer than 100 characters
- **Symptom:** App Store Connect rejects keywords that look short enough, mostly in Turkish, German, CJK.
- **Cause:** the 100 limit counts UTF-8 bytes, not characters ("ş" is 2 bytes, a CJK character 3).
- **Do:** shorten by bytes; drop words, not commas.
- **Guard:** `storebound check` counts bytes for keywords.
## 4. Keyword field length (superseded by 43)
- The limit is 100 characters, not UTF-8 bytes (see 43). Count characters; drop whole words, not commas.
## 5. Data safety says "email collected" for an app that never asks for one
- **Symptom:** the proposal marks email (or phone) as collected because the app has accounts.
@ -564,3 +561,68 @@ that fails without the fix (see CONTRIBUTING.md).
- **Guard:** `tests/asc-query-guard.test.ts` (every App Store Connect GET with a query in `cli/` against the spec and
`APPLE_LIVE_REJECTED`), the fake App Store Connect's 400 for such parameters, `unknownGroupMessage`;
`tests/idempotency.test.ts` (distribute), `tests/testflight.test.ts`.
## 43. Keyword field: 100 characters, not bytes
- **Symptom:** `check` / `listing check` / `check --aso` called live keyword fields too long ("105 bytes > 100") that App
Store Connect accepted.
- **Cause:** App Store Connect counts characters. Live READY_FOR_SALE versions (read 2026-10-13): Thai 95 characters /
265 bytes, Russian 91 / 169, Turkish 100 / 105, Polish 99 / 105. Entry 4 (bytes) was wrong.
- **Do:** count characters (`[...text].length`); fill up to 100 characters in any script.
- **Guard:** every keyword check counts characters (`cli/lib/copy.ts`, `aso.ts`, `listing-translate.ts`,
`aso-intel.ts`); tests with Thai, Turkish and Polish fields over 100 bytes.
## 44. "No 6.7-inch iPhone screenshots" on a live app with the 6.5-inch set
- **Symptom:** `validate` blocked every locale of a live app: "en-US: no APP_IPHONE_67 screenshots".
- **Cause:** App Store Connect requires the 6.9" set (APP_IPHONE_67) or, when it is missing, the 6.5" set
(APP_IPHONE_65), and scales the smaller sizes from it.
- **Do:** treat the 6.5" set as meeting the requirement; adding a 6.9" set is a recommendation.
- **Guard:** `ACCEPTED_INSTEAD` in `cli/lib/validate-rules.ts` (a warning `screenshots-size`, no blocker).
## 45. Play graphics missing in extra languages
- **Symptom:** `validate --play` listed an icon and a feature graphic blocker for every non-default language.
- **Cause:** Play shows the default language's graphics in a language that has none of its own.
- **Do:** only the default language's graphics are required; a language with none uses them (a note).
- **Guard:** `playRules` (`images-fallback` note per type).
## 46. Play Developer Reporting API off
- **Symptom:** `apps list --store play` and Play vitals failed with a 403 SERVICE_DISABLED.
- **Cause:** the Reporting API is not enabled in the service account's Google Cloud project.
- **Do:** open the link the finding prints (`…/playdeveloperreporting.googleapis.com?project=<id>`), press Enable,
wait a few minutes. Meanwhile `apps list` shows the Play apps of this machine's folders and store-only entries.
- **Guard:** `reportingEnableUrl`, `knownPlayPackages` (`cli/apps.ts`), `healthFindings`.
## 47. Onboarding a live app left its products out ("no USD prices")
- **Symptom:** after `init` + `listing pull` the config had no products although the app sells some.
- **Cause:** only texts were pulled, never prices; Apple's price schedule can have a base territory other than the USA.
- **Do:** `storebound onboard` (then `--write`): products and subscriptions from both stores with usd = the US price,
Apple's base territory as an override, one-store items marked with `platforms`. Then `storebound catalog diff`
(clean). Store prices are never changed; an item with no USD price anywhere is named with its base price.
- **Guard:** `importCatalog` (`cli/lib/onboard.ts`), `tests/onboard.test.ts`.
## 48. Contact e-mail, website and App Review contact asked of the owner
- **Symptom:** onboarding kept `support@example.com` and asked for the website and review contact.
- **Cause:** nothing read Play's app details or App Store Connect's review detail.
- **Do:** `storebound onboard` reads them; template values are replaced, real ones kept; the review contact goes to
`.storebound/credentials.json` (masked in output, the phone never printed).
- **Guard:** `contactProposal`, `reviewContactOf`, `maskContact`.
## 49. Sign-in for review defaulted to yes
- **Symptom:** every onboarded app got "sign-in required: add a demo account".
- **Do:** decide from Apple's review detail (`demoAccountRequired`; a demo account there is imported, never printed)
and from the code's auth signals; ask only when neither proves it.
- **Guard:** `signInDecision`.
## 50. Account deletion URL asked although the site has the page
- **Do:** `storebound onboard` tries /delete-account, /account/delete, /delete, /account-deletion and the privacy
policy's deletion links; not found is reported as the real gap it is (Play requires the page). Never invent one.
- **Guard:** `findDeletionUrl`.
## 51. No default credential profile except one named "default"
- **Do:** `storebound accounts default <profile>` (`--clear`). Order: env > the project's named profile > the
project's own keys > this default > the only profile.
- **Guard:** `defaultProfile`, `tests/machine.test.ts`.
## 52. A live app with no local project is missing from the desktop
- **Do:** `storebound apps add --store apple --app-id <id> --account <profile>`; the desktop shows it read-only
("Store only": status, reviews, analytics). `apps remove apple:<id>` takes it out.
- **Guard:** `storeApps` in `~/.storebound/projects.json` (`cli/lib/registry.ts`, `registry.rs`), the boot check.