console: agent-first mode (console plan / console verify), verified App Privacy and Play Create app facts

- `storebound console plan <form> [--json] [--lookup]`: read-only plan for an AI agent that drives the browser
  itself: start URL (deep links), ordered steps, answer sheet with the console's exact labels (certain / ask owner,
  curly quotes noted), never-press list, verified UI facts, driving guidance (in-page async loop + window.__log),
  verify instructions. All six forms.
- `storebound console verify <form> [--from-text <file|->]`: App Privacy per-type summaries from page text, Play
  Create app from input states, content rating / target audience summaries, App Store record and age rating from
  the API; exit 1 on a mismatch.
- MCP: console_plan, console_verify (read-only); console fill stays off MCP.
- App Privacy (verified 2026-10-12): two information steps, unlabeled purpose checkboxes (nearest parent text),
  "Definitions and Examples" never pressed; fixture and engine follow.
- Play Create app (verified 2026-10-12): /developers/<id>/create-new-app, App package name + read-only Check
  availability, material-radio inner [role=radio] click, app dashboard URL and play.appId, service account
  "Add app" as an owner step.
- console-autofill skill rewritten agent-first; agents-core rule 10; README; pitfalls 12, 18 (confirmed sign-out),
  19; CHANGELOG; play-first-release.
This commit is contained in:
CRTkafa 2026-10-12 08:22:03 +08:00
parent a9261a8052
commit c9a7f095c4
34 changed files with 1430 additions and 113 deletions

View file

@ -1,21 +1,82 @@
---
name: console-autofill
description: AI-assisted autofill of App Store Connect and Google Play Console forms that have no API (the New App dialog, App Privacy, Play's Create app form, the Play IARC content rating questionnaire, Target audience and content plus the Ads declaration; the App Store age rating is shown as guidance because the API sets it) in a visible browser with storebound's own profile that the owner signs in to once (never a copy of the owner's Google cookies unless they opt in); storebound opens the form itself. Answers come only from storebound.config.ts; anything the config does not prove stays empty and highlighted. storebound moves through the pages itself (Next; App Privacy: Get Started, Edit, each data type and the draft Save; content rating: Save draft) and never signs in, submits or publishes: the owner presses the final button. Use when the user wants help filling one of these console forms, asks what to answer in them, or wants the answers as a checklist.
description: Fill App Store Connect and Google Play Console forms that have no API (New App, App Privacy, Play Create app, the IARC content rating, Target audience and content plus Ads; the App Store age rating is guidance because the API sets it). Agent-first by default - `storebound console plan <form> --json` (MCP console_plan) gives the start URL, steps, answer sheet with the console's exact labels, never-press list and verified UI facts; you drive the page with your own browser tool (Claude in Chrome, a built-in browser, Playwright), stop before the final button and tell the owner what to press, then `storebound console verify <form>` (MCP console_verify). `storebound console fill` is the optional scripted fast path. Answers come only from storebound.config.ts. Never sign in, submit, publish, accept agreements or grant permissions. Use when the user wants help filling one of these console forms, asks what to answer in them, or wants the answers as a checklist.
---
# Console autofill (forms without an API)
# Console forms without an API
CLI: `storebound` (or `bunx storebound` in a project that has it, or `bun <toolchain>/cli/index.ts`).
**Read this to the owner first.** Console pages change without notice. This is AI-assisted autofill and it may put an
answer in the wrong place. The owner reviews every field and presses the final button. storebound never types a
password, never touches a sign-in page, never prints or sends a cookie, and never clicks Submit, Publish, Create, a
final Save or anything that sends a form for review (English or Turkish console). It clicks only what moves through
the form (see "What storebound clicks" below). It works in its own
browser profile where the owner signs in once by hand. It never copies the owner's Google cookies unless the owner
opts in (`--use-chrome-session google`), because Google binds them to the device and may sign the owner out of their
real Chrome (step 3).
It is not available over MCP.
**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
is pressed by you or by storebound.
## Default: agent-first (you drive the browser)
storebound supplies the knowledge and the check; you drive the page with your own browser tool and adapt to whatever
the UI shows. This is the primary mode.
1. `storebound console plan <form> --json` (MCP: `console_plan`; read-only, offline; `--lookup` asks the App Store
Connect API for the app id). It returns:
- `start`: the URL (a deep link when `apple.appId`, `play.developerId` or `play.appId` is known, or the ids
storebound remembered) and how to reach the form otherwise;
- `steps`: plain language plus what to `click` (by visible text) and which `answers` go on each step;
- `answers`: every answer with `status` (`certain`, `ask-owner`, `owner-check`, `agreement`), the console's own
`label` (curly quotes as shown, `curlyQuotes: true`), `action` (tick / untick / pick / type / choose) and `where`;
- `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
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
answer as the sheet says, wait for the next step to render. If the page differs from the plan, adapt (the console
changes), but never press anything in `buttons.neverPress` and never tick an agreement.
5. Long flows (App Privacy with many data types): one evaluate call times out around 45 s, so start an in-page async
loop and poll it:
```js
window.__log = []; window.__run = (async () => { /* for each step: wait for it, act, */ window.__log.push('Device ID: purposes set'); /* ... */ window.__log.push('done'); })().catch(e => window.__log.push('stopped: ' + e.message));
```
then read `window.__log` every few seconds until it says `done` or `stopped`. Never click past a step you have not
read; on an unknown step, stop the loop and look.
6. Stop before `buttons.final` and tell the owner exactly what to press (App Privacy: review the summary, press
Publish; Play Create app: tick the two declarations, press Create app; content rating: Submit; target audience:
Save; New App: Create).
7. `storebound console verify <form> --from-text -` (MCP: `console_verify` with `text`) with what `verify.capture`
says: App Privacy the page's `innerText`, Play Create app the JSON of the capture snippet. Exit 1 / mismatches:
fix them in the page (or the config), verify again. `apple-app-record` and `apple-age-rating` verify from the API
without text.
### Verified UI facts
- **App Privacy** (`/apps/<id>/distribution/privacy`, 2026-10-12): "Set Up <type>" is a plain `<p>` with a click
handler (click it by exact text); it opens a `<dialog>` whose h2 is the type. Step 1: 6 purpose checkboxes in the
order Third-Party Advertising, Developer’s Advertising or Marketing (curly ’), Analytics, Product Personalization,
App Functionality, Other Purposes; an input has no aria label, its label is the nearest non-empty parent text
(label and help text run together). Step 2: radios "Yes, <type> data collected from this app is linked to the
user’s identity" / "No, …". Steps 3 and 4: two information pages ("Let’s define two important terms…", "To help put
tracking into context, here are a few examples:"), no inputs: Next on each. Step 5: radios "Yes, we use <type> data
for tracking purposes" / "No, …", plus "Definitions and Examples" (never press), Back, Cancel, Save. Save closes the
dialog; the type then shows Edit and a summary ("Used for …", linked, tracking), which `console verify` reads.
- **Play Create app** (2026-10-12, through Claude in Chrome): `/console/u/0/developers/<developerId>/create-new-app`.
App name (max 30), App package name (max 150) with a read-only "Check availability" ("Package name available";
press it), Default language (default "English (United States) – en-US"), App or game and Free or paid as
`<material-radio>`s, two declarations (the owner's). In a background window computer-tool clicks on the material
radios did nothing and `form_input` on the native inputs did not update the form ("Select app or game" stayed):
click the inner `[role=radio]` with a JS click, then check `aria-checked="true"` and that no "Select …" hint is
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).
## 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).
## Forms
@ -28,7 +89,7 @@ It is not available over MCP.
| `play-content-rating` | Policy > App content > Content rating | app.contactEmail, app.profile, products, console.contentRating |
| `play-target-audience` | Policy > App content > Target audience and content (+ Ads) | console.targetAudience, ads.appAdsTxt |
## Steps
## Steps (scripted fast path)
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.
@ -127,7 +188,7 @@ It is not available over MCP.
for the owner to pick).
6. Multi-page forms: storebound fills a page and clicks its Next itself (App Privacy: Get Started or Edit next to
Data Types, Next, the data types' Save, then "Set Up <type>" for each collected type in the Data Types section:
purposes > Next > linked > Next > tracking > Save; a dialog step it does not know: a debug dump and
purposes > Next > linked > Next > two information pages > Next > Next > tracking > Save; a dialog step it does not know: a debug dump and
`paused on "<type>"` until the owner answered it; content rating: Save draft, then Next). It stops on a page only when an answer there is uncertain: it logs
`answer needed on "<page>": <the answers>`, leaves them outlined, and goes on by itself once the owner has set
each one (a box that should stay empty: click it twice). It never clicks Next past an open question.
@ -160,8 +221,11 @@ It is not available over MCP.
owner before they press Create app.
- **Declarations** (Developer Program Policies, US export laws) are agreements: storebound never ticks them, it
outlines them. The owner reads and ticks them and presses Create app.
- The package name is not in this form: the first AAB upload sets it for good (`storebound play upload --track
internal`, or `storebound flow run release`).
- App package name (`play.packageName`, max 150) is in the form: storebound fills it and presses the read-only
"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).
## What storebound clicks, and what stays with the owner
@ -171,10 +235,10 @@ content", "Create and set up your app"):
| form | storebound clicks (navigation) | stays with the owner, and why |
|---|---|---|
| App Privacy | Get Started; Edit next to Data Types (never the Privacy Policy Edit; skipped when the section already lists exactly the config's types); "Set Up <type>" for each collected type (a plain text, clicked by its exact text); a type's own Edit only when its purposes differ from the config; Next; **Save inside the questionnaire dialogs** (a draft: Apple publishes the answers only after Publish) | **Publish** and its confirmation: it puts the answers on the App Store product page; the "?" help buttons are never clicked |
| App Privacy | Get Started; Edit next to Data Types (never the Privacy Policy Edit; skipped when the section already lists exactly the config's types); "Set Up <type>" for each collected type (a plain text, clicked by its exact text); a type's own Edit only when its purposes differ from the config; Next; **Save inside the questionnaire dialogs** (a draft: Apple publishes the answers only after Publish) | **Publish** and its confirmation: it puts the answers on the App Store product page; the "?" help buttons and "Definitions and Examples" are never clicked |
| New App (App Store) | + and New App (Cancel only to leave a team without the bundle id) | **Create**: creates the app record for good |
| Age rating (guide only) | nothing (`storebound apple appinfo` sets it by API) | Set Up / Next / Done, if the owner wants to look |
| Create app (Play) | Create app on the app list (opens the form) | the two **declarations** (agreements, never ticked) and **Create app** in the form: creates the app |
| Create app (Play) | Create app on the app list (opens the form); Check availability next to the package name (read-only) | the two **declarations** (agreements, never ticked) and **Create app** in the form: creates the 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 |

View file

@ -15,8 +15,20 @@ deleting products. Sending a first release for review is the owner's action in t
## Steps (API = storebound does it, UI = Play Console by hand)
1. **UI · Create the app.** Play Console > Home > Create app: name, default language, App or Game, Free or Paid, declarations.
The package name is fixed by the first uploaded AAB.
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
`/console/u/0/developers/<developerId>/create-new-app`: App name (max 30), App package name (`play.packageName`,
max 150; press the read-only "Check availability": "Package name available"; fixed for good), default language,
App or Game and Free or Paid (material radios: click the inner `[role=radio]`, then no "Select …" hint may be
left), and the declarations, which the owner ticks before pressing Create app. Free can never become Paid.
`storebound console verify play-app-record` checks the form first. Afterwards the app lives at
`/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`.
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,

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 (bounded wait and a debug dump), a signed-in console read as a sign-in page (Google account widget frames). 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 - 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 (bounded wait and a debug dump), a signed-in console read as a sign-in page (Google account widget frames), console forms driven agent-first. Read it before any store task, and when a store command does something you did not expect.
---
# Store pitfalls
@ -103,11 +103,19 @@ Each entry: **symptom** → cause → what to do → what guards it now. The ful
- **Symptom:** an agent cannot create the Play app through the API, or clicks "Create app" without knowing whether it
opens the form or submits it; or an app goes out as Free and the owner later wants it paid.
- **Cause:** the Play Developer API cannot create apps. Play Console's Home has a "Create app" button that opens the
form, and the form's final button has the same words. Free cannot become Paid later on Google Play. The package
name is not in this form: the first AAB upload sets it.
- **Do:** `storebound console fill play-app-record` (pick the account with `play.developerId` or
`play.developerAccount`); tell the owner that Free is final; the owner ticks the declarations and presses Create
app; then `storebound play upload --track internal` (or `flow run release`) sets the package name.
form, and the form's final button has the same words. Free cannot become Paid later on Google Play. The form
(`/console/u/0/developers/<developerId>/create-new-app`, verified 2026-10-12) has an App package name field (max
150) with a read-only "Check availability" button ("Package name available"); the package name is fixed for good.
App or game and Free or paid are `<material-radio>`s: setting the native input leaves the form saying "Select app
or game" until the inner `[role=radio]` is clicked (a JS click works in a background window).
- **Do:** `storebound console plan play-app-record` and drive the form with your browser tool (or the scripted
`console fill play-app-record`); fill the package name and press Check availability; click the inner
`[role=radio]`s and check that no "Select …" hint is left (`console verify play-app-record`); tell the owner that
Free is final; the owner ticks the declarations and presses Create app. Afterwards the app lives at
`/console/u/0/developers/<developerId>/app/<appId>/app-dashboard` (set `play.appId`). The service account's
app-level permissions do NOT cover the new app: the OWNER adds it (Users and permissions > the service account >
Add app > Admin, or the minimal set > Apply > Save changes; never an agent: it is a permission grant), then
`storebound apps check --store play`.
- **Guard:** `createAppAllowed` lets the opener through only on the app list, never inside a form or dialog (English
and Turkish); agreement checkboxes are never ticked; Free or paid is outlined as an owner check.
@ -183,6 +191,8 @@ Each entry: **symptom** → cause → what to do → what guards it now. The ful
opt-in that prints the warning; an agent never adds it. Apple publishes no such binding, but `--use-chrome-session
apple` is opt-in too. If Google says "Couldn't sign you in" in storebound's window, storebound prints "Google refused
sign-in in this window (/signin/rejected)": try again, or sign in at accounts.google.com first in that window.
- **Confirmed (2026-10-12):** the earlier default copy of the Google cookies DID sign the owner out of Play Console
in their real Chrome.
- **Guard:** `browserPlan` / `openConsoleBrowser` in `cli/console/session.ts` (no copy without the flag, the copy keeps
only the form's own provider), Chrome started by storebound without `--enable-automation` and with
`navigator.webdriver` false, `googleRejected`; `tests/console-session.test.ts`.
@ -206,6 +216,20 @@ Each entry: **symptom** → cause → what to do → what guards it now. The ful
`tests/play-post-reads.test.ts` (phone-only fixture: dry plan, info line, exit 0; `tablets: true` without renders is
an error; no tablet DELETE without `--prune`).
## 20. Drive consoles agent-first; the scripted fill is a fast path
- **Symptom:** hours went into fix cycles of the scripted engine's selectors (`console fill`) while the console UI
kept differing from the model (App Privacy's "Set Up" as plain text, unlabeled purpose checkboxes, two information
steps, Play's material radios), each one a stop until a new release.
- **Cause:** a script only knows the page it was written for. An AI agent with a browser tool reads the page as it is
and adapts.
- **Do:** run `storebound console plan <form> --json` and drive the page yourself (Claude in Chrome, a built-in
browser, Playwright) from its answer sheet, never-press list and UI facts; 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 and tell the owner
what to press; then `storebound console verify <form>`. `console fill` is the optional scripted fast path; when it
stalls, switch to the plan instead of waiting for a selector fix.
- **Guard:** `console plan` / `console verify` (CLI and the read-only MCP tools `console_plan`, `console_verify`), the
console-autofill skill puts them first; `tests/console-plan.test.ts`.
## Adding a lesson
Every bug found in real use gets an entry in `docs/pitfalls.md` and here, and, where possible, a check or a test
that fails without the fix (see CONTRIBUTING.md).