A real `console fill apple-app-privacy` clicked Next onto a console page the engine did not recognise and logged nothing for 8 hours. - Step watchdog: after a click, or while the form is expected, no known page within --step-timeout seconds (default 30) -> "the page after "Next" is not a page storebound knows ... Buttons there: ...", a debug dump, and the manual steps. It keeps watching, but 20 min without progress closes the window and exits 1. - A heartbeat line every minute while waiting (also during sign-in); the final review wait is bounded (2 h) with a reminder every 10 min. - Debug dump (cli/console/debug.ts) in .storebound/console-debug/ <form>-<time>/: page.json (URL without query, headings, dialogs, every visible control with role and name), page.html (rebuilt from a clone: no scripts, values, hidden inputs or non-whitelisted attributes) and a small screenshot. Emails, phone numbers and the header / account area are replaced; cookies, storage and tokens are never read. --debug-dump writes one at every step for bug reports. - Guardrail tests: an unknown-page fixture (dump sanitized, bounded exit) and a scan of cli/console for while loops without a deadline and Playwright waits without a timeout (one waitForLoadState fixed). - Pitfall 14 (docs/pitfalls.md, store-pitfalls skill); the console-autofill skill says how to send a dump.
5.7 KiB
5.7 KiB
Pitfalls: lessons from real use
A changelog of mistakes found while shipping real apps and games with storebound, newest first. Each entry says what
went wrong, why, and what now guards it. The agent-facing short version is skills/store-pitfalls/SKILL.md; keep the
two in step.
Rule (CONTRIBUTING.md): every bug found in real use adds an entry here and, where possible, a check or a test that fails without the fix.
| # | date | lesson | guard |
|---|---|---|---|
| 14 | 2026-10 | console fill apple-app-privacy clicked Next onto a real console page it did not recognise and then waited 8 hours without a single log line. |
Every wait of the console engine is bounded: a step watchdog (--step-timeout, 30 s) writes a sanitized debug dump to .storebound/console-debug/ and prints the page's buttons and the manual steps; a line a minute while waiting; no progress for 20 min stops the run with exit 1; --debug-dump dumps every step (cli/console/debug.ts, tests/console-flow.test.ts, which also scans the engine for waits without a deadline). |
| 13 | 2026-10 | App Privacy purposes were over-mapped: Play "advertising" became both Third-Party Advertising and Developer's Advertising or Marketing (Device ID, Coarse Location, Product Interaction, Performance Data). | applePurpose maps one Play purpose to one Apple purpose (advertising: third-party only); Developer's Advertising only from appPrivacy.types; check compares appPrivacy.types with Info.plist / PrivacyInfo.xcprivacy (tests/app-privacy.test.ts). |
| 12 | 2026-10 | A new Play app has no API: Play Console's "Create app" is the only way, its page-level button carries the same words as the form's final submit, and Free cannot become Paid later. | console fill play-app-record: createAppAllowed lets the opener through only on the app list, never in a form or dialog (EN + TR); declarations never ticked; Free / Paid outlined as an owner check (tests/console-fill.test.ts). |
| 11 | 2026-10 | Google Play has no public search API, so there is no Play keyword rank or Play competitor list to read without scraping. | aso reads App Store search only (iTunes Search API); aso audit covers Play through the listing text (short description, density); the aso-basics skill says so. |
| 10 | 2026-10 | The iTunes Search API order is close to, not equal to, App Store search (no personalisation, no search ads, no in-app event rows) and it has no subtitle field. | aso rank / aso competitors / aso audit label ranks as a trend and show no competitor subtitles; popularity only from Apple Ads, never estimated (cli/lib/aso-intel.ts). |
| 9 | 2026-10 | A retried or repeated write can duplicate on a live store: a POST create retried after a timeout or 5xx, a re-run during the list lag right after a create, two runs at once, a Play edit redone after an unclear commit, a crash in the middle of a screenshot set. | Every write is an upsert (cli/lib/upsert.ts: findOrCreate with a fresh re-check and a journal of recent creates, createOnce treating 409 as found, no blind retry of a create); API clients retry only requests that are safe twice; Play edits are all or nothing; asset sets keep the matching prefix; one lock per app and store (cli/lib/lock.ts, --force-unlock). Table: docs/idempotency.md; tests/idempotency.test.ts runs every write twice and asserts zero writes on the second run. |
| 8 | 2026-10 | Capacitor plugins in a monorepo live in apps/native/node_modules, so scan missed them. |
scan / privacy-manifest resolve workspace packages (cli/lib/scan/project.ts, cli/lib/node-packages.ts). |
| 7 | 2026-10 | Console UIs: the App Store Connect sign-in runs in an idmsa.apple.com iframe; "User Access" is a plain heading, not a labeled group; Chrome drops session cookies without --restore-last-session. |
console session and fill (cli/console/), tests/console-*.test.ts. |
| 6 | 2026-10 | Screenshot fonts: only part of the glyphs a language needs were available, so Turkish / Polish / Vietnamese characters fell back to another font silently. | shots fonts / shots check check every language's characters against the fonts (tests/glyphs.test.ts). |
| 5 | 2026-10 | Account features do not imply email collection; Data safety said "email collected" without evidence. | scan marks email only with code evidence, else "ask owner" (cli/lib/scan/declare.ts). |
| 4 | 2026-10 | The App Store keyword limit (100) counts UTF-8 bytes, not characters. | check counts bytes (cli/lib/copy.ts). |
| 3 | 2026-10 | App Store app names are unique per storefront: "already being used" although the developer owns no such app. | apple.name (App Store name with a suffix the owner picks); app.name stays the brand. |
| 2 | 2026-10 | Dry runs that print no plan ("listing: 20 locales") break "show the dry run to the owner". | cli/lib/dry.ts dryPlan(title, rows); tests/pitfalls.test.ts scans cli/** for --dry branches that print only a count (allowlist with TODOs for commands being fixed). |
| 1 | 2026-10 | App Store Connect's /inAppPurchasesV2 list lags right after apple iap creates a product; apple iap-review then skipped it silently. |
apple iap-review names config products missing from the store's list and exits 1; apple iap and play products re-read the list after creating and warn (cli/lib/store-lag.ts, tests/pitfalls.test.ts). |
Adding an entry
- A row above (next number, month, one-sentence lesson, the guard with file or test names).
- A section in
skills/store-pitfalls/SKILL.md: symptom → cause → what to do → which command guards it. Thenbun tools/gen-adapters.ts. - Where the code can know it: a check (
check,validate,scan, a warning in the command) and a test with recorded store answers that fails without the fix.