store-only apps edit their listing on the store (storebound listing live / set: old -> new per field with the checks, a PATCH of only the edited fields; serve listing.set is the one store-only write, its token bound to the store ids; policy.rs STORE_ONLY_WRITES, write_with_confirm for store targets); screenshot captions per language and scene edited as a text edit of the shots copy JSON (JSON files keep their layout); Play content rating, target audience and App content answers in App info; docs/console-parity.md after the editors (73 rows: 55 complete, 18 with the reason) and tests/console-parity.test.ts keeping it honest

This commit is contained in:
CRTkafa 2026-10-14 00:53:38 +08:00
parent 3cea2af4e1
commit 7ac028421a
33 changed files with 852 additions and 157 deletions

View file

@ -7,6 +7,7 @@ import { type Account, type AccountCheck, type ConsoleFillResult, type ConsoleFo
type PortfolioResult, type Project, type ProjectSummary, type Settings, type StoreApp, type StoreRelease, type Studio, type ToolResult } from '../api';
import { buildApps, type Overrides } from '../lib/identity';
import type { CatalogDiff, HealthSnapshot, MetricRow, Review, TestFlightStatus, ValidateResult, ScanResult } from '../lib/model';
import { editableFixture, liveFixture } from './editor-fixtures';
import { APPLE_STATUS_READY, APPLE_STATUS_UNRESOLVED, consolePlan, consoleVerify, ownerDry, PLAY_STATUS } from './submit-fixtures';
import { ANALYTICS, ASO, DECLARATIONS, LISTING_TEXT, PPO, PPO_STORE, PRICE_DIFF, PRICES, renderArt, RENDERS } from './fixtures-more';
@ -436,12 +437,14 @@ function read(method: string, id: string, args: Record<string, unknown> = {}): T
case 'reviews.list': return ok(method, STORE_REVIEWS.filter(r => (play || r.store === 'apple') && (/ios:/.test(id) || r.store === 'play')));
case 'apple.testflight_status': return ok(method, TF['p-tidy']);
case 'apple.ppo_status': return ok(method, /ios:/.test(id) ? PPO_STORE : { tests: [] });
case 'listing.live': return ok(method, liveFixture(id));
default: return { method, ok: false, exitCode: 1, dryRun: false, command: `storebound ${method}`, output: `fixture: ${method} needs an app folder` };
}
}
switch (method) {
case 'project.icon': return ok(method, { path: SUMMARY[id]?.icon ?? null, dataUrl: ICONS[id] ?? null });
case 'project.listings': return ok(method, LISTINGS[id]);
case 'project.editable': return ok(method, editableFixture(SUMMARY[id]));
case 'reviews.drafts': return ok(method, DRAFTS[id]);
case 'health': return ok(method, HEALTH[id]);
case 'reviews.list': return ok(method, REVIEWS[id]);