agent rules: first rule "Do it, don't ask" (agents-core and every adapter, README, store-pitfalls lead, first-release, declarations and console skills); the agent runs the real command after the dry run, the owner presses only final public or irreversible buttons and answers compliance facts in one batch; handoff text removed from skills; tests/do-it-dont-ask.test.ts. Pitfall 26

This commit is contained in:
CRTkafa 2026-10-12 09:28:45 +08:00
parent fc524cd2d4
commit dfc28c15f8
22 changed files with 393 additions and 105 deletions

View file

@ -29,7 +29,7 @@ other platforms' UI. Play promo video: YouTube URL, public or unlisted, ads off,
## Steps
1. **Hands-free footage.** Give the game an autoplay / attract mode switch, or the app a scripted demo of its main
flow on sample data (URL param, launch argument or intent extra), plus the screenshot switches (language, fixed
seed or demo data, no ads). Otherwise record the screen yourself and use `--source folder`.
seed or demo data, no ads). Otherwise record the screen (simulator, emulator or browser recording you start) and use `--source folder`.
2. **Clips.** `shots.video.record.clips`: per clip `seconds` (1-60) and `path` (web, after `shots.capture.baseUrl`),
or `url` / `args` / `extras` (simulator / emulator), `waitFor`, `delayMs`. The source and its settings come from
`shots.capture` (or `record.source`). `storebound shots record --dry` shows the plan, then
@ -48,7 +48,9 @@ other platforms' UI. Play promo video: YouTube URL, public or unlisted, ads off,
the page with their poster). Watch at least one language end to end.
5. **Upload.** App Store: `storebound apple previews --dry`, then `storebound apple previews` (editable version,
MD5-idempotent, poster frame from `--poster` / `shots.video.poster`, files checked before anything is sent).
Play: upload `preview/youtube/01.mp4` to YouTube yourself, set `play.video`, run `storebound play listing`
Play: the video must be on YouTube. Publishing on the owner's channel is public, so ask for it in the start batch
(or for an existing video URL); upload it with your browser tool once the owner said yes, set `play.video`, run
`storebound play listing`
(the listing PUT replaces the whole listing: without `play.video` a video set in Play Console is cleared).
## From an AI tool (MCP)

View file

@ -10,10 +10,12 @@ or `bun <toolchain>/cli/index.ts`; agents can also use the `storebound mcp` tool
Config: `storebound.config.ts`; credentials: `ASC_KEY_ID`, `ASC_ISSUER_ID`, `ASC_P8` (env or
`.storebound/credentials.json`, gitignored). All commands are idempotent and have `--dry`. Run `storebound check` first.
**Ask the owner before submitting for review, releasing, or canceling a submission.** The final "Submit for Review"
of a first version is the owner's click. Shared agent rules: `docs/agents-core.md` of the toolchain.
**Do it, don't ask** (first rule in AGENTS.md / `docs/agents-core.md` of the toolchain): you run every step below
yourself, API steps with storebound and UI steps in App Store Connect with your browser tool, and verify each. The
owner only presses Submit for Review, Release, App Privacy Publish, cancels a submission or accepts agreements, and
answers the compliance facts no file proves, all in one batch at the start.
## Steps (API = storebound, UI = App Store Connect / Xcode by hand)
## Steps (API = storebound, UI = App Store Connect, driven by you with your browser tool; Local = this machine)
1. **UI · API key.** App Store Connect > Users and Access > Integrations > App Store Connect API > Team Keys > +
(Admin or App Manager). Download `AuthKey_<KEYID>.p8` once, keep it in `~/.appstoreconnect/private_keys/`, note the
@ -49,21 +51,23 @@ of a first version is the owner's click. Shared agent rules: `docs/agents-core.m
description / keywords / promo / URLs). Run it with `--dry` first and show the owner the plan: the version decision
(keep, update the editable version's string, create, or refuse with the reason), the build, and per locale create /
update with old -> new per field. Locales still marked `status=draft` / `status=translate` are skipped until
`listing reviewed` (`--include-drafts` only when the owner says so). The real run asks in a terminal and applies
exactly that plan. Then `storebound apple release --phased on`: manual release + phased
`listing reviewed` (`--include-drafts` only when the owner says so). Then run it yourself with `--yes`: it
applies exactly that plan. Then `storebound apple release --phased on`: manual release + phased
release is the default for every version (`release-management`).
8. **API · Screenshots.** `store-screenshots` skill, then
`storebound apple screenshots > .storebound/asc-shots.log 2>&1 &` (6.9" + 6.5" per locale, MD5-idempotent).
9. **API · App info.** `storebound apple appinfo` (`--dry` first: old -> new per field): categories, age rating declaration, content rights, Free price,
all territories. The **first** availability record of a new app may be refused by the public API (an error on
POST /v2/appAvailabilities); then set it once in App Store Connect > Pricing and Availability and re-run (later runs only read it).
POST /v2/appAvailabilities); then set it once in App Store Connect > Pricing and Availability with your browser
tool and re-run (later runs only read it).
10. **API · Review details.** `storebound apple review-details` (contact from `.storebound/credentials.json`
`reviewContact`, or `--contact-from <Apple ID of another app of the team>`; notes from the config). Apps behind a
sign-in: the demo account from `.storebound/` (`review-access` skill). Apps with accounts: in-app deletion
(`account-deletion` skill).
11. **API + UI · In-app purchases and subscriptions.** `store-iap-setup` and `store-subscriptions` skills. First
IAPs and subscriptions go to review **with** this version.
12. **UI · App Privacy.** `store-declarations` skill (nutrition labels), then **Publish**.
12. **UI · App Privacy.** `store-declarations` skill (nutrition labels; you fill and save them), then the owner
presses **Publish**.
13. **Preflight.** `storebound validate --apple` until it shows no blocker (`store-preflight`).
14. **UI · Submit.** Version page > Add for Review (with the IAPs in the same draft submission) > Submit for Review
(owner). Release type MANUAL means the owner presses "Release" after approval.

View file

@ -72,7 +72,8 @@ true; never use competitor or other apps' or games' names, "best / #1", or unrel
owner's step.
- A subtitle change ships with a version: change one thing, then compare with `analytics compare`.
- Google Play store listing experiments (short / full description, icon, feature graphic, screenshots) have no API:
storebound prepares the text and renders; the owner sets the experiment up in Play Console.
storebound prepares the text and renders, you set the experiment up in Play Console with your browser tool, and
the owner presses the button that starts it.
## Where the numbers come from (say it to the owner)
| number | source | is | is not |

View file

@ -7,6 +7,10 @@ description: Fill App Store Connect and Google Play Console forms that have no A
CLI: `storebound` (or `bunx storebound` in a project that has it, or `bun <toolchain>/cli/index.ts`).
**Do it, don't ask** (first rule in AGENTS.md / `docs/agents-core.md`): you open the console, navigate, fill the form,
save drafts and verify; the owner only signs in once and presses the final button. Ask the owner's `ask-owner`
answers in one batch before you start, never one at a time on the page.
**Read this to the owner first.** Console pages change without notice. You (the agent) fill the form from storebound's
answer sheet and the owner reviews every field and presses the final button. Nobody types a password or touches a
sign-in page except the owner; nothing that submits, publishes, creates, accepts an agreement or grants a permission
@ -27,7 +31,8 @@ the UI shows. This is the primary mode.
- `buttons.neverPress` (Publish, Submit, Create, Create app, Send, Release, "Definitions and Examples", "?", the
final Save of a form ...), `buttons.final` and `buttons.allowed` (navigation and draft saves);
- `uiFacts` (verified facts per form, see below), `driving` and `verify` (the command and what to capture).
2. Show the owner the `ask-owner` answers and ask (AGENTS.md rule 5). Never guess them; leave them empty if the owner
2. Collect the `ask-owner` answers of every form you will fill in ONE question to the owner before you start (AGENTS.md
rule 5) and write them into the config, so the plan has them. Never guess them; leave them empty if the owner
wants to answer in the page.
3. Open `start.url` in your browser tool. If a sign-in page shows, the owner signs in; you wait.
4. Follow `steps`. Read the page (text or accessibility tree) before each action, click by visible text, set each
@ -68,15 +73,15 @@ the UI shows. This is the primary mode.
left. Text inputs set by `form_input` are fine. After Create app the app lives at
`/console/u/0/developers/<developerId>/app/<appId>/app-dashboard`: put the id in `play.appId`.
- **After Play Create app, an owner step:** a service account's app-level permissions do NOT cover apps created
later. The owner opens Users and permissions > the service account > Add app > the new app > Admin (or the minimal
set the plan lists) > Apply > Save changes, then `storebound apps check --store play`. Never do it yourself (a
permission grant; auto mode blocks it too).
later. Granting them is the owner's (a permission grant; auto mode blocks it too): give the exact path together
with the Create app button, Users and permissions > the service account > Add app > the new app > Admin (or the
minimal set the plan lists) > Apply > Save changes; then run `storebound apps check --store play` yourself.
## Optional: the scripted fast path (`storebound console fill`)
`console fill` drives a visible browser by itself with the same answers and click rules. It is fast when the page
matches its model; when it stalls on a changed page, switch to the agent-first mode above instead of waiting for a
fix. It is not available over MCP (the owner runs it in a terminal or the desktop app).
fix. It is not available over MCP: run it from your shell (or the owner uses the desktop app).
## Forms
@ -93,7 +98,7 @@ fix. It is not available over MCP (the owner runs it in a terminal or the deskto
1. `storebound console fill` lists the forms. `storebound console fill <form> --dry` prints every answer with its
source; `[check]` lines are not proven by the config. Show them to the owner.
2. For each `[check]` line, ask the owner (AGENTS.md rule 5: never guess a compliance answer). If they want it filled
2. Ask the owner about every `[check]` line in one batch before the run (AGENTS.md rule 5: never guess a compliance answer). If they want it filled
next time, add it to the config, for example:
```ts
console: {
@ -103,8 +108,8 @@ fix. It is not available over MCP (the owner runs it in a terminal or the deskto
},
```
Age rating answers live in `apple.info.ageRating` (also `socialMedia`, `socialMediaAgeRestricted`).
3. The owner runs `storebound console fill <form>` in a terminal (or presses the button in the desktop app). The
first log line says which browser it uses:
3. Run `storebound console fill <form>` yourself from a shell (the desktop app has a button for it too). The first
log line says which browser it uses:
- **profile** (the default): storebound's own profile (`~/.storebound/console-profile/<store>`, or `--profile <dir>`
/ `STOREBOUND_CONSOLE_PROFILE`; one per store, not per account: every studio shares the one login). Nothing is
read or copied from the owner's own Chrome. storebound starts Chrome itself as a normal window (its own
@ -158,8 +163,9 @@ fix. It is not available over MCP (the owner runs it in a terminal or the deskto
Save, Submit, Publish, Create, Send, Next or the Turkish equivalents; only a LINK named exactly Start / Manage /
Başla / Yönet is let through. If a step is not found in time, or two apps match (they are listed: set
`apple.appId` / `play.packageName`), storebound says which step failed, prints what to click, and keeps watching:
a sign-in that finishes restarts navigation at step 1; when the owner moves on by hand (the + button, the New App
item, the app in the list appear) it continues from that step; when the owner opens the form it fills it. Watching
a sign-in that finishes restarts navigation at step 1; when the page moves on (you click the step with your
browser tool: the + button, the New App item, the app in the list appear) it continues from that step; when the
form is open it fills it. Watching
ends when the window is closed. When the form appears storebound fills the certain answers, outlines every
unproven or unfillable field with a "storebound: check this" label, and lists them in the terminal.
5. **One login, many developer accounts or teams.** storebound never asks for a second sign-in; it finds the
@ -204,7 +210,8 @@ fix. It is not available over MCP (the owner runs it in a terminal or the deskto
step: attach that folder to a bug report after looking at screen.jpg (labels only; no values, cookies or tokens;
emails, phone numbers and the account area are replaced).
9. If a field cannot be found or matches twice (for more than a moment: a page that is still loading does not count), the console UI changed: storebound stops filling and prints the
step-by-step list ("label: answer") for the owner to copy. That is the expected safe behavior, not an error.
step-by-step list ("label: answer"). That is the expected safe behavior, not an error: finish that part yourself
with your browser tool (agent-first mode above), not by handing the list to the owner.
## Play Create app (`play-app-record`)
@ -225,7 +232,8 @@ fix. It is not available over MCP (the owner runs it in a terminal or the deskto
"Check availability" ("Package name available"). It is fixed for good once the app is created.
- App or game and Free or paid are `<material-radio>`s: storebound clicks the inner `[role=radio]` (the native input
does not update the form's model). With `play.developerId` the form opens by its own URL (`.../create-new-app`).
- After Create app: the owner adds the new app to the service account (see "Verified UI facts" above).
- After Create app: the owner grants the service account access to the new app (a permission grant; see "Verified UI
facts" above), then you run `storebound apps check --store play`.
## What storebound clicks, and what stays with the owner
@ -242,8 +250,8 @@ content", "Create and set up your app"):
| Content rating (Play) | Start; **Save draft** then Next on each page (a draft: IARC gets nothing before Submit; the owner agreed) | **Submit** on the Summary: sends the questionnaire to IARC |
| Target audience (Play) | Start / Manage, Next between the pages | **Save** on the last page: stores the declaration, which Google then reviews |
Mid-way storebound stops only for an uncertain answer (see 6). It never asks the owner to click a navigation button
unless the console page changed so that storebound cannot find it (then it says so).
Mid-way storebound stops only for an uncertain answer (see 6). A navigation button is never the owner's: when the
console page changed so that storebound cannot find it, it says so and you click it with your browser tool.
## The owner decides

View file

@ -10,10 +10,12 @@ or `bun <toolchain>/cli/index.ts`; agents can also use the `storebound mcp` tool
It reads `storebound.config.ts` in the project (create with `storebound init`) and credentials from env or
`.storebound/credentials.json` (gitignored). Every write command has `--dry`. Run `storebound check` first.
**Ask the owner before anything public or irreversible**: production releases, "Send for review", publishing,
deleting products. Sending a first release for review is the owner's action in the Console, not yours.
**Do it, don't ask** (first rule in AGENTS.md / `docs/agents-core.md`): you run every step below yourself, API steps
with storebound and UI steps in Play Console with your browser tool, and verify each. The owner only presses the
public or irreversible buttons (Send changes for review, production release or rollout, agreements, deleting
products) and answers the compliance facts no file proves, all in one batch at the start.
## Steps (API = storebound does it, UI = Play Console by hand)
## Steps (API = storebound, UI = Play Console, driven by you with your browser tool)
1. **UI · Create the app.** `storebound console plan play-app-record` (then drive the form with your browser tool,
or the scripted `storebound console fill play-app-record`). The form is
@ -25,10 +27,11 @@ deleting products. Sending a first release for review is the owner's action in t
`/console/u/0/developers/<developerId>/app/<appId>/app-dashboard`: put that app id in `play.appId` (the App
content forms deep-link from it).
**Owner step after every Create app:** a service account's app-level permissions do NOT cover apps created later.
The owner opens Users and permissions > the service account > Add app > the new app > Admin (or at least: view app
information, manage store presence, release to testing tracks, manage testing tracks, manage production releases,
manage policy declarations, manage orders and subscriptions) > Apply > Save changes. Never do this yourself: it is
a permission grant (agents in auto mode are blocked from it too). Then `storebound apps check --store play`.
Granting them is the owner's (a permission grant; agents in auto mode are blocked from it too): give the exact
path in the same message as the Create app button, Users and permissions > the service account > Add app > the new
app > Admin (or at least: view app information, manage store presence, release to testing tracks, manage testing
tracks, manage production releases, manage policy declarations, manage orders and subscriptions) > Apply > Save
changes. Then run `storebound apps check --store play` yourself.
2. **UI · API access.** Google Cloud project (any) > enable "Google Play Android Developer API" > create a service
account > JSON key (keep it outside the repo). Play Console > Users and permissions > Invite new users > the
service account email > app permissions: view app information, manage store presence, release to testing tracks,
@ -52,7 +55,7 @@ deleting products. Sending a first release for review is the owner's action in t
6. **API · Listing text.** Fill `store/listing.md` (one section per language), then `storebound play listing`.
It also sets contact email + website (website = the domain serving app-ads.txt). Run `--dry` first and show the
owner the plan (details and per language old -> new per field; draft languages skipped until `listing reviewed`,
`--include-drafts` only on the owner's word); the real run asks in a terminal and PUTs only what changes.
`--include-drafts` only on the owner's word); then run it yourself with `--yes`: it PUTs only what changes.
7. **API · Graphics.** Render (`store-screenshots` skill), `storebound shots jpeg`, then
`storebound play images > .storebound/play-images.log 2>&1 &` and follow the log (it is slow: one edit per language).
Required: the 512 x 512 icon, the 1024 x 500 feature graphic and at least 2 phone screenshots (Play Console Help
@ -68,7 +71,8 @@ deleting products. Sending a first release for review is the owner's action in t
with the demo account (`review-access`); with accounts: the delete-account URL (`account-deletion`); subscriptions:
`storebound play subscriptions` (`store-subscriptions`).
11. **UI · Store settings.** Category + tags, contact details.
12. **API + UI · Production.** `storebound play release --version-code N --production draft`, then in the Console:
12. **API + UI · Production.** `storebound play release --version-code N --production draft`, then in the Console
(you, with your browser tool):
Production > Countries/regions > Add countries > select all > Save; Production > the draft release > Edit >
Next > Save; Publishing overview > **Send changes for review** (owner).
13. **Verify.** `storebound validate --play` (no blockers), `storebound play status` (tracks, listings, products), Console dashboard has no open tasks,

View file

@ -6,7 +6,8 @@ description: Repo-local workflows for a mobile app or game - named multi-step fl
# Release flows
CLI: `storebound flow list | validate | run <name> [NAME=value ...] [--dry | --apply] [--from <step> | --resume]`.
MCP: `flow_list`, `flow_run` (always the safe mode: writes as dry runs; `--apply` is a terminal command the owner runs).
MCP: `flow_list`, `flow_run` (always the safe mode: writes as dry runs; `--apply` is a CLI command: run it from your
shell after the dry run).
## Where flows live
- `store/workflows/<name>.yml` (one flow per file, named after the file; `.yaml`, `.json` and `.ts` work too), or
@ -45,11 +46,11 @@ once (the second build step prints "prebuild skipped (already ran in this flow r
run for real; **every write step runs with `--dry`**; wait steps check once; shell steps are skipped. Nothing
changes. Show this output to the owner.
- `--dry`: the plan only, nothing runs.
- `--apply`: writes and shell steps run for real, **only after the owner agreed in chat**; each write command still
asks its own confirmation in the terminal. Never add `--yes` to a step (flow validate refuses it).
- `--apply --yes`: the owner's approval of every write step of this run (they get `--yes`; reads, waits and owner
steps do not). Only when the owner said so in chat for this run, e.g. a background run with a log where no terminal
can answer. Without `--yes` and without a terminal, a confirming command declines: the flow stops there with
- `--apply`: writes and shell steps run for real; each write command still asks its own confirmation in the
terminal. Never add `--yes` to a step (flow validate refuses it).
- `--apply --yes`: how you run it after showing the dry run (do it, don't ask: AGENTS.md first rule): every write
step gets `--yes`; reads, waits and owner steps do not, so nothing owner-only runs. When the dry run shows a live price change
(`play products`, `apple subscriptions` on an existing product), get the owner's yes for it first. Without `--yes` and without a terminal, a confirming command declines: the flow stops there with
"step N declined: not a terminal and no --yes ...", never "ok" (status `declined` in `--json` and the state file).
- Owner steps never run in any mode: submit, release, phased release, rollout, sending review replies, price
imports, inviting testers, Beta App Review, starting experiments, offer codes (AGENTS.md rule 1). They print as

View file

@ -71,4 +71,4 @@ Before pushing local texts over a console edit, pull the live ones and read the
shared by several codes is only changed when the store has the same text for all of them.
- `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 by hand or with your agent, then `storebound check`.
listings into the config yourself (the agent), then `storebound check`.

View file

@ -28,7 +28,7 @@ keyword repetition, other apps), interstitials at launch, payments outside Play
a rejection, run it too: the same cause often hides elsewhere. A finding the owner decided is fine goes
into `scan.ignore` with the reason (never silence one yourself).
Checked at review, not by scan (look at these by hand): crashes and hangs on the reviewer's device (2.1; `store-health`),
Checked at review, not by scan (check these yourself in the app and the listing): crashes and hangs on the reviewer's device (2.1; `store-health`),
links that are dead at review time (`storebound validate` checks the configured URLs), screenshots and description
that do not match the app (2.3, `store-screenshots`), poor or unfinished UI (4.0), "too simple" and spam / template
apps (4.2, 4.3; `portfolio-originality`), IAPs that fail in the sandbox (2.1(b), `store-iap-setup`), privacy policy

View file

@ -5,6 +5,11 @@ description: Answer the store compliance forms for a mobile app or game - Google
# Store declarations
**Do it, don't ask** (first rule in AGENTS.md): you fill these forms yourself (API, or the console with your browser
tool from `storebound console plan`). Ask the owner only the answers no code or config proves, all in one batch at
the start, and save them in the config; the owner presses only App Privacy Publish and the final Submit / Save of a
console questionnaire.
`storebound scan` proposes the Data safety and App Privacy answers from the detected SDKs and code (each item "detected (file:line)" or "ask owner") as a diff against `dataSafety` / `appPrivacy`; `--write` adds only the detected ones after the owner agrees.
Templates: `store/declarations.md` (Play App content + IARC answers), `store/app-privacy.md` (Apple labels), both
@ -21,12 +26,14 @@ CLI: `storebound` (or `bunx storebound` in a project that has it, or `bun <toolc
Play cannot read the form back: storebound keeps the hash of the last upload in `.storebound/data-safety.sha256`
and skips an unchanged CSV. After a hand edit in Play Console, `--force` sends the config's answers again.
Then check the summary in Play Console > Policy and programs > App content > Data safety.
3. **Play App content (UI).** Policy and programs > App content, each card with `store/declarations.md`:
3. **Play App content (UI, you drive it).** Policy and programs > App content, each card with `store/declarations.md`
(`storebound console plan play-content-rating` / `play-target-audience` for the forms it knows):
privacy policy, app access, ads, content rating (IARC questionnaire), target audience and content, news, COVID-19,
data safety, government apps, financial features, health, advertising ID.
4. **Apple age rating (API).** `storebound apple appinfo` (all NONE except your overrides, e.g. `advertising: true`).
5. **Apple App Privacy (UI only).** App Store Connect > the app > App Privacy > Get Started / Edit, from
`store/app-privacy.md`; per data type: purposes > linked to identity > used for tracking. Then **Publish**.
5. **Apple App Privacy (UI only, you drive it: `storebound console plan apple-app-privacy`).** App Store Connect >
the app > App Privacy > Get Started / Edit, from `store/app-privacy.md`; per data type: purposes > linked to
identity > used for tracking, Save. Then the owner presses **Publish**.
- Purposes follow Apple's definitions (developer.apple.com/app-store/app-privacy-details/), one per Play purpose:
Play `advertising` (ads from an ad SDK) is **Third-Party Advertising only**, never also Developer's Advertising
or Marketing (that one is first-party ads or marketing to your own users: the owner's answer); `analytics` is

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 - 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, re-runs and parallel runs, App Store search ranks that are only a trend, Play without a 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 (Google account widget frames), 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 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
@ -8,6 +8,14 @@ description: Known pitfalls with App Store Connect, Google Play and storebound i
Each entry: **symptom** → cause → what to do → what guards it now. The full changelog of lessons with dates is
`docs/pitfalls.md` in the storebound repository. When you hit a new one, tell the owner and add it there (and here).
## First: do it, don't ask (the costliest pitfall)
You and storebound do everything: API calls, uploads, builds, CSV / template exports, every console navigation
(open, Get Started, Edit, Next, Continue, Save draft), console forms, verification. Never hand the owner "run this" or
"click X". The owner presses only the final public or irreversible buttons (Submit for review, release or a
production rollout, App Privacy Publish, legal agreements; deleting store data, live prices, review replies,
testers) and answers, in ONE batch at the start, only the facts no file proves (compliance answers that `check` /
`validate` list). Loop: plan, drive, verify, continue. Full rule: `docs/agents-core.md` (AGENTS.md); lesson 26.
## 1. A product you just created is "not there"
- **Symptom:** `apple iap-review` uploads nothing for a product `apple iap` created a minute ago; a list looks short.
- **Cause:** App Store Connect's `/inAppPurchasesV2` list (and Play's product list) can lag for a short while after a
@ -135,7 +143,7 @@ Each entry: **symptom** → cause → what to do → what guards it now. The ful
- **Cause:** the console showed a page storebound does not recognise (a changed layout, a new question): no field it
can fill and no button it may click.
- **Do:** read the warning: it names the page ("the page after "Next""), its buttons, and where the debug dump is.
Do that part by hand (the manual steps follow), or stop. To report it, re-run with `--debug-dump` and attach the
Drive that part yourself with your browser tool from `storebound console plan <form>` (agent-first), then go on. To report it, re-run with `--debug-dump` and attach the
folder `.storebound/console-debug/<form>-<time>/` after the owner looked at `screen.jpg` (the screenshot shows what
the window showed). `page.json` / `page.html` hold labels and structure only: no values, cookies or tokens; emails,
phone numbers and the account area are replaced.
@ -291,8 +299,8 @@ that fails without the fix (see CONTRIBUTING.md).
`apple testflight wait` for a build that was never uploaded.
- **Cause:** a declined confirmation (`confirm()` in `cli/lib/ctx.ts`) returned from the command with exit 0, and
`flow run --yes` was not passed on to the steps, so a run without a terminal could never confirm.
- **Do:** read a step's output, not only its status. For a run without a terminal the owner can approve the writes
with `flow run <name> --apply --yes` (only on their word, per run); otherwise run the flow in a terminal.
- **Do:** read a step's output, not only its status. After showing the dry run, run the writes yourself with
`flow run <name> --apply --yes` (owner steps never run in a flow, they print as "YOUR CLICK").
- **Guard:** every declined or unanswerable confirmation prints `declined: <reason>` and exits 3 (`DECLINED_EXIT`);
`flow run` stops with "step N declined: <reason>" (status `declined`, never `ok`) and adds `--yes` to write steps
only with `--apply --yes` (`planStep` in `cli/lib/flow.ts`, `declinedReason` in `cli/flow.ts`).
@ -315,3 +323,20 @@ that fails without the fix (see CONTRIBUTING.md).
`sendParts` in `cli/apple/upload.ts`, `cli/lib/progress.ts` (injectable heartbeat), `waitForBuild` in
`cli/apple/testflight.ts`, a 2-minute limit per App Store Connect call (`ASC_REQUEST_TIMEOUT_MS`);
`tests/tools-path.test.ts`, `tests/build-sign.test.ts`, `tests/upload-progress.test.ts`.
## 26. Asking the owner for every step
- **Symptom:** an agent using storebound spent hours handing the owner one click, one command or one answer at a
time: "open App content", "press Next", "download the CSV template", "now run the upload", "what is your
answer for X?" in the middle of a run.
- **Cause:** skills and rules said "let the owner do it" and "by hand" for navigation, draft saves, exports and
uploads, and compliance questions were asked when the run reached them, not at the start.
- **Do:** follow the first rule (top of this file, `docs/agents-core.md`): do every API call, upload, build, export,
console navigation and form yourself, verify by reading back, go on. Collect every fact no file proves in one batch
at the start (`storebound check` / `validate` list them) and save the answers in the config. Stop only for the
owner steps (Submit for review, release / production rollout, App Privacy Publish, legal agreements, deleting store
data, live prices, review replies, testers), prepared with the dry run shown. Make `check` / `--dry` catch store
refusals so they never surface as a 400 mid-run.
- **Guard:** the rule is the first section of `docs/agents-core.md` and so of every generated adapter (AGENTS.md,
GEMINI.md, Copilot and Cursor rules), of README and of this skill; `tests/do-it-dont-ask.test.ts` checks that and
fails when a skill or the rules hand navigation, draft saves, exports, uploads or form filling to the owner
(navigation "by hand", uploads or exports left to the owner, asking the owner to click through a form).

View file

@ -92,8 +92,8 @@ Each line: number, level (`BLOCKER`, `warn`, `ok`), store, message, and a `fix:`
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 by hand)
## 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`.) Tell the owner which are still open.
testflight status`.) Fix what you can; tell the owner only what is an owner step (agreements, tax, banking).

View file

@ -138,8 +138,8 @@ images there), so only do that after a test has a winner. What the stores offer,
- **Google Play store listing experiments** (Play Console > Grow users > Store listing experiments): up to 2 variants
against the current listing; one default-graphics experiment or up to 5 localized experiments at a time; icon,
feature graphic, screenshots (+ descriptions for localized ones); stops automatically after 6 months. Console only:
the Play Developer API has no experiments endpoint, so storebound renders and checks the variant images and you
upload them in the experiment form yourself.
the Play Developer API has no experiments endpoint, so storebound renders and checks the variant images and the
agent uploads them in the experiment form with its browser tool; starting the experiment is the owner's button.
https://support.google.com/googleplay/android-developer/answer/6227309
- **App Store Product Page Optimization** (App Store Connect > your app > Product Page Optimization): up to 3
treatments against the original, one test at a time, up to 90 days; icon, screenshots and app previews, localized

View file

@ -33,9 +33,9 @@ offers, offerCodes, reviewScreenshot, basePlanId, gracePeriod, platforms }] }]`,
1. Prerequisites: Apple Paid Applications agreement, banking and tax; Play payments profile and a build with the
billing library on a track.
2. `storebound check` (limits: Apple name 30 / description 45; Play description 200, up to 4 benefits).
3. `storebound apple subscriptions --dry` and `storebound play subscriptions --dry`; read the plan with the owner; then
run them in a terminal (they ask). Through MCP (no terminal) they only create what is new; price changes of
existing subscriptions wait for the owner.
3. `storebound apple subscriptions --dry` and `storebound play subscriptions --dry`; show the plan, then run them
yourself (`--yes`). A price change of an existing subscription is a live price: get the owner's yes for it first.
Through MCP (no terminal) they only create what is new; price changes of existing subscriptions wait for the owner.
4. Review screenshot of the paywall per subscription (`reviewScreenshot`), uploaded by `apple subscriptions`.
5. **First subscriptions go with an app version** (and a new group with at least one of its subscriptions):
`storebound apple iap-submit --with-version --dry` plans one review submission with the app version, the