console: Play App content card Saves are the agent's (declaration-save; Send changes for review, Submit, Publish, Restart review stay forbidden), one owner batch per plan with the IARC Terms tick before the run, console plan play-app-content --all, New App fields typed not set, play.appId from the Create app redirect (console verify --url --write) and storebound play app-id across app list pages

This commit is contained in:
CRTkafa 2026-10-13 05:43:56 +08:00
parent 1366e7e231
commit f741c15829
29 changed files with 1029 additions and 165 deletions

View file

@ -9,6 +9,10 @@ fails without the fix.
| # | date | lesson | guard |
|---|---|---|---|
| 41 | 2026-10 | Every Play App content card ended in an owner-only Save, so the owner came back once per card (privacy policy, ads, app access, advertising ID, government, financial, health, target audience, content rating, data safety). Saving an App content declaration is not public: it goes to Google only with the next "Send changes for review" in Publishing overview (verified 2026-10-13). | Card Saves are the agent's (`declaration-save`): `declarationSaveAllowed` (exactly Save / Kaydet, a button on an /app-content/<card> page) and `flowAllowed` for content rating and target audience, while `publishingControl` refuses "Send changes for review", "Submit N changes for review", Submit, Publish, "Restart review" everywhere; the plans' `buttons.final` is "Send changes for review"; `console fill` presses the declaration's Save; `console plan play-app-content --all` walks every card and ends with one owner list (`cli/lib/console-forms.ts`, `cli/lib/console-plan.ts`, `cli/lib/console-play-plans.ts`, `cli/console/fill.ts`; `tests/console-play-plans.test.ts`, `tests/console-flow.test.ts`). |
| 40 | 2026-10 | Finding a new app's Play Console app id (`play.appId`) took several steps: the app list shows 10 apps per page and the new app was not on page 1. The id was in the URL Create app redirects to (/developers/<devId>/app/<appId>/app-dashboard) all along. | `console verify play-app-record --url <url>` reads the id from the redirect (or the capture JSON's url), shows the `play.appId` change as a dry diff and `--write` upserts it (`upsertNestedString`); `storebound play app-id` finds it by package name across captured app list pages (search box or pages) as an agent step (`cli/lib/play-app-id.ts`, `cli/play/app-id.ts`; `tests/play-app-id.test.ts`, a modeled 3-page list). |
| 39 | 2026-10 | Play IARC questionnaire: page 1's Next did nothing until the IARC Terms of Use box was ticked, an owner-only agreement, so the agent stopped mid-run and waited for the owner. | The box is in the plan's ONE owner batch before the run with its exact place (`ownerBatch`, printed first; `play-app-content` and `play-content-rating`); the step says not to press Next again and again; `console fill` waits for the tick ("agreement needed on …", `blocksNext`) instead of pressing a Next that does nothing (`tests/console-play-plans.test.ts`, `tests/console-flow.test.ts`). |
| 38 | 2026-10 | App Store Connect's New App dialog: a programmatic value set (a form_input-style set) on the Name and SKU textboxes left the dialog's React state empty (""), while clicking the field and typing worked. | The `apple-app-record` plan says "type, don't set value" (uiFacts and the fill step); `console fill` types every text field key by key and reads it back (`typeText` in `cli/console/fill.ts`); `tests/console-flow.test.ts` (a New App fixture whose state follows key presses only). |
| 37 | 2026-10 | A `scan.ignore` entry whose `match` text was in the evidence (a code line) instead of the finding title accepted nothing and said nothing. `init --profile game` wrote example-looking values into `.storebound/credentials.json` (`YOUR_KEY_ID`, `/absolute/path/to/upload.p12`, `sk_your_revenuecat_v2_secret_key`), which commands read as real credentials. | `scan.ignore[].evidence` matches an evidence line ("file:line text"), `match` stays the title; `scan` (report notes) and `check` (warning) name every entry that accepts no finding (`ignoreMatches` / `applyIgnore` / `unusedIgnores` in `cli/lib/scan/rules.ts`). `templates/credentials.example.json` holds empty strings only; `isPlaceholder` (`cli/lib/credentials.ts`) makes "your-…", `<…>`, "xxx", "/absolute/path/to/…", "example" and the zero Apple Ads client id missing in every credential read; `check` warns, `doctor` shows set / missing / placeholder per key. `tests/game-report.test.ts`. |
| 36 | 2026-10 | Privacy answers derived from the wrong source: `privacy-manifest --propose` wrote an EMPTY NSPrivacyCollectedDataTypes while `appPrivacy.types` listed 5 collected types; `console plan apple-app-privacy` added "Other Usage Data" from dataSafety (PSL_OTHER_APP_ACTIVITY) although `appPrivacy.types` was given; `console plan play-advertising-id` answered "uses advertising ID" Yes because dataSafety declared PSL_DEVICE_ID (a random install id, not the advertising ID). | `--propose` builds NSPrivacyCollectedDataTypes from `appPrivacy.types` (types, linked, tracking, purposes as Apple's manifest keys; NSPrivacyTracking still only with app-level domains, lesson 29) and `check` warns on an empty list against collected types. `appPrivacy.types` is authoritative in the App Privacy sheet (unlisted = not collected, certain); without it, tracking false and no ad / attribution SDK make Advertising Data, Customer Support, Environment Scanning, Hands and Head "not collected" unless a project signal suggests them (`cli/lib/project-signals.ts`). Advertising ID comes from ad / attribution SDKs, the AD_ID permission (source or merged manifest, or its `tools:node="remove"`), ads.appAdsTxt or `console.appContent.advertisingId`; none: No (certain), a conflict: ask (`advertisingIdUse`). `tests/game-report.test.ts`. |
| 35 | 2026-10 | `scan` on a Capacitor game: rule `review-prompt` (3.2.2(x), reward for rating) fired on `const rate = F.genRate(lv, bonus.gen(r));` (a bare "rate" next to "bonus"); every account finding cited `import './buildings/register';` as sign-up evidence; and 5.1.1(v) cleared as soon as `accounts.inApp` named a screen the code did not have. | The incentive signal needs a rate-us / leave-a-review phrase next to a reward, a reward "for rating / reviewing", or a reward granted within a few lines of a system review prompt call (`rewardNearReviewApi`). Sign-in / sign-up / deletion signals skip import, require and export-from lines (`codeLine`), and auth endpoints must be an absolute path, URL or `${base}/...`, never a module path; config-only accounts say "from config (accounts.enabled)". A config claim of in-app deletion without code evidence stays a finding (medium, "claimed by config, not found in code"). `tests/game-report.test.ts` with `tests/fixtures/scan/capacitor-game`. |