docs/console-parity.md: audit of every App Store Connect and Play Console field storebound reads or writes against the desktop (view, edit in place, store write), with the gaps

This commit is contained in:
CRTkafa 2026-10-14 00:09:54 +08:00
parent 7b05e4b396
commit e59f439101
2 changed files with 106 additions and 0 deletions

104
docs/console-parity.md Normal file
View file

@ -0,0 +1,104 @@
# Console parity: App Store Connect and Play Console in the desktop app
The desktop app (apps/desktop, [desktop.md](desktop.md)) is meant to replace App Store Connect and Play Console for
everything storebound can read or write. This page lists every owner-editable thing in the two consoles that the CLI
covers, and what the desktop does with it.
Columns:
- **storebound**: the command that reads or writes it, and where the value lives (the source of truth in the app
folder: `storebound.config.ts`, `store/listing.md`, `store/appstore.md`, `.storebound/`).
- **View**: the desktop shows the current value.
- **Edit**: the desktop edits it in place (validation, unsaved-changes guard, undo) and saves it into the source file.
- **Store write**: the desktop writes it to the store (dry run in the window, native confirm dialog, then the real
run; owner-only steps through the owner-action dialog).
- **Gap**: what is missing, or why a cell stays "no" on purpose.
Status: **audit before the editors (2026-10-14)**. Every row whose CLI can write the value but the desktop cannot edit
it is a gap. Phase 2 closes them; this table is updated with the result.
## App Store Connect
| Console screen | storebound | View | Edit | Store write | Gap |
| --- | --- | --- | --- | --- | --- |
| App Information > Name (per locale) | `apple listing` (`### name`, apple.name, app.name) | counter only | no | yes (`apple.listing`) | no text editor |
| App Information > Subtitle | `apple listing` (`### subtitle`) | counter only | no | yes | no text editor |
| Version > Promotional Text | `apple listing` (`### promotionalText`) | counter only | no | yes | no text editor |
| Version > Description | `apple listing` (`### description`) | counter only | no | yes | no text editor |
| Version > Keywords | `apple listing` (`### keywords`), `check --aso` | counter only | no | yes | no editor, no keyword checks while typing |
| Version > What's New | `apple listing`, `whats-new apply` (`### whatsNew`) | no | no | yes | no editor |
| Version > Support URL | `apple listing` (app.supportUrl) | no | no | yes | not shown, no editor |
| Version > Marketing URL | `apple listing` (app.marketingUrl) | no | no | yes | not shown, no editor |
| App Privacy > Privacy Policy URL | `apple listing` (app.privacyPolicyUrl) | no | no | yes | not shown, no editor |
| Version > Copyright | `apple listing` (app.copyright) | no | no | yes | not shown, no editor |
| Version > version string, build | `apple listing --version --build` | yes | version field | yes | none |
| Version > Screenshots (per device, locale) | `shots render`, `apple screenshots` | renders | no | yes (`apple.screenshots`) | no ordering or replacement of scenes |
| Version > App Previews | `shots video`, `apple previews` | renders | no | yes | no replacement |
| App Information > Category, subcategories | `apple appinfo` (apple.info.categories) | yes | no | yes (`apple.appinfo`) | no editor |
| App Information > Age Rating | `apple appinfo` (apple.info.ageRating), console plan | yes | no | yes | no editor of the answers |
| App Information > Content Rights | `apple appinfo` (apple.info.contentRights) | yes | no | yes | no editor |
| Pricing and Availability > Free, territories | `apple appinfo` (apple.info.free) | yes | no | yes | no editor |
| Pricing and Availability > Pre-orders | `apple preorder` | no | no | terminal (owner) | public step; terminal only |
| App Privacy (data types, purposes) | appPrivacy.types, `console fill apple-app-privacy` | yes | no | no API (console fill) | no editor of the answers |
| App Review Information > Contact | `apple review-details` (.storebound/credentials.json reviewContact) | masked | no | yes (`apple.review_details`) | no editor |
| App Review Information > Notes | `apple review-details` (apple.info.reviewNotes, reviewAccess.instructions) | yes | no | yes | no editor |
| App Review Information > Sign-in required, demo account | reviewAccess.signInRequired, credentials.json reviewDemoAccount | masked | no | yes | no editor of sign-in required |
| Export compliance (encryption) | `apple encryption` (apple.usesNonExemptEncryption) | yes | no | yes | no editor |
| In-App Purchases > reference name, display name, description per locale | `apple iap` (products[]) | parity only | no | yes (`apple.iap`) | no editor |
| In-App Purchases > price | products[].usd, overrides, `prices import` | yes (Prices) | no | yes, owner gate | no editor of the base price |
| In-App Purchases > review note, screenshot | products[].reviewNote / reviewScreenshot, `apple iap-review` | no | no | yes (`apple.iap_review`) | not shown, no editor |
| Subscriptions > group name per locale | `apple subscriptions` (subscriptions[].listings) | no | no | yes | no editor |
| Subscriptions > name, description per locale | `apple subscriptions` (subscriptions[].subscriptions[].listings) | parity only | no | yes | no editor |
| Subscriptions > level, period, price | `apple subscriptions` | parity only | no | yes, owner gate for live prices | no editor |
| Subscriptions > introductory and promotional offers | subscriptions[].subscriptions[].intro / offers | no | no | yes (sync) | no editor |
| Subscriptions > offer codes | `apple subscriptions` (offerCodes), `apple offer-codes` | no | no | terminal (owner) | no editor |
| Subscriptions > win-back offers | `apple win-back` (winBack) | no | no | terminal (owner) | no editor |
| In-App Purchases > promoted purchases | `apple promoted` (apple.promoted) | no | no | terminal (owner) | no editor |
| Custom product pages | `apple cpp list / create / upload` (apple.customProductPages) | no | no | CLI only | not in the window |
| In-app events | `apple events list / create / upload` (apple.events) | no | no | CLI only | not in the window |
| Product Page Optimization | `apple ppo` | yes | create / start / stop | yes (owner start / stop) | none |
| TestFlight > What to Test (per build) | `apple testflight what-to-test` | yes | yes (text) | yes | per-locale text not editable |
| TestFlight > groups | `apple testflight groups sync` (apple.testflight.groups) | yes | no | yes (create missing) | no group editor |
| TestFlight > Test Information (beta description, notes) | `apple testflight info` (apple.testflight.description / reviewNotes) | no | no | CLI only | not in the window |
| TestFlight > testers | `owner.testflight_testers` (.storebound/testers.csv) | count | no | yes (owner) | CSV not editable |
| Ratings and Reviews > reply | `reviews draft / reply / approve / send` | yes | yes | yes (owner send) | none |
| Version > phased release | `owner.ios_phased` | yes | n/a | yes (owner) | none |
| Version > Submit for review | `owner.apple_submit` / `owner.apple_resubmit` | yes | n/a | yes (owner) | none |
## Google Play Console
| Console screen | storebound | View | Edit | Store write | Gap |
| --- | --- | --- | --- | --- | --- |
| Main store listing > App name (per language) | `play listing` (`### title`, app.name) | counter only | no | yes (`play.listing`) | no text editor |
| Main store listing > Short description | `play listing` (`### short`) | counter only | no | yes | no text editor |
| Main store listing > Full description | `play listing` (`### full`) | counter only | no | yes | no text editor, no stuffing check while typing |
| Release > Release notes (per language) | `play release`, `whats-new apply` (`### What's new`) | counter only | no | with the release (owner) | no editor |
| Main store listing > Graphics (icon, feature graphic, screenshots) | `icons`, `shots render`, `play images` | renders | no | yes (`play.images`) | no ordering or replacement of scenes |
| Main store listing > Video (YouTube URL) | `play listing` (play.video) | no | no | yes | not shown, no editor |
| Store settings > Contact email | `play listing` app details (app.contactEmail) | no | no | yes | not shown, no editor |
| Store settings > Website | `play listing` app details (app.website) | no | no | yes | not shown, no editor |
| Store settings > App category | play.category, console plan (no API) | yes | no | no API | no editor |
| App content > Privacy policy | app.privacyPolicyUrl, console plan | no | no | no API | not shown, no editor |
| App content > Data safety | `play data-safety` (dataSafety) | yes | no | yes (`play.data_safety`) | no editor of the answers |
| App content > Content rating (IARC) | social, products[].randomRewards, `console fill play-content-rating` | yes | no | no API (console fill) | no editor of the answers |
| App content > Target audience | play.targetAges, `console fill play-target-audience` | plan | no | no API (console fill) | no editor |
| Monetize > In-app products: title, description per language | `play products` (products[].listings) | parity only | no | yes (`play.products`) | no editor |
| Monetize > In-app products: price | products[].usd / overrides, `prices import` | yes (Prices) | no | yes, owner gate | no editor of the base price |
| Monetize > Subscriptions: title, benefits, description per language | `play subscriptions` | parity only | no | yes | no editor |
| Monetize > Subscriptions: base plans, offers, price | `play subscriptions` | parity only | no | yes, owner gate | no editor |
| Pricing > regional prices (price sheet) | `prices export / import` | yes | no | yes (owner) | sheet not editable in place |
| Production > Countries / regions | play.countries, console plan | count | no | no API | no editor |
| Publishing overview > Managed publishing | play.managedPublishing | yes | no | no API | no editor |
| Testing tracks (internal, closed, open) | `play release --track` | status | n/a | terminal (owner) | sends builds to testers: not in the window by design |
| Production > staged rollout | `owner.play_rollout` | yes | n/a | yes (owner) | none |
| Production > Send to production | `owner.play_production` | yes | n/a | yes (owner) | none |
| Ratings and reviews > reply | `reviews draft / reply / approve / send` | yes | yes | yes (owner send) | none |
| Internal app sharing | `play share` | no | n/a | CLI only | not in the window |
## Both stores
| What | storebound | View | Edit | Store write | Gap |
| --- | --- | --- | --- | --- | --- |
| Pull the live texts into the files | `apple listing pull`, `play listing pull`, `catalog pull` | no | n/a | n/a (writes local files) | not in the window |
| Store-only app (no folder): listing text | (none) | yes (status) | no | no | store-only apps are read-only in the window |
| Uncommitted changes in the app folder | git | no | n/a | n/a | not shown |

View file

@ -17,6 +17,8 @@ development; this page is for contributors.
- **Catalog parity.** Products (and subscriptions, once the config has them) are defined once; `portfolio.status`
compares the config with both stores (ids missing on a store, ids on one store only).
Console parity (what the window can view, edit and write for each console screen): [console-parity.md](console-parity.md).
Types of the protocol: `cli/lib/desktop-protocol.ts`; pure portfolio logic: `cli/lib/portfolio.ts`.
## Safety model