price changes of existing products need --allow-price-changes (play products, play/apple subscriptions, prices import): --yes and flow run --apply --yes confirm creates, first prices and other updates only; without the flag the run writes the rest, names each held change and exits 3 (declined), dry runs mark them owner, flows refuse the flag; lib/price-gate.ts, tests/price-gate.test.ts. CLI wording: console fill leaves what it cannot open or fill to the calling agent's browser tool (console plan / verify) instead of 'yourself'; other 'by hand' output points at the config; do-it-dont-ask test scans cli/ output

This commit is contained in:
CRTkafa 2026-10-12 09:38:56 +08:00
parent b5b8f2da9d
commit e01a27d7b7
33 changed files with 414 additions and 117 deletions

View file

@ -50,7 +50,7 @@ every one of `fastlane/`, `ios/fastlane/`, `android/fastlane/` that exists; pass
- `price_tier`: products and the prices CSV (`store-iap-setup`). Age rating config: `store-declarations`.
Export compliance: `export-compliance` (`apple.usesNonExemptEncryption` stays commented out).
- Lanes, `match`, `gym` / `gradle`: building and signing stay where they are; storebound has no lanes.
- Ruby logic (`for_platform`, `for_lane`, `ENV[...]`, interpolation): reported by key and line, set by hand.
- Ruby logic (`for_platform`, `for_lane`, `ENV[...]`, interpolation): reported by key and line; set those in storebound.config.ts.
- Promo graphic, TV banner, TV / Wear screenshots, Apple TV privacy policy, Korean trade representative contact:
consoles.

View file

@ -42,7 +42,8 @@ Config (optional): `prices: { ppp: { source, file, floor, ceiling, out } }`.
`storebound prices ppp` (default `store/prices-ppp.csv`, `--out` to change it).
4. The owner reviews and edits the CSV (delete rows to keep a store's own conversion, round to a house price ladder).
5. Apply through the normal price sheet flow: `storebound prices import --csv store/prices-ppp.csv --dry`, then
without `--dry` in a terminal after the owner says yes (see store-iap-setup). Import checks Apple price points
`--yes --allow-price-changes` after the owner says yes to that diff (see store-iap-setup; `--yes` alone never
changes an existing price). Import checks Apple price points
(the error lists the neighbours) and Play's region currencies.
## Read before applying

View file

@ -49,8 +49,11 @@ once (the second build step prints "prebuild skipped (already ran in this flow r
- `--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 gets `--yes`; reads, waits and owner steps do not, so nothing owner-only runs. `--yes` never changes the price
of an existing product (`play products`, `apple subscriptions`, `play subscriptions`): such a step writes everything
else, names each held change ("price change on <id> (<from> -> <to>) needs the owner") and exits 3, so the flow
stops as declined. After the owner's yes for that diff, run that one command with `--allow-price-changes` (refused
in flow steps, never added by `flow run`), then `flow run <name> --apply --yes --resume`. 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

@ -30,10 +30,13 @@ Auto-renewable subscriptions are a separate list (`subscriptions`, `store-subscr
2. `storebound play products`. Per product: `pricing:convertRegionPrices` (USD -> every region at local price points,
and the `regionsVersion`), your overrides on top, `PATCH .../onetimeproducts/{id}?allowMissing=true&regionsVersion.version=...`
(create or update), then `oneTimeProducts/{id}/purchaseOptions:batchUpdateStates` to activate (new options are DRAFT).
New products and listing text go straight through. Price, region or activation changes of an EXISTING product
are the owner's decision: the run shows the diff and asks in a terminal; without a terminal (MCP, CI, the desktop
app's normal write) they are skipped with a warning, the listing text still updates. The owner confirms in a
terminal, or applies prices with the desktop Prices "Apply" (owner action, `prices import`).
New products (with their first prices) and listing text go straight through; `--yes` confirms activating the
purchase option of an existing product. Price or region changes of an EXISTING product are the owner's decision
and `--yes` never confirms them (also not the `--yes` that `flow run --apply --yes` adds): the dry run marks those
rows `owner`, and a real run writes everything else, prints `price change on <id> (<from> -> <to>) needs the
owner: re-run with --allow-price-changes` and exits 3 (declined; a flow stops there). After the owner's yes in
chat for exactly that diff, run `storebound play products --allow-price-changes`; the owner can also answer the
question in a terminal (no `--yes`) or use the desktop Prices "Apply" (owner action).
3. Verify: `storebound play status` lists every product with `buy:ACTIVE`.
4. Test: Play Console > Settings > License testing: add tester Gmail accounts; install from the internal testing
link; purchases are free test orders.
@ -41,7 +44,8 @@ Auto-renewable subscriptions are a separate list (`subscriptions`, `store-subscr
## App Store (API + UI)
1. `storebound apple iap --dry`, then `storebound apple iap`: creates missing products, localizations, a price schedule
(USA base + overrides; Apple equalizes the rest) and availability. Existing schedules and availability are never
changed by it; price changes go through `storebound prices import` (diff first, then the owner confirms).
changed by it; price changes go through `storebound prices import` (diff first, then the owner's yes and
`--allow-price-changes`).
2. Review screenshot per product (a real device size, e.g. 1290 x 2796, showing the item in the shop):
`storebound apple iap-review`. States move to READY_TO_SUBMIT when complete.
3. **Send them to App Review** (the owner's step): `storebound apple iap-submit --dry` shows the plan: which version
@ -74,7 +78,9 @@ App Store only, a future YYYY-MM-DD).
neighbours); Play takes any price in the region's currency.
3. `storebound prices import --dry` (MCP: `prices_diff`) prints the diff table: product, store, region, from, current,
new, change %. Show it to the owner.
4. The owner runs `storebound prices import` (asks first; not an MCP tool). Apple: one new price schedule per changed
4. After the owner's yes for that diff: `storebound prices import --yes --allow-price-changes` (not an MCP tool;
`--yes` alone only sets first prices of products that have none and exits 3 naming every held price change).
Apple: one new price schedule per changed
product (base territory, default the current one or `prices.baseTerritory`, plus every manual price; territories
without one follow Apple's equalization). Play: the purchase option's regional prices are replaced; regions the
product has no price for get `convertRegionPrices` from the USD base, `--reconvert` resets every region the CSV

View file

@ -34,8 +34,10 @@ offers, offerCodes, reviewScreenshot, basePlanId, gracePeriod, platforms }] }]`,
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`; 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.
yourself (`--yes`): new subscriptions, first prices, listings, base plans and offers are written. A price change
of an existing subscription is a live price and `--yes` never applies it: the run writes everything else, prints
`price change on <id> (<from> -> <to>) needs the owner: re-run with --allow-price-changes` and exits 3. After the
owner's yes in chat for that diff, run it again with `--yes --allow-price-changes`. Flows never pass that flag.
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