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

@ -7,16 +7,43 @@ alwaysApply: false
These rules apply to every AI tool that uses storebound (skills, MCP server or the CLI in a terminal).
## First rule: do it, don't ask
storebound plus you, the AI agent, do the work. The owner is not your pair of hands.
1. **You do everything yourself:** API calls, uploads, builds, template and CSV downloads and exports, every console
navigation (open, Get Started, Edit, Next, Continue, Save draft), console forms, and the verification afterwards.
Never tell the owner "run this yourself" or "click X yourself" for any of these.
2. **The owner keeps only the final public or irreversible buttons:** Submit for review (App Review or Beta App
Review), releasing a version or completing / resuming a production rollout, App Privacy Publish, accepting legal
agreements; plus deleting store data, changing live prices, sending review replies and inviting or removing
testers. (Also the owner's: canceling a submission, starting or stopping a store experiment, a console form's final
Submit / Create that a console plan lists in `buttons.final`, signing in and granting account permissions.) For
these you prepare everything, show the dry run, and the owner presses one button or says yes.
3. **Ask the owner only for facts that no code, config or doc can prove:** compliance answers such as Data safety,
App Privacy, age rating, export compliance, partial data deletion. Collect ALL of them in ONE batch at the start
(`storebound check` and `storebound validate` list them), never one at a time during the run. Reuse answers that
are already in the config.
4. **Default loop:** plan, then you drive (the API first, your browser tool for consoles without an API), then verify
(read it back), then continue. No round trip to the owner in between.
5. **Catch rejections offline.** Before a write, `storebound check` and `--dry` must catch what the store would refuse
(live-shape fixtures, id and pair checks). A failure belongs in the dry run, not in a 400 at write time; when a
store refuses something the dry run passed, fix the check (pitfalls), not just the data.
Every hour of "please click this" is an hour the owner did not want to spend. If you catch yourself writing "you
need to ...", do it instead, unless it is one of the owner steps in point 2.
## The other rules
**Read `skills/store-pitfalls/SKILL.md` before any store task.** It lists the mistakes already made in real releases
(store lists that lag after a create, dry runs without a plan, taken App Store names, byte limits, ...) and what to do.
1. **The owner decides public and irreversible steps.** Submitting for review (App Review or Beta App Review),
releasing a version, resuming or completing a rollout, canceling a submission, deleting products or listings,
accepting agreements, sending review replies, inviting or removing testers, changing prices, starting or stopping
a store experiment: explain what will happen and let the owner do it (or run the command only after a clear yes in
chat). storebound's MCP server does not offer these at all.
2. **Dry run first.** Every write command has `--dry` (MCP: `dry_run`, default true). Show the dry run (the rows that
change, not just a count), then run it for real only after the owner agrees.
1. **The owner presses only the owner steps** (point 2 of the first rule). Prepare them completely, say what will
happen, and let the owner press the button, or run the command only after a clear yes in chat. storebound's MCP
server does not offer these at all. Everything else you run yourself.
2. **Dry run first, then run it yourself.** Every write command has `--dry` (MCP: `dry_run`, default true). Run the
dry run, check and show it (the rows that change, not just a count), then run the real command yourself (`--yes`
where it asks for a confirmation) and read the result back. Wait for the owner's yes only before an owner step.
3. **Read before you write.** `storebound scan` (offline: rejection risks in the project itself), `storebound check`
(offline) and `storebound validate` (read-only, both stores) tell you what is missing and in which order to fix it.
4. **Credentials only from the environment, `.storebound/`** in the project (gitignored) **or a credential profile**
@ -24,7 +51,8 @@ These rules apply to every AI tool that uses storebound (skills, MCP server or t
passwords or tokens in chat, never print them, never write them into the config, docs or commits. The same goes
for the App Review demo account (`reviewDemoAccount` in `.storebound/credentials.json`): the owner puts it there.
5. **Never answer a compliance question you cannot know.** Export compliance, Data safety, App Privacy, age rating,
target audience: ask the owner when the code does not prove the answer. A wrong "no" is worse than a slow "ask".
target audience: ask the owner when the code does not prove the answer, all such questions in one batch at the
start (first rule, point 3), and write the answers into the config. A wrong "no" is worse than a slow "ask".
6. **Pick builds by explicit number.** Use the build number / versionCode you just uploaded, never "the latest".
7. **Long uploads run in the background with a log** (`storebound play images > .storebound/images.log 2>&1 &`) and
you report the progress lines, not guesses.
@ -36,7 +64,7 @@ These rules apply to every AI tool that uses storebound (skills, MCP server or t
10. **Console forms without an API: drive them yourself, agent-first.** `storebound console plan <form> --json` (MCP
`console_plan`) gives the start URL, steps, the answer sheet, the buttons you must never press and the verified UI
facts. Drive the page with your own browser tool, adapt to what it shows, run long flows as an in-page async loop
that writes `window.__log` (one evaluate call times out around 45 s), stop before the final button (Publish,
that writes `window.__log` (one evaluate call times out around 45 s), stop only before the final button (Publish,
Submit, Create, a final Save) and tell the owner exactly what to press, then `storebound console verify <form>`.
Never sign in, accept an agreement or grant a permission (a service account's app access) for the owner.
`storebound console fill` is the optional scripted fast path; it never blocks you.

View file

@ -7,16 +7,43 @@ format; `storebound install --agent copilot` copies them to `.github/skills/` of
These rules apply to every AI tool that uses storebound (skills, MCP server or the CLI in a terminal).
## First rule: do it, don't ask
storebound plus you, the AI agent, do the work. The owner is not your pair of hands.
1. **You do everything yourself:** API calls, uploads, builds, template and CSV downloads and exports, every console
navigation (open, Get Started, Edit, Next, Continue, Save draft), console forms, and the verification afterwards.
Never tell the owner "run this yourself" or "click X yourself" for any of these.
2. **The owner keeps only the final public or irreversible buttons:** Submit for review (App Review or Beta App
Review), releasing a version or completing / resuming a production rollout, App Privacy Publish, accepting legal
agreements; plus deleting store data, changing live prices, sending review replies and inviting or removing
testers. (Also the owner's: canceling a submission, starting or stopping a store experiment, a console form's final
Submit / Create that a console plan lists in `buttons.final`, signing in and granting account permissions.) For
these you prepare everything, show the dry run, and the owner presses one button or says yes.
3. **Ask the owner only for facts that no code, config or doc can prove:** compliance answers such as Data safety,
App Privacy, age rating, export compliance, partial data deletion. Collect ALL of them in ONE batch at the start
(`storebound check` and `storebound validate` list them), never one at a time during the run. Reuse answers that
are already in the config.
4. **Default loop:** plan, then you drive (the API first, your browser tool for consoles without an API), then verify
(read it back), then continue. No round trip to the owner in between.
5. **Catch rejections offline.** Before a write, `storebound check` and `--dry` must catch what the store would refuse
(live-shape fixtures, id and pair checks). A failure belongs in the dry run, not in a 400 at write time; when a
store refuses something the dry run passed, fix the check (pitfalls), not just the data.
Every hour of "please click this" is an hour the owner did not want to spend. If you catch yourself writing "you
need to ...", do it instead, unless it is one of the owner steps in point 2.
## The other rules
**Read `skills/store-pitfalls/SKILL.md` before any store task.** It lists the mistakes already made in real releases
(store lists that lag after a create, dry runs without a plan, taken App Store names, byte limits, ...) and what to do.
1. **The owner decides public and irreversible steps.** Submitting for review (App Review or Beta App Review),
releasing a version, resuming or completing a rollout, canceling a submission, deleting products or listings,
accepting agreements, sending review replies, inviting or removing testers, changing prices, starting or stopping
a store experiment: explain what will happen and let the owner do it (or run the command only after a clear yes in
chat). storebound's MCP server does not offer these at all.
2. **Dry run first.** Every write command has `--dry` (MCP: `dry_run`, default true). Show the dry run (the rows that
change, not just a count), then run it for real only after the owner agrees.
1. **The owner presses only the owner steps** (point 2 of the first rule). Prepare them completely, say what will
happen, and let the owner press the button, or run the command only after a clear yes in chat. storebound's MCP
server does not offer these at all. Everything else you run yourself.
2. **Dry run first, then run it yourself.** Every write command has `--dry` (MCP: `dry_run`, default true). Run the
dry run, check and show it (the rows that change, not just a count), then run the real command yourself (`--yes`
where it asks for a confirmation) and read the result back. Wait for the owner's yes only before an owner step.
3. **Read before you write.** `storebound scan` (offline: rejection risks in the project itself), `storebound check`
(offline) and `storebound validate` (read-only, both stores) tell you what is missing and in which order to fix it.
4. **Credentials only from the environment, `.storebound/`** in the project (gitignored) **or a credential profile**
@ -24,7 +51,8 @@ These rules apply to every AI tool that uses storebound (skills, MCP server or t
passwords or tokens in chat, never print them, never write them into the config, docs or commits. The same goes
for the App Review demo account (`reviewDemoAccount` in `.storebound/credentials.json`): the owner puts it there.
5. **Never answer a compliance question you cannot know.** Export compliance, Data safety, App Privacy, age rating,
target audience: ask the owner when the code does not prove the answer. A wrong "no" is worse than a slow "ask".
target audience: ask the owner when the code does not prove the answer, all such questions in one batch at the
start (first rule, point 3), and write the answers into the config. A wrong "no" is worse than a slow "ask".
6. **Pick builds by explicit number.** Use the build number / versionCode you just uploaded, never "the latest".
7. **Long uploads run in the background with a log** (`storebound play images > .storebound/images.log 2>&1 &`) and
you report the progress lines, not guesses.
@ -36,7 +64,7 @@ These rules apply to every AI tool that uses storebound (skills, MCP server or t
10. **Console forms without an API: drive them yourself, agent-first.** `storebound console plan <form> --json` (MCP
`console_plan`) gives the start URL, steps, the answer sheet, the buttons you must never press and the verified UI
facts. Drive the page with your own browser tool, adapt to what it shows, run long flows as an in-page async loop
that writes `window.__log` (one evaluate call times out around 45 s), stop before the final button (Publish,
that writes `window.__log` (one evaluate call times out around 45 s), stop only before the final button (Publish,
Submit, Create, a final Save) and tell the owner exactly what to press, then `storebound console verify <form>`.
Never sign in, accept an agreement or grant a permission (a service account's app access) for the owner.
`storebound console fill` is the optional scripted fast path; it never blocks you.

View file

@ -8,16 +8,43 @@ file below and follow it step by step.
These rules apply to every AI tool that uses storebound (skills, MCP server or the CLI in a terminal).
## First rule: do it, don't ask
storebound plus you, the AI agent, do the work. The owner is not your pair of hands.
1. **You do everything yourself:** API calls, uploads, builds, template and CSV downloads and exports, every console
navigation (open, Get Started, Edit, Next, Continue, Save draft), console forms, and the verification afterwards.
Never tell the owner "run this yourself" or "click X yourself" for any of these.
2. **The owner keeps only the final public or irreversible buttons:** Submit for review (App Review or Beta App
Review), releasing a version or completing / resuming a production rollout, App Privacy Publish, accepting legal
agreements; plus deleting store data, changing live prices, sending review replies and inviting or removing
testers. (Also the owner's: canceling a submission, starting or stopping a store experiment, a console form's final
Submit / Create that a console plan lists in `buttons.final`, signing in and granting account permissions.) For
these you prepare everything, show the dry run, and the owner presses one button or says yes.
3. **Ask the owner only for facts that no code, config or doc can prove:** compliance answers such as Data safety,
App Privacy, age rating, export compliance, partial data deletion. Collect ALL of them in ONE batch at the start
(`storebound check` and `storebound validate` list them), never one at a time during the run. Reuse answers that
are already in the config.
4. **Default loop:** plan, then you drive (the API first, your browser tool for consoles without an API), then verify
(read it back), then continue. No round trip to the owner in between.
5. **Catch rejections offline.** Before a write, `storebound check` and `--dry` must catch what the store would refuse
(live-shape fixtures, id and pair checks). A failure belongs in the dry run, not in a 400 at write time; when a
store refuses something the dry run passed, fix the check (pitfalls), not just the data.
Every hour of "please click this" is an hour the owner did not want to spend. If you catch yourself writing "you
need to ...", do it instead, unless it is one of the owner steps in point 2.
## The other rules
**Read `skills/store-pitfalls/SKILL.md` before any store task.** It lists the mistakes already made in real releases
(store lists that lag after a create, dry runs without a plan, taken App Store names, byte limits, ...) and what to do.
1. **The owner decides public and irreversible steps.** Submitting for review (App Review or Beta App Review),
releasing a version, resuming or completing a rollout, canceling a submission, deleting products or listings,
accepting agreements, sending review replies, inviting or removing testers, changing prices, starting or stopping
a store experiment: explain what will happen and let the owner do it (or run the command only after a clear yes in
chat). storebound's MCP server does not offer these at all.
2. **Dry run first.** Every write command has `--dry` (MCP: `dry_run`, default true). Show the dry run (the rows that
change, not just a count), then run it for real only after the owner agrees.
1. **The owner presses only the owner steps** (point 2 of the first rule). Prepare them completely, say what will
happen, and let the owner press the button, or run the command only after a clear yes in chat. storebound's MCP
server does not offer these at all. Everything else you run yourself.
2. **Dry run first, then run it yourself.** Every write command has `--dry` (MCP: `dry_run`, default true). Run the
dry run, check and show it (the rows that change, not just a count), then run the real command yourself (`--yes`
where it asks for a confirmation) and read the result back. Wait for the owner's yes only before an owner step.
3. **Read before you write.** `storebound scan` (offline: rejection risks in the project itself), `storebound check`
(offline) and `storebound validate` (read-only, both stores) tell you what is missing and in which order to fix it.
4. **Credentials only from the environment, `.storebound/`** in the project (gitignored) **or a credential profile**
@ -25,7 +52,8 @@ These rules apply to every AI tool that uses storebound (skills, MCP server or t
passwords or tokens in chat, never print them, never write them into the config, docs or commits. The same goes
for the App Review demo account (`reviewDemoAccount` in `.storebound/credentials.json`): the owner puts it there.
5. **Never answer a compliance question you cannot know.** Export compliance, Data safety, App Privacy, age rating,
target audience: ask the owner when the code does not prove the answer. A wrong "no" is worse than a slow "ask".
target audience: ask the owner when the code does not prove the answer, all such questions in one batch at the
start (first rule, point 3), and write the answers into the config. A wrong "no" is worse than a slow "ask".
6. **Pick builds by explicit number.** Use the build number / versionCode you just uploaded, never "the latest".
7. **Long uploads run in the background with a log** (`storebound play images > .storebound/images.log 2>&1 &`) and
you report the progress lines, not guesses.
@ -37,7 +65,7 @@ These rules apply to every AI tool that uses storebound (skills, MCP server or t
10. **Console forms without an API: drive them yourself, agent-first.** `storebound console plan <form> --json` (MCP
`console_plan`) gives the start URL, steps, the answer sheet, the buttons you must never press and the verified UI
facts. Drive the page with your own browser tool, adapt to what it shows, run long flows as an in-page async loop
that writes `window.__log` (one evaluate call times out around 45 s), stop before the final button (Publish,
that writes `window.__log` (one evaluate call times out around 45 s), stop only before the final button (Publish,
Submit, Create, a final Save) and tell the owner exactly what to press, then `storebound console verify <form>`.
Never sign in, accept an agreement or grant a permission (a service account's app access) for the owner.
`storebound console fill` is the optional scripted fast path; it never blocks you.

View file

@ -2,6 +2,13 @@
## Unreleased
- Agent rules: a first, top-level rule "Do it, don't ask" in `docs/agents-core.md` (and so AGENTS.md, GEMINI.md,
the Copilot and Cursor rules), README ("How agents use storebound"), the store-pitfalls skill and the openings of
console-autofill, play-first-release, appstore-first-release and store-declarations. The agent does API calls,
uploads, builds, exports, console navigation and forms itself and runs the real command after the dry run; the
owner presses only the final public or irreversible buttons and answers the facts no file proves in one batch at
the start. Skill text that left navigation, draft saves, exports or uploads to the owner is rewritten;
`tests/do-it-dont-ask.test.ts` guards it. Pitfall 26.
- Fix (`play build`): the Android SDK is found the way Android Studio finds it, so a non-interactive shell (no
`ANDROID_HOME`) with a gitignored `local.properties` no longer ends in Gradle's "SDK location not found". Order:
`ANDROID_HOME`, `ANDROID_SDK_ROOT`, `sdk.dir` in the Gradle project's `local.properties`, then

View file

@ -6,16 +6,43 @@ This extension ships Agent Skills (skills/) and the storebound MCP server for mo
These rules apply to every AI tool that uses storebound (skills, MCP server or the CLI in a terminal).
## First rule: do it, don't ask
storebound plus you, the AI agent, do the work. The owner is not your pair of hands.
1. **You do everything yourself:** API calls, uploads, builds, template and CSV downloads and exports, every console
navigation (open, Get Started, Edit, Next, Continue, Save draft), console forms, and the verification afterwards.
Never tell the owner "run this yourself" or "click X yourself" for any of these.
2. **The owner keeps only the final public or irreversible buttons:** Submit for review (App Review or Beta App
Review), releasing a version or completing / resuming a production rollout, App Privacy Publish, accepting legal
agreements; plus deleting store data, changing live prices, sending review replies and inviting or removing
testers. (Also the owner's: canceling a submission, starting or stopping a store experiment, a console form's final
Submit / Create that a console plan lists in `buttons.final`, signing in and granting account permissions.) For
these you prepare everything, show the dry run, and the owner presses one button or says yes.
3. **Ask the owner only for facts that no code, config or doc can prove:** compliance answers such as Data safety,
App Privacy, age rating, export compliance, partial data deletion. Collect ALL of them in ONE batch at the start
(`storebound check` and `storebound validate` list them), never one at a time during the run. Reuse answers that
are already in the config.
4. **Default loop:** plan, then you drive (the API first, your browser tool for consoles without an API), then verify
(read it back), then continue. No round trip to the owner in between.
5. **Catch rejections offline.** Before a write, `storebound check` and `--dry` must catch what the store would refuse
(live-shape fixtures, id and pair checks). A failure belongs in the dry run, not in a 400 at write time; when a
store refuses something the dry run passed, fix the check (pitfalls), not just the data.
Every hour of "please click this" is an hour the owner did not want to spend. If you catch yourself writing "you
need to ...", do it instead, unless it is one of the owner steps in point 2.
## The other rules
**Read `skills/store-pitfalls/SKILL.md` before any store task.** It lists the mistakes already made in real releases
(store lists that lag after a create, dry runs without a plan, taken App Store names, byte limits, ...) and what to do.
1. **The owner decides public and irreversible steps.** Submitting for review (App Review or Beta App Review),
releasing a version, resuming or completing a rollout, canceling a submission, deleting products or listings,
accepting agreements, sending review replies, inviting or removing testers, changing prices, starting or stopping
a store experiment: explain what will happen and let the owner do it (or run the command only after a clear yes in
chat). storebound's MCP server does not offer these at all.
2. **Dry run first.** Every write command has `--dry` (MCP: `dry_run`, default true). Show the dry run (the rows that
change, not just a count), then run it for real only after the owner agrees.
1. **The owner presses only the owner steps** (point 2 of the first rule). Prepare them completely, say what will
happen, and let the owner press the button, or run the command only after a clear yes in chat. storebound's MCP
server does not offer these at all. Everything else you run yourself.
2. **Dry run first, then run it yourself.** Every write command has `--dry` (MCP: `dry_run`, default true). Run the
dry run, check and show it (the rows that change, not just a count), then run the real command yourself (`--yes`
where it asks for a confirmation) and read the result back. Wait for the owner's yes only before an owner step.
3. **Read before you write.** `storebound scan` (offline: rejection risks in the project itself), `storebound check`
(offline) and `storebound validate` (read-only, both stores) tell you what is missing and in which order to fix it.
4. **Credentials only from the environment, `.storebound/`** in the project (gitignored) **or a credential profile**
@ -23,7 +50,8 @@ These rules apply to every AI tool that uses storebound (skills, MCP server or t
passwords or tokens in chat, never print them, never write them into the config, docs or commits. The same goes
for the App Review demo account (`reviewDemoAccount` in `.storebound/credentials.json`): the owner puts it there.
5. **Never answer a compliance question you cannot know.** Export compliance, Data safety, App Privacy, age rating,
target audience: ask the owner when the code does not prove the answer. A wrong "no" is worse than a slow "ask".
target audience: ask the owner when the code does not prove the answer, all such questions in one batch at the
start (first rule, point 3), and write the answers into the config. A wrong "no" is worse than a slow "ask".
6. **Pick builds by explicit number.** Use the build number / versionCode you just uploaded, never "the latest".
7. **Long uploads run in the background with a log** (`storebound play images > .storebound/images.log 2>&1 &`) and
you report the progress lines, not guesses.
@ -35,7 +63,7 @@ These rules apply to every AI tool that uses storebound (skills, MCP server or t
10. **Console forms without an API: drive them yourself, agent-first.** `storebound console plan <form> --json` (MCP
`console_plan`) gives the start URL, steps, the answer sheet, the buttons you must never press and the verified UI
facts. Drive the page with your own browser tool, adapt to what it shows, run long flows as an in-page async loop
that writes `window.__log` (one evaluate call times out around 45 s), stop before the final button (Publish,
that writes `window.__log` (one evaluate call times out around 45 s), stop only before the final button (Publish,
Submit, Create, a final Save) and tell the owner exactly what to press, then `storebound console verify <form>`.
Never sign in, accept an agreement or grant a permission (a service account's app access) for the owner.
`storebound console fill` is the optional scripted fast path; it never blocks you.

View file

@ -28,6 +28,23 @@ No account, no sign-up, no telemetry. A small Bun CLI that talks to the official
Agent Skills that walk your AI coding tool (or you) through the parts that are still console clicks, and an MCP server
so agents can call the CLI safely. **storebound never presses Submit, Release or Publish for you.**
## How agents use storebound
**Do it, don't ask.** Your AI agent and storebound do the work: API calls, uploads, builds, CSV and template
exports, every console page (open, Get Started, Edit, Next, Save draft), console forms and the check afterwards. You
are not asked to click through consoles or run commands one by one.
- **You press only the final public or irreversible buttons:** Submit for review, releasing a version or a production
rollout, App Privacy Publish, legal agreements; plus deleting store data, changing live prices, sending review
replies and inviting or removing testers. The agent prepares each one and shows you the dry run first.
- **You answer only what no file can prove** (Data safety, App Privacy, age rating, export compliance, partial
deletion): all of it in one batch at the start (`storebound check` / `validate` list the questions), saved in the
config so nobody asks again.
- **The loop:** plan, the agent drives (API first, its browser for consoles without an API), verify by reading back,
continue. `check` and `--dry` catch what a store would refuse before anything is written.
The full rules are in [AGENTS.md](AGENTS.md) (generated from `docs/agents-core.md`).
Pick a profile and start from honest templates: `init --profile app` (utility / productivity app, optional account,
analytics, no ads; the default), `--profile subscription-app` (accounts + auto-renewable subscriptions) or
`--profile game` (free-to-play with ads and IAP).
@ -497,8 +514,8 @@ a terminal first and are never offered over MCP.
visible browser, goes to the form by itself (Apps > + > New App, the app's App Privacy, Play All apps > Create app,
Play app list > App content > Start / Manage;
navigation clicks only, never Save / Submit / Publish / Create / Next), fills what the config proves, highlights the
rest and stops before the final button: you click. **Sign-in:** storebound's own profile
(one per store, `~/.storebound/console-profile/<store>`): you sign in there once by hand, it is kept between runs
rest and stops before the final button: you press it. **Sign-in:** storebound's own profile
(one per store, `~/.storebound/console-profile/<store>`): you sign in there once, it is kept between runs
(session restore on launch, graceful close; tick "Keep me signed in" / "Stay signed in"). storebound starts that
Chrome itself as a normal window (no `--enable-automation`, `navigator.webdriver` false) so Google accepts the
sign-in; if Google still answers `/signin/rejected` it says so. A Chrome running with remote debugging is used in
@ -518,9 +535,11 @@ a terminal first and are never offered over MCP.
declarations and never presses the form's Create app: you do. With `play.developerId` it opens the form's own URL
(`/console/u/0/developers/<id>/create-new-app`); it fills App package name from `play.packageName` and presses the
read-only "Check availability", and clicks the inner `[role=radio]` of Play's material radios (the native input
does not update the form). After Create app, add the new app to your service account yourself (Users and
permissions > the service account > Add app): app-level permissions do not cover apps created later.
When a page has changed it falls back to step-by-step guidance with copyable answers. **Console pages change; this is AI-assisted: review every field and save it yourself.** Desktop: "Fill in
does not update the form). After Create app, grant your service account access to the new app (Users and
permissions > the service account > Add app; a permission grant, so it is yours): app-level permissions do not
cover apps created later. When a page has changed, your agent drives that part with its browser tool from
`storebound console plan`. **Console pages change; this is AI-assisted: review every field before you press the
final button.** Desktop: "Fill in
console" on the App info tab. Never over MCP. Skill: `console-autofill`.
MCP gets only the safe parts: `prices_ppp`, `web_init`, `apple_adhoc_options` (local files, dry run by default) and

View file

@ -2,16 +2,43 @@
These rules apply to every AI tool that uses storebound (skills, MCP server or the CLI in a terminal).
## First rule: do it, don't ask
storebound plus you, the AI agent, do the work. The owner is not your pair of hands.
1. **You do everything yourself:** API calls, uploads, builds, template and CSV downloads and exports, every console
navigation (open, Get Started, Edit, Next, Continue, Save draft), console forms, and the verification afterwards.
Never tell the owner "run this yourself" or "click X yourself" for any of these.
2. **The owner keeps only the final public or irreversible buttons:** Submit for review (App Review or Beta App
Review), releasing a version or completing / resuming a production rollout, App Privacy Publish, accepting legal
agreements; plus deleting store data, changing live prices, sending review replies and inviting or removing
testers. (Also the owner's: canceling a submission, starting or stopping a store experiment, a console form's final
Submit / Create that a console plan lists in `buttons.final`, signing in and granting account permissions.) For
these you prepare everything, show the dry run, and the owner presses one button or says yes.
3. **Ask the owner only for facts that no code, config or doc can prove:** compliance answers such as Data safety,
App Privacy, age rating, export compliance, partial data deletion. Collect ALL of them in ONE batch at the start
(`storebound check` and `storebound validate` list them), never one at a time during the run. Reuse answers that
are already in the config.
4. **Default loop:** plan, then you drive (the API first, your browser tool for consoles without an API), then verify
(read it back), then continue. No round trip to the owner in between.
5. **Catch rejections offline.** Before a write, `storebound check` and `--dry` must catch what the store would refuse
(live-shape fixtures, id and pair checks). A failure belongs in the dry run, not in a 400 at write time; when a
store refuses something the dry run passed, fix the check (pitfalls), not just the data.
Every hour of "please click this" is an hour the owner did not want to spend. If you catch yourself writing "you
need to ...", do it instead, unless it is one of the owner steps in point 2.
## The other rules
**Read `skills/store-pitfalls/SKILL.md` before any store task.** It lists the mistakes already made in real releases
(store lists that lag after a create, dry runs without a plan, taken App Store names, byte limits, ...) and what to do.
1. **The owner decides public and irreversible steps.** Submitting for review (App Review or Beta App Review),
releasing a version, resuming or completing a rollout, canceling a submission, deleting products or listings,
accepting agreements, sending review replies, inviting or removing testers, changing prices, starting or stopping
a store experiment: explain what will happen and let the owner do it (or run the command only after a clear yes in
chat). storebound's MCP server does not offer these at all.
2. **Dry run first.** Every write command has `--dry` (MCP: `dry_run`, default true). Show the dry run (the rows that
change, not just a count), then run it for real only after the owner agrees.
1. **The owner presses only the owner steps** (point 2 of the first rule). Prepare them completely, say what will
happen, and let the owner press the button, or run the command only after a clear yes in chat. storebound's MCP
server does not offer these at all. Everything else you run yourself.
2. **Dry run first, then run it yourself.** Every write command has `--dry` (MCP: `dry_run`, default true). Run the
dry run, check and show it (the rows that change, not just a count), then run the real command yourself (`--yes`
where it asks for a confirmation) and read the result back. Wait for the owner's yes only before an owner step.
3. **Read before you write.** `storebound scan` (offline: rejection risks in the project itself), `storebound check`
(offline) and `storebound validate` (read-only, both stores) tell you what is missing and in which order to fix it.
4. **Credentials only from the environment, `.storebound/`** in the project (gitignored) **or a credential profile**
@ -19,7 +46,8 @@ These rules apply to every AI tool that uses storebound (skills, MCP server or t
passwords or tokens in chat, never print them, never write them into the config, docs or commits. The same goes
for the App Review demo account (`reviewDemoAccount` in `.storebound/credentials.json`): the owner puts it there.
5. **Never answer a compliance question you cannot know.** Export compliance, Data safety, App Privacy, age rating,
target audience: ask the owner when the code does not prove the answer. A wrong "no" is worse than a slow "ask".
target audience: ask the owner when the code does not prove the answer, all such questions in one batch at the
start (first rule, point 3), and write the answers into the config. A wrong "no" is worse than a slow "ask".
6. **Pick builds by explicit number.** Use the build number / versionCode you just uploaded, never "the latest".
7. **Long uploads run in the background with a log** (`storebound play images > .storebound/images.log 2>&1 &`) and
you report the progress lines, not guesses.
@ -31,7 +59,7 @@ These rules apply to every AI tool that uses storebound (skills, MCP server or t
10. **Console forms without an API: drive them yourself, agent-first.** `storebound console plan <form> --json` (MCP
`console_plan`) gives the start URL, steps, the answer sheet, the buttons you must never press and the verified UI
facts. Drive the page with your own browser tool, adapt to what it shows, run long flows as an in-page async loop
that writes `window.__log` (one evaluate call times out around 45 s), stop before the final button (Publish,
that writes `window.__log` (one evaluate call times out around 45 s), stop only before the final button (Publish,
Submit, Create, a final Save) and tell the owner exactly what to press, then `storebound console verify <form>`.
Never sign in, accept an agreement or grant a permission (a service account's app access) for the owner.
`storebound console fill` is the optional scripted fast path; it never blocks you.

View file

@ -9,6 +9,7 @@ fails without the fix.
| # | date | lesson | guard |
|---|---|---|---|
| 26 | 2026-10 | An agent using storebound spent hours handing the owner one click, command or answer at a time ("press Next", "download the CSV template", "now run the upload", a compliance question in the middle of a run): skills said "let the owner do it" / "by hand" for navigation, exports and uploads. | The first rule "Do it, don't ask" heads `docs/agents-core.md` (every generated adapter), README and the store-pitfalls skill: the agent does API calls, uploads, builds, exports, console navigation and forms itself; the owner presses only the final public or irreversible buttons and answers the facts no file proves in one batch at the start. `tests/do-it-dont-ask.test.ts` checks the rule is there and fails on skill text that hands navigation, draft saves, exports, uploads or form filling to the owner. |
| 25 | 2026-10 | In a non-interactive shell `play build` stopped in Gradle with "SDK location not found" (no `ANDROID_HOME` there, `local.properties` gitignored) although the SDK was in `~/Library/Android/sdk`; `apple upload --no-wait --yes` sent the IPA for over 10 minutes without a line (progress every 10th part only, no per-part or per-call timeout). | `resolveAndroidSdk` (ANDROID_HOME, ANDROID_SDK_ROOT, `sdk.dir` in local.properties, the Android Studio default folder; only folders with platforms/ or platform-tools/) hands the SDK to Gradle in the environment, never writes local.properties, stops before Gradle with the places checked; `doctor` shows it (`cli/lib/tools.ts`, `cli/play/build.ts`, `cli/doctor.ts`). `apple upload` prints the size, a line per part and a 20 s heartbeat (api, altool, processing wait), per-part and per-call timeouts, `--timeout` exits 7 with the resume hint (`sendParts` in `cli/apple/upload.ts`, `cli/lib/progress.ts`; `tests/tools-path.test.ts`, `tests/build-sign.test.ts`, `tests/upload-progress.test.ts`). |
| 24 | 2026-10 | `flow run release --apply --yes` ran `apple upload --no-wait`, which printed "not a terminal: re-run with --yes to confirm" and declined, yet the flow logged "4/13 upload: ok" and went on to `apple testflight wait` for a build that was never uploaded: a declined confirmation exited 0, and the flow's `--yes` never reached the steps. | `confirm()` (`cli/lib/ctx.ts`): every decline (answered no, not a terminal without `--yes`, under serve) prints `declined: <reason>` and exits 3 (`DECLINED_EXIT`). `flow run` reports "step N declined: <reason>" (status `declined`) and stops; `--apply --yes` adds `--yes` to write steps only (`planStep`, `declinedReason`; `tests/flow.test.ts`). |
| 23 | 2026-10 | Capacitor: native build ships old web assets. `flow run release --apply` went straight into `apple build` (xcodebuild archive) with `ios/App/App/public` and `android/app/src/main/assets/public` 3.5 hours older than the latest source commit: the native projects hold the copy `npx cap sync` made last, and nothing ran the web build + cap sync first, so the old game would have shipped. | New `build.prebuild` (per store `apple.prebuild` / `play.prebuild`): `apple build` / `play build` run it first (printed, streamed, nonzero exit stops; `--dry` only prints; `--no-prebuild`; once per `flow run` through `STOREBOUND_PREBUILD_DONE`). After it, a Capacitor project's copied `public/index.html` must be newer than the last commit outside ios/ android/ node_modules/ store/ .storebound/ and `<webDir>/index.html`, else the build stops ("web assets are older than the source ..."; `--allow-stale-web` overrides). `check` warns when stale without a prebuild; build output and dry plan show the bundle's sha256 + mtime. Monorepo (`apps/native` with its own capacitor.config.ts, android/, ios/; `play build` failed with "no Gradle project ... in android or ."): the one Capacitor project one or two levels down is found and named, several ask for `play.build.dir` / `apple.build.*` (`cli/lib/web-assets.ts`, `tests/web-assets.test.ts`). |

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

View file

@ -0,0 +1,69 @@
// Pitfall 26: an agent spent hours handing the owner one click, command or answer at a time. The first rule
// "Do it, don't ask" must head the agent rules (and so every generated adapter), README and the pitfalls skill, and
// no skill or rule text may hand navigation, draft saves, exports, uploads or form filling to the owner.
import { describe, expect, test } from 'bun:test';
import { readdirSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
const root = join(import.meta.dir, '..');
const read = (rel: string) => readFileSync(join(root, rel), 'utf8');
const RULE = "do it, don't ask";
describe("first rule: do it, don't ask", () => {
test('is the first section of docs/agents-core.md, above the numbered rules', () => {
const core = read('docs/agents-core.md');
const firstHeading = /^## (.*)$/m.exec(core)?.[1] ?? '';
expect(firstHeading.toLowerCase()).toContain(RULE);
expect(core.indexOf('## First rule')).toBeLessThan(core.indexOf('1. **The owner presses only the owner steps**'));
for (const owner of ['Submit for review', 'App Privacy Publish', 'legal', 'live prices', 'review replies', 'testers'])
expect(core).toContain(owner);
expect(core).toMatch(/ONE batch at the start/);
});
test('reaches every generated adapter, README and the lead of store-pitfalls', () => {
for (const f of ['AGENTS.md', 'GEMINI.md', '.github/copilot-instructions.md', '.cursor/rules/storebound.mdc'])
expect(read(f).toLowerCase()).toContain(RULE);
const readme = read('README.md');
const at = readme.indexOf('## How agents use storebound');
expect(at).toBeGreaterThan(0);
expect(readme.slice(0, at).split('\n## ').length).toBe(1); // the first section of README
const pit = read('skills/store-pitfalls/SKILL.md');
expect(/^## (.*)$/m.exec(pit)?.[1]?.toLowerCase()).toContain(RULE);
for (const s of ['console-autofill', 'play-first-release', 'appstore-first-release', 'store-declarations'])
expect(read(`skills/${s}/SKILL.md`).toLowerCase()).toContain(RULE);
});
// Phrases that hand agent work (navigation, draft saves, exports, uploads, form filling, commands) to the owner.
const HANDOFF: RegExp[] = [
/\bUI = [^)\n]*by hand/i,
/\bask the owner to (click|press next|open|navigate|download|upload|export|fill|save|copy|run)\b/i,
/\b(have|let) the owner (click|open|navigate|download|upload|export|fill|save|copy|run)\b/i,
/\bthe owner (clicks|opens|navigates|downloads|uploads|exports|fills|copies|pastes)\b/i,
/\b(click|download|upload|export|navigate)\b[^.\n]{0,60}\byourself\b/i,
/\b(do|fill|finish) (that|this) (part )?by hand\b/i,
/\bfor the owner to copy\b/i,
];
const files = ['docs/agents-core.md', 'README.md',
...readdirSync(join(root, 'skills')).map(n => `skills/${n}/SKILL.md`).filter(f => { try { read(f); return true; } catch { return false; } })];
test('no skill or rule hands navigation, exports, uploads or forms to the owner', () => {
const hits: string[] = [];
for (const f of files) {
read(f).split('\n').forEach((line, i) => {
if (/never tell the owner/i.test(line)) return; // the rule quoting what not to say
for (const re of HANDOFF) if (re.test(line)) hits.push(`${f}:${i + 1}: ${line.trim().slice(0, 120)}`);
});
}
expect(hits).toEqual([]);
});
test('the handoff patterns catch the phrases that cost hours', () => {
const bad = ['## Steps (API = storebound, UI = Play Console by hand)', 'ask the owner to click Next on each page',
'upload them in the experiment form yourself', 'Do that part by hand (the manual steps follow)',
'The owner opens App content > Data safety > Export to CSV', 'prints the list for the owner to copy'];
for (const b of bad) expect(HANDOFF.some(re => re.test(b))).toBe(true);
const ok = ['drive the page yourself (Claude in Chrome)', 'the owner presses Publish', 'the owner signs in once',
'press Create app yourself', 'Granting them is the owner\'s (a permission grant)'];
for (const o of ok) expect(HANDOFF.some(re => re.test(o))).toBe(false);
});
});