merge origin/main (cross-store text checker); macOS platform pitfall renumbered to 79

This commit is contained in:
CRTkafa 2026-10-20 03:06:16 +07:00
commit 9db7180eb9
35 changed files with 618 additions and 60 deletions

View file

@ -25,6 +25,9 @@ true; never use competitor or other apps' or games' names, "best / #1", or unrel
- **Screenshot captions**: write them with the words people search for ("Budget planner with shared lists", "Match 3 puzzles offline", not "Fun awaits").
Claims that Apple reads caption text for search ranking are unverified; good captions help conversion either way.
- Changing keywords needs a new version (submitted with it); promotional text (170) changes any time but is not indexed.
- **No other platforms (2.3.10)**: no Android, Google Play (Games), Play Store, Galaxy Store, Amazon Appstore, Steam,
Microsoft Store, AppGallery (and localized forms such as "Google Play Spiele", "安卓") in any App Store field,
What's New included. Every App Store text write refuses them (store-pitfalls 77).
## Google Play
- **Indexed**: title (30), short description (80), full description (4000). There is no keyword field.
@ -33,6 +36,9 @@ true; never use competitor or other apps' or games' names, "best / #1", or unrel
- **No stuffing**: a word in more than ~3 % of the full description, keyword lists, ALL CAPS titles, emoji in the
title, or "best / top / #1" break the metadata policy. `check --aso` warns on these.
- Localize every listing you can; Play machine-translates otherwise, which ranks and converts worse.
- **No Apple platforms (Metadata policy)**: no iOS, iPhone, iPad, App Store, Apple Arcade, Game Center or TestFlight in
the title, descriptions or release notes; every Play text write refuses them. A real sign-in button name ("Sign in
with Apple") stays only through the owner's `listing.allowPlatformMentions` entry.
## Workflow
1. List 20-30 candidate terms with the owner: what people would type to find this app (the job it does, the problem it solves; for a game:

View file

@ -30,7 +30,7 @@ presses release; you prepare, dry-run and report. `storebound mcp` deliberately
`storebound apple listing --version 1.3.0 --build <that number>`. Never pick "the latest build": a CI or a
teammate may have uploaded another one meanwhile.
2. `storebound apple release --phased on --dry`, then without `--dry` (works while the version is being prepared).
With versions on more than one App Store platform add `--platform ios|macos|tvos|visionos` (pitfall 77).
With versions on more than one App Store platform add `--platform ios|macos|tvos|visionos` (pitfall 79).
3. Submit (owner, UI). Wait: `storebound apple status --wait-review` (polls with backoff, exit 0 approved, 1 rejected,
7 timeout; `--timeout 72h`). Rejected: `review-rejections` skill.
4. Approved = PENDING_DEVELOPER_RELEASE. Owner releases: `storebound apple release --manual-release` (asks to confirm)

View file

@ -55,7 +55,7 @@ in-app purchases). Resubmit that same submission; never create a second one next
3. `storebound apple resubmit --build <n>` (dry run without `--yes`): the plan table shows the build to attach, the
submission reused, every item in it already or added, which REJECTED items are marked resolved, and the submit.
An app with a macOS (tvOS, visionOS) version too: it picks the platform whose version was rejected, and asks for
`--platform ios|macos|...` when both were (pitfall 77).
`--platform ios|macos|...` when both were (pitfall 79).
4. The owner's yes, then `storebound apple resubmit --build <n> --yes`: build n is attached by its number, missing
items go in, the REJECTED version item is marked resolved (`PATCH reviewSubmissionItems/{id}` `resolved: true`),
then it submits. Apple answers every submit with "Version is not ready to be submitted yet" (and the console's
@ -89,6 +89,11 @@ Plain, polite, specific; one guideline per paragraph; no blame, no threats, no "
- **2.3 Accurate metadata**: screenshots must show the real app (for a game: real gameplay); no prices, no "free"
claims for paid items, no other platforms' UI; keywords without competitor names; age rating answers that match
the content.
- **2.3.10 Accurate Metadata (other platforms)**: "Android", "Google Play", "Google Play Games" or another store in
any App Store text, often a What's New copied from notes shared with Play (store-pitfalls 77). Fix: remove the
lines (tag shared notes `[play]` / `[apple]`, `storebound whats-new apply`), `storebound check` must show no
`other platform` blocker, push with `apple listing --version x.y.z`, reply "removed the references to other
platforms" and resubmit. Play's mirror case is the Metadata policy (iOS, App Store, Game Center in Play text).
- **2.3.1 Hidden or undocumented features** (Play: Deceptive behavior): remote config, feature flags or a remote
paywall the review notes did not mention (`storebound scan` rule `remote-features`, store-pitfalls 74). Fix: say in
the notes which SDK changes what remotely; a business-model change needs a new review and a description update.

View file

@ -36,8 +36,10 @@ storebound api apple salesReports_getCollection --param ... --out report.gz #
## Write (only the owner sends)
1. Run the write without `--yes`: it prints the exact request (method, URL, body; the key is never shown) and sends
nothing. Show that to the owner.
2. The owner runs it in their own terminal with `--yes`. Outside an interactive terminal, under MCP or in the desktop
app, a write is never sent, whatever the flags.
2. The owner runs it in their own terminal with `--yes`. Under MCP or in the desktop app a write is never sent,
whatever the flags. `--yes` with stdout not a TTY (piped `| tail`, redirected `> out.json`, an agent's shell) is
refused: exit 1 and "refused: writes through `storebound api` need an interactive terminal ... Nothing was sent."
on stderr (store-pitfalls 78). Never pipe or redirect a write; use the dedicated command the message names.
3. Owner-only operations (cli/api/gate.ts: every DELETE, submissions and their cancel, release requests, phased
release changes, product page test start / stop, price schedules and price migrations, review replies, tester and
user invitations, Play `edits.commit`, product activation, refunds, revokes and cancels, app signing) also ask for

View file

@ -763,7 +763,8 @@ that fails without the fix (see CONTRIBUTING.md).
bundleIdCapabilities answers, lag, a write that never sticks).
## 66. `storebound api apple ... --body f.json --yes` only dry-ran from an agent
- **Do:** expected: writes through `storebound api` are sent only from an interactive terminal, by design. Use the
- **Do:** expected: writes through `storebound api` are sent only from an interactive terminal, by design (since
pitfall 78 it is a loud refusal with exit 1, not a dry run). Use the
dedicated command the message names (bundle id capabilities: `apple signing ensure`; IAPs: `apple iap`; listing:
`apple listing`, ...), which runs anywhere with its own dry run and read-back.
- **Guard:** `dedicatedCommand` (cli/api/gate.ts), `tests/signing-capabilities.test.ts`.
@ -941,7 +942,30 @@ that fails without the fix (see CONTRIBUTING.md).
re-run, still processing, rejected / archived files, add-for-review without submitting, verify read-only with the
waiting state, a refused live placement, the validate finding).
## 77. A macOS (tvOS, visionOS) version was invisible next to the iOS one
## 77. What's New shared with Play rejected on the App Store (2.3.10)
- **Symptom:** a Mac App Store version was rejected under Guideline 2.3.10 (Accurate Metadata): its What's New came
from release notes shared with Google Play and had "Google Play Games" and "Android" lines. storebound wrote it
without a word (the old check was a warning in the editor only).
- **Cause:** one notes source for both stores, no per-store split, and no write that refused other platforms. Google
Play's Metadata policy is the mirror case (iOS, iPhone, App Store, Game Center, TestFlight in Play text).
- **Do:** tag store-specific lines `[play]` / `[apple]` in shared notes (whats-new skill); `storebound check` must
show no `other platform` line. A functional mention (a "Continue with Google" / "Sign in with Apple" button) is the
owner's call: `listing.allowPlatformMentions: [{ store, text, reason }]`. Never add an entry yourself.
- **Guard:** `cli/lib/cross-store.ts` (term lists, per locale / field / line, the allowlist, `notesForStore`).
Refused before anything is sent, dry runs included: `apple listing`, `play listing`, `play release` /
`play upload` (release notes), `listing set`, `whats-new check` / `apply`, the desktop editor and `storebound edit`
(`appleLocaleIssues` / `playLangIssues` errors). `check` / `validate` blockers (`other-platforms`), `scan` rules
`other-platforms` (2.3.10) and `play-other-platforms` (Play: Metadata). `tests/cross-store.test.ts`.
## 78. `storebound api ... --yes` piped or redirected became a silent dry run
- **Symptom:** `storebound api apple <write> --yes | tail` (or `> out.json`) printed a dry run and exited 0; the
owner thought it was sent. Happened twice.
- **Cause:** writes need an interactive terminal; without one the gate fell back to a dry run.
- **Do:** run writes without a pipe or redirect in a terminal, or use the dedicated storebound command.
- **Guard:** `decide` in `cli/api/gate.ts` refuses (`NOT_A_TTY`): exit 1, the message on stderr, nothing sent, the
dedicated command named. Without `--yes` a write is still a plain dry run. `tests/api.test.ts`.
## 79. A macOS (tvOS, visionOS) version was invisible next to the iOS one
- **Symptom:** `apple resubmit` said "nothing to resubmit" while a macOS version was REJECTED with an
UNRESOLVED_ISSUES submission; `apple listing --version 0.8.7` refused because the iOS 0.8.7 was live, although the
macOS 0.8.7 was the rejected one to fix; `apple status` listed versions without saying which platform.
@ -954,4 +978,3 @@ that fails without the fix (see CONTRIBUTING.md).
every version; validate and release status warn about another platform's editable or rejected version.
- **Guard:** `cli/apple/platform.ts` (`pickPlatform`, `parsePlatform`), `tests/apple-platforms.test.ts` (fake App Store
Connect: iOS 0.8.7 live, macOS 0.8.7 rejected in UNRESOLVED_ISSUES).

View file

@ -29,6 +29,20 @@ CI, PR, OTA, fingerprint, bump, deps, ...), version-control words (commit, merge
code identifiers (`saveSlot`, `max_lives`). Each warning names the word and a plain alternative. Rewrite the line;
when a flagged word is a real player-facing term (a character called "Sentry"), add it to `whatsNew.allowWords`.
## One source, two stores: tag store-specific lines
A notes source shared by both stores must not carry one store's lines into the other: a Mac App Store version was
rejected under Guideline 2.3.10 for a What's New with "Google Play Games" and "Android" lines (store-pitfalls 77).
- Tag a line for one store: `[play] Achievements on Google Play Games.` or `• [apple] Game Center leaderboards.`
(also `[android]` / `[ios]`). A line that is only `[play]` starts a block for that store up to the next blank line.
Untagged lines go to both stores; the tag itself is removed.
- The draft leaves an untagged line that names the other side's platform out of that store's draft and says so
(tag it to make it explicit). `whats-new check` and `apply` refuse notes that still name Android, Google Play,
Steam, ... in App Store text or iOS, iPhone, App Store, Game Center, TestFlight in Play text, with language, line
and term; `apple listing`, `play listing`, `play release` / `play upload` (release notes), `listing set` and the
desktop editor refuse the same, dry runs included.
- A functional mention (a "Continue with Google" or "Sign in with Apple" button) is the owner's call: an entry in
`listing.allowPlatformMentions` (`{ store, text, reason }`). Never add one yourself to get past the check.
## Write it for your users
1. Run `storebound whats-new --dry`, read the draft with the owner.
2. Rewrite:
@ -68,5 +82,5 @@ when a flagged word is a real player-facing term (a character called "Sentry"),
## Gotchas
- Squash-merged repos: the PR titles are the subjects; keep them in conventional form (`feat: ...`) to get clean drafts.
- Commits marked `[skip notes]` or `[internal]` are left out.
- Commits marked `[skip notes]` or `[internal]` are left out; `feat: [play] ...` subjects go only into the Play draft.
- Release notes are public: nothing about cheats, exploits, unreleased content or private names.