# Changelog ## Unreleased ## 0.8.20 · 2026-10-20 - **Tooling: the desktop updater signatures of the Linux and Windows installers are made on the VDS.** The release no longer downloads them to the Mac: the VDS fetches them from the Forgejo release, checks their SHA-256 against the candidate, signs them with its copy of the updater key and sends back only the .sig texts, which the Mac verifies against the app's public key before publishing. `desktop-release.ts vds-key` installs or rotates that key; `release.ts --sign-local` keeps the old path. ## 0.8.19 · 2026-10-20 - **`shots capture` / `render` find Playwright's chromium in a project's CommonJS playwright** whether it comes on the namespace or under `default`; when the module has no `chromium.launch`, the error names the resolved file and its exports (was "Cannot read properties of undefined (reading 'launch')"). - **Heavy app-style headlines again:** `shots fonts` downloads the weights the templates use (the full `wght` axis of a variable Google family, else the used range, else the static weights), so a family like Plus Jakarta Sans no longer falls back to its thin 400 instance. `shots fonts` warns about weights a family or a local file (`{ weight }`) lacks; `shots check` warns per image when a title was drawn at another weight (`font-weight`). - **palette.bg as a string:** one colour (flat) or a CSS gradient string now renders as given instead of a white background; anything else fails config validation ("palette.bg must be a colour, a gradient or an array of 2-3 colours"). `shots check` warns when the title ink has low contrast against the pixels behind it (`contrast`). - **Photo backgrounds:** `palette.image` (+ `overlay`) and `shots.feature.background` put a cover-cropped photo under a dark overlay (the Play feature graphic, optionally the screenshots); `shots check` checks the file and its size. ## 0.8.18 · 2026-10-20 - **No more "Reparsing as ES module" warning** for a `.ts` config in a CommonJS project (Node's MODULE_TYPELESS_PACKAGE_JSON); the app does not need `"type": "module"`. - **Capture sign-in keeps App Review's account apart:** the docs use `captureAuth` for a local demo sign-in, and `check` notes a capture against localhost that uses `reviewDemoAccount` (the account `apple review-details` sends). ## 0.8.17 · 2026-10-20 - `shots capture --source web` can sign in first: `shots.capture.auth` is a sign-in request (run once per browser context, cookies kept; body and headers use `${cred:NAME}` from `.storebound/credentials.json` or `${env:NAME}`, literal secrets are refused, a failed status stops with the status only) or a `storageState` file. `storebound shots login` opens a fresh visible browser for a demo sign-in by hand and saves `.storebound/auth.json` (0600, counts only). A shot that lands on a sign-in page warns "session expired? run storebound shots login again". Secrets and cookie values are masked in logs and the capture report. ## 0.8.16 · 2026-10-20 - `apps folders add []` adds the folder (it listed and added nothing); a template config is added with a note, a folder without a config is refused with the reason and the fix (pitfall 81). - MCP: the plugin's `.mcp.json` no longer spawns a plugin path that may be gone (ENOENT, CONNECTION_CLOSED): it falls back to `storebound` on PATH, then `npx -y storebound@latest`; `mcp` and `serve` skip the self-update (pitfall 82). - Store build minimums in one dated table (`cli/lib/store-minimums.ts`): `play upload` / `play release` refuse an AAB below Play's target API level (36 since 2026-08-31) and `apple upload` an IPA built below Xcode 26 / the 26 SDKs (since 2026-04-28), with the file to change; `scan` / `validate` report the same (`play-target-sdk`, `apple-min-sdk`) (pitfall 83). ## 0.8.15 · 2026-10-19 - **Mac App Store screenshots.** `apple.mac: true` adds the `mac` size (2880 x 1800; `shots check` takes exactly Apple's four 16:10 sizes, no alpha) and `apple screenshots --platform macos` uploads it as `APP_DESKTOP` to the macOS version. `validate` notes a macOS version without Mac screenshots. Art pull maps `APP_DESKTOP` to `mac/`. - **Landscape layout** in both templates: copy column plus the frame in a Mac window or bare (`shots.landscape`), for Mac, Play Chromebook (`play.chromebook`; no API type, `play images` links the console) and, with `shots.landscape.playTablets`, Play's 7" / 10" tablet slots. - **Safe margins:** text and logo keep 7 % of the width left / right and 5 % of the height at the top in every template (feature graphics too); `shots check` fails boxes under 6 % / 4 %, measured from the render's layout data. - **Contact sheet:** the `sheet.png` wait scales with the images, sizes and machine load, with a heartbeat, instead of a flat 30 s. - iPhone Duo skip message says plainly that the API has no Duo type and names the console page. ## 0.8.14 · 2026-10-19 - **Header and Search Results: upload, add for review, place once approved.** `storebound apple creative-assets` uploads each file once into the Asset Library (MD5 upsert: a rejected file or one that failed processing twice is not uploaded again, an archived copy is unarchived), and places it on the version as soon as Apple approved it: the version page's slots offer only approved assets. Until then every locale and placement says "uploaded , waiting for Apple's approval; run `storebound apple creative-assets` again later", and a re-run places it (an approved old asset stays shown meanwhile). `--add-for-review` adds the unreviewed assets to the app's draft review submission (never submits; Submit for Review stays the owner's); `--verify` (MCP `apple_creative_assets_verify`) reads every slot back read-only with the same waiting state, and `validate` / the report list waiting files. A live app without an editable version uses the live version; if App Store Connect refuses a placement there, the run says so, leaves the live page unchanged (the new placement goes first, the old one is removed only after Apple accepted it), links the live version page and does not retry the other live slots. Art without words needs no copy per locale: `/creative/header.png` / `search.*` fill every language without its own file, uploaded once and placed per locale (the dry run counts files, not slots). Pitfall 76 rewritten. - **iPhone Duo.** Apple's published App Store Connect API spec (re-checked: still the 2026-10-06 file) has no Duo screenshot or preview type, so `apple screenshots` keeps skipping the Duo sets with the console link; the check / validate note now says the console takes Duo and the API does not yet. - **macOS (tvOS, visionOS) versions are no longer invisible.** `apple status`, `listing`, `listing pull`, `resubmit`, `submit`, `release` and `screenshots` take `--platform ios|macos|tvos|visionos` (MCP tools and the desktop's owner actions: `platform`); without it they use the platform that has the version they need (a rejected one, an UNRESOLVED_ISSUES submission, an editable one) and refuse, naming both, when several platforms do. Every version and review submission lookup is per platform, so `resubmit` finds the macOS submission back from App Review and `listing --version 0.8.7` works on the rejected macOS 0.8.7 while the iOS 0.8.7 is live. `apple status`, `validate`, `release status`, the report and the desktop label every version with its platform; validate and release status warn about another platform's editable or rejected version. `whats-new apply --platform` names it in the push command. Pitfall 79. - **Other platforms in store text are refused (both stores).** One checker (`cli/lib/cross-store.ts`) finds Android, Google Play (Games), Play Store, Galaxy Store, Amazon Appstore, Steam, Microsoft Store, AppGallery (and localized forms) in App Store text (Guideline 2.3.10) and iOS, iPhone, iPad, App Store, Apple Arcade, Game Center, TestFlight in Play text (Metadata policy), per locale, field and line with a fix. `apple listing`, `play listing`, `play release` / `play upload` release notes, `listing set`, `whats-new check` / `apply`, `storebound edit` and the desktop editor refuse before anything is sent (dry runs show the hits); `check` and `validate` report blockers; `scan` reports `other-platforms` (2.3.10) and `play-other-platforms` (Play: Metadata). A functional mention stays only through the owner's `listing.allowPlatformMentions: [{ store, text, reason }]`. - **Per-store release notes.** Lines tagged `[apple]` / `[play]` (or a `[play]` line heading a block) in a shared notes source go only into that store's notes; the draft leaves untagged other-platform lines out of the wrong store and says so; each store's limit counts its own notes. - **`storebound api` writes piped or redirected are refused loudly.** `--yes` with stdout not a TTY now exits 1 with "refused: writes through `storebound api` need an interactive terminal ... Nothing was sent." on stderr instead of a silent dry run. ## 0.8.13 · 2026-10-18 - **iPhone Duo screenshots.** `apple.iphoneDuo: true` adds the foldable's outer (1398 x 2034) and inner (2007 x 2853) sets, portrait or landscape, to `shots render`, `shots check` (exact sizes, no alpha) and the gallery. App Store Connect API 4.5.1 has no Duo `ScreenshotDisplayType`, so `apple screenshots` skips them with a message and the deep link to the version page's App Previews and Screenshots tab (one constant, `IPHONE_DUO_DISPLAY`, takes the enum names once Apple adds them). `check` / `validate` note a missing Duo set: optional now, required from April 2027 for iOS 27.1 SDK builds (a warning after that date, never a blocker), and point Play to the tablet sets (no foldable slot). - **Required iPhone class follows the console.** `validate` now requires "iPhone with Dynamic Island (medium display)" (`APP_IPHONE_61`) per locale, met by a 6.9" or 6.5" set that App Store Connect scales down (an ok row, not a warning). Labels match the console: "iPhone with Dynamic Island (medium display)", "iPhone Duo", "iPad 13" display". - **App Store Header and Search Results.** `storebound apple creative-assets [--dry]` puts a product page header (3840 x 1646, 21:9) and a search results asset (3:2, 1920 x 1280 to 3840 x 2560), image or video (5-30 s, 30/60 fps), per locale from `//creative/`, or one universal 5244 x 2950 PNG, into the App Asset Library and selects them (`PRODUCT_PAGE_HEADER_ASSET`, `APP_STORE_SEARCH_RESULTS_ASSET`) once Apple approved them; MD5 upsert, read back. Local checks: size, no alpha, video length / fps / container (ffprobe, else skipped with a note). `check` and `validate` note when there is none (Play: the feature graphic and promo video are the counterparts). MCP `apple_creative_assets`; config `apple.creativeAssets`; pitfall 76. ## 0.8.12 · 2026-10-17 - **Shareable readiness report.** `storebound report` puts `scan`, `check` and `validate` into one self-contained HTML page (`.storebound/reports/readiness-.html`, inline CSS, no scripts, light / dark, print-friendly) and opens it in the browser: a verdict per store ("Ready to submit", "N blockers", "Not checked" with the reason when a store key is missing), findings per store by severity with guideline, evidence, fix and an Open in App Store Connect / Play Console button, and the questions only the owner can answer as a checklist. Credentials, key ids, emails and the review demo account are masked. `--md`, `--json`, `--no-open`, `--offline`, `--out`. The desktop's Checks tab has a Report button (same code path) and its Markdown export uses the same renderer; MCP gets a read-only `report` tool. - **Remote config and remote paywalls (App Review 2.3.1, Play Deceptive behavior).** `storebound scan` (and `validate`; `check` as a warning) reports `remote-features` for both stores when the app ships a remote config / feature flag SDK (Firebase Remote Config, Statsig, LaunchDarkly, GrowthBook, Optimizely, PostHog feature flags, Unleash, ConfigCat, Amplitude Experiment, Split) or a remote paywall (RevenueCat Paywalls, Superwall, Adapty, Qonversion, Purchasely, Apphud) and the review notes do not mention it. Warn only: storebound never writes the note; the config templates carry a commented example line. Code obfuscators are a low note, minify alone nothing (store-pitfalls 74). ## 0.8.11 · 2026-10-17 - **Guest-play apps rejected as a login wall (Play).** `reviewAccess.instructions` with `signInRequired: false` holds guest steps: `storebound check` warns when a Play app has a first-run gate (name entry, code / transfer entry) or in-app purchases and no steps, and the `play-sign-in-details` plan then declares one entry without credentials, the dialog's required full-access box as the owner's step (store-pitfalls 73). ## 0.8.10 · 2026-10-15 - **Neutral names only.** Docs, pitfalls, tests and comments use example names instead of the projects that reported issues; the history was rewritten the same way. Older npm versions are withdrawn. - **Releases:** a candidate whose build jobs were cancelled by a newer push says so and gives the tag move and `--resume` line. ## 0.8.9 · 2026-10-15 - **Play edits Play will not send for review are committed anyway.** After a rejection, with other pending changes or managed publishing, Play answers validate / commit with 400 "Changes cannot be sent for review automatically. Please set the query parameter changesNotSentForReview to true". storebound now commits the same edit again (edits.commit only: validate refuses the parameter with 400 "Unknown name") with that parameter (a dry run validates that way and says the real run will commit so), then prints "committed; Play did not send it for review: press Send changes for review in Publishing overview: " (the owner's button). `--not-sent-for-review` on `play release`, `upload`, `rollout`, `listing` and `images` (MCP / desktop `not_sent_for_review`) sets it from the start. The API cannot read that changes wait to be sent, so storebound records its commit in `.storebound/` and `play status` shows "changes committed