Screenshot guardrails: name every screenshot not COMPLETE on 409 ASSET_IN_POST_PROCESSING (submit, validate; stuck after 5 min in UPLOAD_COMPLETE), apple screenshots --repair-stuck replaces only those in place, part uploads without Expect: 100-continue (failed part deletes the reservation, one retry, never committed), storebound api --confirm <operationId> for agents' plain writes (never owner-only)

This commit is contained in:
CRTkafa 2026-10-21 12:40:26 +07:00
parent 164182012f
commit 00756e6908
19 changed files with 475 additions and 45 deletions

View file

@ -40,6 +40,9 @@ storebound api apple salesReports_getCollection --param ... --out report.gz #
whatever the flags. `--yes` with stdout not a TTY (piped `| tail`, redirected `> out.json`, an agent's shell) is
refused: exit 1 and "refused: writes through `storebound api` need an interactive terminal ... Nothing was sent."
on stderr (store-pitfalls 78). Never pipe or redirect a write; use the dedicated command the message names.
An agent without a terminal sends a plain (non-owner) write with `--yes --confirm <operationId>`, the id equal
to the one the call resolved to (store-pitfalls 92), after showing the dry run. `--confirm` never applies to
owner-only operations.
3. Owner-only operations (cli/api/gate.ts: every DELETE, submissions and their cancel, release requests, phased
release changes, product page test start / stop, price schedules and price migrations, review replies, tester and
user invitations, Play `edits.commit`, product activation, refunds, revokes and cancels, app signing) also ask for

View file

@ -1113,3 +1113,26 @@ that fails without the fix (see CONTRIBUTING.md).
check `apple status` for the draft's state before re-running anything else.
- **Guard:** `willSubmit` in `cli/lib/review-submit.ts` (a non-empty READY_FOR_REVIEW draft that holds the named app
version, or for `iap-submit` an asked-for item, is submitted); `tests/resubmit.test.ts`.
## 91. Submit refused: 409 STATE_ERROR.ASSET_IN_POST_PROCESSING.APP_SCREENSHOT (screenshots stuck in UPLOAD_COMPLETE)
- **Symptom:** `apple submit` answered 409 `ASSET_IN_POST_PROCESSING.APP_SCREENSHOT` for hours. A few screenshots
sat in UPLOAD_COMPLETE and never became COMPLETE: their part upload had gone out with `Expect: 100-continue`, got
a non-2xx answer, and the upload was committed anyway.
- **Do:** `storebound validate` (or the failed submit) now lists every screenshot that is not COMPLETE: locale,
display type, position, file name, state and age. UPLOAD_COMPLETE for over 5 minutes is **stuck**, not processing
(a blocker); younger ones are a warning, wait. Then `storebound apple screenshots --repair-stuck --dry`, then
without `--dry`: only the stuck / FAILED / AWAITING_UPLOAD ones are deleted, the same local file (same name in the
renders) uploaded again and put back at its position; the set count is checked afterwards. Re-runnable.
- **Guard:** `cli/apple/screenshot-health.ts` (`pendingScreenshots`, `explain409`, `repairPlan`, `repairSet`,
ledger `.storebound/screenshot-states.json` for the UPLOAD_COMPLETE age), `uploadAssetVia` in `cli/apple/asc.ts`
(parts without `Expect`, any non-2xx part is a failure, the AWAITING_UPLOAD reservation is deleted and the upload
tried once more, never committed after a failed part), `tests/screenshot-health.test.ts`.
## 92. Agents could not send any `storebound api` write (no TTY)
- **Symptom:** an agent's shell has no TTY, so every write through `storebound api` was refused (pitfall 78), even
a harmless one the owner had already agreed to, and the owner was asked to run it by hand.
- **Do:** show the dry run, then `storebound api apple|play <operationId> ... --yes --confirm <operationId>`. The
`--confirm` value must equal the operation id the call resolved to (also with `METHOD path`), or nothing is sent.
Owner-only operations (submit, release, delete, prices, testers, replies, ...) refuse `--confirm`: the owner
still types the id in a terminal. MCP and the desktop app still never send writes.
- **Guard:** `decide` / `OWNER_NO_CONFIRM` in `cli/api/gate.ts`, `tests/api.test.ts`.