Commit graph

38 commits

Author SHA1 Message Date
CRT
714352ec18 Play edits under a machine-wide per-package lock (parallel reads of one app no longer delete each other's edits; a read whose edit was deleted is redone once, writes are not); a store that failed to read is 'Couldn't read Google Play' and keeps its last good releases with 'couldn't refresh'; stores on different versions: neutral 'Current · Play x · App Store y' header and each lane its own state, 'Not on this store' only without a record; Play status decided from the public store page's live version (Live / In review / In review (live: x) / managed publishing / Rolling out / Halted / Draft; only a network failure says 'Couldn't check the store page', with the last known answer and its time); tests/play-edit-lock.test.ts, recorded page fixture, boot check auto-read cases (pitfall 58) 2026-10-14 00:26:17 +08:00
CRT
f741c15829 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 2026-10-13 05:44:29 +08:00
CRT
1366e7e231 scan, privacy answers and credentials from a second game's first release (lessons 35-37): review-prompt needs rating context, not a bare "rate"; account signals skip import lines and module paths, config-only accounts say "from config"; accounts.inApp without deletion code stays a medium 5.1.1(v) finding; privacy-manifest --propose builds NSPrivacyCollectedDataTypes from appPrivacy.types and check warns on an empty list; appPrivacy.types is authoritative in the App Privacy sheet (unlisted = not collected), ad / support / AR types default to not collected without tracking or ad SDKs; Play advertising ID from SDKs, the AD_ID permission, app-ads.txt or the flag, never from a Device ID alone; scan.ignore[].evidence and warnings for entries that match nothing; empty credentials template, placeholder values count as missing (check, doctor) 2026-10-13 05:39:09 +08:00
CRT
c8213be2b6 ITMS-91064: NSPrivacyTracking and NSPrivacyTrackingDomains must agree in every privacy manifest (true needs domains, domains need true); check, scan and privacy-manifest fail on either mismatch with the exact ITMS text; App Privacy tracking no longer demands the app manifest's true: SDK manifests that declare tracking (Pods, frameworks, SwiftPM, Carthage, plugins, the built IPA) are info, known tracking SDKs info, else a warning; privacy-manifest --propose writes true only with appPrivacy.trackingDomains; apple upload checks every manifest in the IPA (XML or binary) before any network call, apple build after the export. Pitfall 29 2026-10-12 10:27:21 +08:00
CRT
b7bfe89151 Play console steps without an API as agent plans: play-store-settings (App category; play listing cannot set it), play-app-content (every card in order, one owner batch) and its cards (privacy policy, ads, sign in details, advertising ID, government, financial, health), play-production-countries and play-production-release; IARC plan with the Terms of Use agreement, follow-ups (console.contentRating.details, products[].randomRewards, social) and the Summary's final Save; config play.category / countries / managedPublishing, console.appContent; check, validate, play status and play release reminders; inPageLoop helper and material checkbox clicks; App Privacy published signal; first IAPs via apple iap-submit --with-version. Pitfalls 27, 28 2026-10-12 10:16:34 +08:00
CRT
c9a7f095c4 console: agent-first mode (console plan / console verify), verified App Privacy and Play Create app facts
- `storebound console plan <form> [--json] [--lookup]`: read-only plan for an AI agent that drives the browser
  itself: start URL (deep links), ordered steps, answer sheet with the console's exact labels (certain / ask owner,
  curly quotes noted), never-press list, verified UI facts, driving guidance (in-page async loop + window.__log),
  verify instructions. All six forms.
- `storebound console verify <form> [--from-text <file|->]`: App Privacy per-type summaries from page text, Play
  Create app from input states, content rating / target audience summaries, App Store record and age rating from
  the API; exit 1 on a mismatch.
- MCP: console_plan, console_verify (read-only); console fill stays off MCP.
- App Privacy (verified 2026-10-12): two information steps, unlabeled purpose checkboxes (nearest parent text),
  "Definitions and Examples" never pressed; fixture and engine follow.
- Play Create app (verified 2026-10-12): /developers/<id>/create-new-app, App package name + read-only Check
  availability, material-radio inner [role=radio] click, app dashboard URL and play.appId, service account
  "Add app" as an owner step.
- console-autofill skill rewritten agent-first; agents-core rule 10; README; pitfalls 12, 18 (confirmed sign-out),
  19; CHANGELOG; play-first-release.
2026-10-12 08:39:12 +08:00
CRT
18fde37b9b console fill: labels match straight and curly quotes (Developer’s Advertising, the user’s identity); an App Privacy dialog step with no inputs (the definitions page) is passed with Next 2026-10-12 07:52:13 +08:00
CRT
e0bef82b06 console fill apple-app-privacy: set up each data type from the real page ('Set Up <type>' is a plain text, clicked by its exact text in the Data Types section), fill its dialog step by step, dump and pause on an unknown step; '?' and Publish never clicked 2026-10-12 07:38:25 +08:00
CRT
3aedf9e137 console fill: sign-in only by the top-level URL, the main frame's form or an Apple sign-in frame
Google's account widget and cookie rotation frames (accounts.google.com /
ogs.google.com) on a signed-in Play Console no longer count as a sign-in
page. Each detection logs its rule and URL; heartbeats name the page; a
copied Chrome session that lands on a sign-in URL is said plainly; the
sign-in wait gets a watchdog dump, and --debug-dump up to 3 dumps.
Fixtures and tests for the chooser and home with the widget frames and a
top-level accounts.google.com page; pitfall 16.
2026-10-12 07:29:11 +08:00
CRT
59150c1186 console fill apple-app-privacy: fit the Data Collection dialog to the real console
A real run (sanitized debug dump) showed why the walk stopped after Next:
the data types step is the same "Data Collection" dialog (URL unchanged),
and each checkbox's accessible name is its label run together with its
help text ("NameIncluding first or last name"). Some labels also differ
from Apple's help page ("Purchases", "Other Data").

- labelNames: the exact name, then the label as a prefix that ends where
  the help text starts (no lowercase letter follows), so "Name" never
  matches "Phone Number...", "Head" never "Health...", "Contact" never
  "Contacts...". Used for the data types and the purposes.
  TYPE_ALIASES maps Purchase History / Other Data Types to the console's
  labels.
- Dialog per step: a section's dialog is also found as the one visible
  dialog with a heading of that name.
- Every click and check is bounded (ACTION_MS, 10 s; the scan test now
  also rejects clicks without a timeout).
- The flow fixture mirrors the real dialog's structure (dialog/h2 title,
  group h3s, input name = Apple's enum, <label> with label + help <p>s,
  Save disabled until a type is ticked); no data from the dump. A test
  checks every Apple type matches exactly one real-style checkbox.
- Pitfall 15.

Publish is still never clicked; unknown steps still get the watchdog dump.
2026-10-12 07:17:15 +08:00
CRT
8b669f5c86 console fill play-app-record: read the real account chooser, match names loosely
With the copied Chrome profile, `console fill play-app-record` stopped with
"no developer account named "<name>" (play.developerAccount) on the
chooser" although the name was there: the real "Choose developer account"
rows are not plain /developers/<id> text links (nested spans, icon-font
glyphs, role=button rows with the id in a data attribute, an avatar row
whose id only its click reveals).

- readChooser: every visible clickable row in the page content (header
  and navigation left out); the name is the first visible line (icon
  fonts, images and aria-hidden parts skipped); the id comes from
  /developers/<id> in any attribute, else a data-*id attribute, else ''
  (the row is then opened through the account-switch gate and the id is
  read from the URL). "Create a new developer account" is not an account.
- matchAccount: exact spelling, then spaces and case aside (Turkish I/ı
  folded), then a unique prefix. A case pair (ACME / Acme) needs the
  exact spelling; otherwise it stops and lists both with their ids.
- One developer account: picked (a different play.developerAccount is
  said). Not found: every visible account is listed with its id.
- The resolved developerId is remembered as before.

Fixtures play-chooser-real.html (the owner's chooser shape, placeholder
names) and play-chooser-one.html; tests for exact, case pair, prefix,
single account and not found.
2026-10-12 07:12:05 +08:00
CRT
81313b6fa4 console fill: no silent waits; an unknown page gets a debug dump and a bounded stop
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.
2026-10-12 07:08:10 +08:00
CRT
8ffc83bb39 console fill: walk the whole form; the owner only reviews and presses the final button
App Privacy stopped at "now click Get Started yourself" and every page
asked the owner to click Next. The owner should not click what
storebound can safely do.

- Every button of every console form is classified (navigation,
  draft-save, final-submit) in FORM_BUTTONS (cli/lib/console-forms.ts),
  citing Apple's and Google's help pages. A scoped flow gate
  (flowAllowed) lets navigation through; the generic click gate stays
  absolute for field clicks.
- App Privacy: Get Started, Edit next to Data Types (never the Privacy
  Policy Edit), each data type / "Set Up <type>", Next, and the Save
  inside the questionnaire dialogs (a draft: Apple publishes only after
  Publish, per "Manage app privacy"). Each type's dialog may be one page
  or purposes > Next > linked > Next > tracking > Save. It ends with
  "review the summary, then press Publish yourself". Publish is never
  clicked.
- Play content rating and target audience: Next by itself. Save draft,
  Submit, the final Save, Create and Create app stay the owner's.
- An uncertain answer stops the walk on its page (outlined, "answer
  needed on ...") until the owner has set it; then it goes on.

Fixture apple-app-privacy-flow.html (first time, ?existing=1, decoys);
tests/console-flow.test.ts with the guardrail that no log line asks the
owner to click a navigation control.
2026-10-12 06:51:01 +08:00
CRT
b39e44e6ab console fill: one app shown twice is one app; App Privacy opens by app id via the API
`console fill apple-app-privacy` without apple.appId stopped at "the app
in the list" with "more than one app matches: X | X" for an account with
one app: the Apps grid names each app twice (the icon link's aria-label
and the title link), both opening the same /apps/<id>/... page.

- pickApp tells hits apart by the app they open (/apps/<id>, else the
  link target), not by name. Two apps of one name with different targets
  still stop and are listed.
- Without apple.appId, but with App Store Connect API credentials, the
  app id is looked up from apple.bundleId (GET /v1/apps?filter[bundleId],
  as `apple status` does), cached in .storebound/console-app-ids.json, and
  App Privacy opens by deep link. Without credentials, or when the lookup
  fails, the Apps list by name is used, and the log says so.

Fixture asc-apps-card.html (a card with the name in the title and an
aria-label); tests for both paths.
2026-10-12 06:32:21 +08:00
CRT
d531626836 Merge remote-tracking branch 'origin/main' into play-app-record
# Conflicts:
#	CHANGELOG.md
2026-10-12 05:26:34 +08:00
CRT
04fb0089b5 Merge remote-tracking branch 'origin/main' into play-app-record
# Conflicts:
#	CHANGELOG.md
#	docs/pitfalls.md
#	skills/store-pitfalls/SKILL.md
#	tests/console-fill.test.ts
2026-10-12 05:24:52 +08:00
CRT
7746a984ec Merge remote-tracking branch 'origin/main' into shots-liga
# Conflicts:
#	CHANGELOG.md
#	cli/index.ts
2026-10-12 05:24:19 +08:00
CRT
af08d7113a shots check: ligatures no longer read as system-font glyphs (probe spans shaped as one run); --help on every command and group, unknown flags rejected before anything runs
Root cause: the per-character font probes were inline spans side by side, so Chrome shaped f + i into the fi ligature across them and the lone i probe reported no glyph of ours. Each probe is now its own block. The Google Fonts text= subsets keep the GSUB ligatures; regression test with a real Montserrat text= subset.

--help / -h prints the command's usage and exits 0 before any config, render or network; unknown flags exit 2 with the usage (cli/data/flags.json from tools/gen-flags.ts). Command tables moved to cli/commands.ts; ads apple reachable again; shots render --scenes rejects unknown ids at once.
2026-10-12 05:23:37 +08:00
CRT
7e26941ffc aso: competitors, suggest and audit from official data; ASO card on the Growth tab
- aso competitors: top apps per keyword from the iTunes Search API (name, developer,
  rating count, release / update dates, localized genre), coverage gaps (words of 2+
  developers), new entrants vs the last snapshot (.storebound/aso/competitors/)
- aso suggest: candidates from own text, competitor names, localized category names and
  Apple Ads keyword suggestions (POST /v1/suggestions/keywords/query, relative 0-100
  popularity, only with credentials); 100-byte keyword field per locale as a diff,
  --write marks it a draft that check --aso and aso audit report
- aso audit: check --aso, free keyword room, rank trend arrows, gaps, Play short
  description / density, App Store conversion, PPO / Play experiment ideas, next 3 actions
- MCP: read-only aso_competitors, aso_suggest, aso_audit; desktop ASO card (visx rank chart)
- docs: README, aso-basics, store-pitfalls (Search API order, no Play search API), CHANGELOG;
  demo-app listing files; tests on recorded Search API and mocked Ads answers
2026-10-12 04:09:34 +08:00
CRT
4533e3e1c4 app privacy: one Apple purpose per Play purpose (advertising is Third-Party Advertising only; Developer's Advertising only from the owner), appPrivacy.types (owner answers per Apple type id: certain in console fill, proposed by scan without linked / tracking, compared by check with Info.plist and PrivacyInfo.xcprivacy) 2026-10-12 04:07:15 +08:00
CRT
75d6f4cb71 console fill play-app-record: Play Console's Create app form (account picked by developerId / workspace / play.developerAccount, else stop and list; Create app opener allowlisted only on the app list, never in the form, EN + TR; name, default language, app or game, free or paid as an owner check; declarations never ticked; package name set by the first AAB upload) 2026-10-12 03:48:20 +08:00
CRT
8186b25575 console fill: User Access radios found under a plain heading (real App Store Connect), a lasting partial miss fills every field found and leaves only the missing ones to the owner 2026-10-12 03:05:11 +08:00
CRT
21c0c2ed60 console fill: sign-in detected by URL, frame and text; keeps watching after the fallback; session kept across relaunches; one login across Play developer accounts and App Store Connect teams 2026-10-12 02:54:48 +08:00
CRT
e5f610a033 Merge origin/main into console-nav 2026-10-12 02:21:37 +08:00
CRT
ce69fbd600 console fill: opens the form itself and reuses the owner's Chrome sign-in
Navigation after sign-in (navPlan + navigate): App Store Connect Apps > + > New App
(EN/TR), App Privacy by /apps/<appId>/distribution/privacy or the app list, Play app
list by package name > Policy and programs > App content > Start / Manage. Every
navigation click goes through navClick: the click gate stays absolute, only a link
named exactly Start / Manage / Başla / Yönet passes. Each step is logged; a step not
found in time or two matching apps -> the printed steps and wait (no crash).
The TR gate now refuses "uygula" (apply) but not "uygulama" (app).

Browser (cli/console/session.ts): attach to a Chrome with remote debugging (--cdp or
DevToolsActivePort), else on macOS a temporary profile with only Local State and the
profile's Cookies filtered to the console hosts, opened in real Chrome and deleted at
the end, else storebound's own profile (--fresh-profile). Cookies are never printed.
Desktop: fresh_profile option, the dialog says it uses the Chrome sign-in.
Tests: navigation fixtures (ASC EN/TR, Play list, App content, missing step,
duplicates, deny-listed decoys), session file selection, host filter, cleanup, and a
real-Chrome copy-mode test on a throwaway profile and a local site.
2026-10-12 02:21:35 +08:00
CRT
582ea909ca scan: accounts mean User ID, never email without evidence; accounts.signUp (username -> PSL_ACM_USER_ID_OTHER_AUTH); built bundles are low-trust evidence; privacy-manifest and scan find plugins in monorepo node_modules, workspaces and local SwiftPM packages 2026-10-12 02:03:07 +08:00
CRT
2d2fd9040d Merge remote-tracking branch 'origin/main' into growth-extras
# Conflicts:
#	cli/index.ts
#	cli/lib/config.ts
#	cli/lib/credentials.ts
#	docs/desktop.md
#	templates/credentials.example.json
#	tools/site-icons.ts
2026-10-12 00:14:36 +08:00
CRT
cea2dffdc3 growth extras: prices ppp, ads apple, web, apple adhoc, play share, revenuecat, console fill (+ desktop button) 2026-10-12 00:14:00 +08:00
CRT
97ab678691 Merge scan-play: Google Play scan rule parity
Conflict in cli/lib/config.ts (play section): kept play.build from main and the new targetAges, accountType, accountCreated and billingPrograms. Adapters and API tables regenerated (unchanged).
2026-10-12 00:09:22 +08:00
CRT
84ef94253c storebound scan: Google Play parity
Play rules verified against the live Policy Center / Play Console Help pages
(checked 2026-10-11, cited in cli/lib/scan/play-rules.ts): restricted
permissions and their declarations (background location, photo / video, All
files access, AccessibilityService, REQUEST_INSTALL_PACKAGES, Health Connect,
full-screen intent, VpnService), prominent disclosure and the in-app privacy
policy, Data safety vs SDK disclosures, Families (certified ad SDKs, AD_ID,
location), AD_ID on targetSdk 33+, 16 KB page sizes (Unity, React Native, NDK,
AGP, prebuilt .so), testing for new personal accounts, Play metadata, ads at
launch, payments outside Play Billing, impersonation. Detection reads manifest
components, accessibility configs, strings.xml, native toolchain versions and a
local build's merged manifest. New fixtures and tests; README table per store.
2026-10-12 00:05:35 +08:00
CRT
0092a67fc7 Teaser remake: inbox hook, real scan and re-scan, screenshot wall, Data safety, three desktop scenes, Submit card
New cut on the approved storyboard (33.75 s, every cut on the 128 BPM beat): rejection mails in a generic inbox,
storebound scan typed on black with its real output (tests/fixtures/scan/teaser), the re-scan of the fixed copy
(new fixture teaser-fixed) flipping five reds to green, 200 store screenshots, 764 Data safety answers, the desktop
app's Portfolio, release timeline (rollout to 20%) and Checks tab, then the Submit card and the logo.
60 fps capture blended to 30 fps for motion blur, whip / mask / zoom transitions, sound design on every cut,
a 1280x720 web version. tests/teaser.test.ts checks the scan text, beat grid, caption length and holds.
2026-10-11 23:54:19 +08:00
CRT
217d42cc9a Merge build-sign: signing, Xcode/Gradle build and upload, originality check
# Conflicts:
#	CHANGELOG.md
#	cli/index.ts
#	cli/lib/credentials.ts
2026-10-11 23:48:20 +08:00
CRT
41319562d8 Signing, builds, uploads and an originality check
apple signing status|ensure: bundle id capabilities, a distribution certificate from a key made locally (kept in .storebound/signing, never printed), App Store profiles installed for Xcode; Developer ID 403 explained.
apple build / apple upload: xcodebuild archive + export with a generated ExportOptions.plist, next build number, App Store Connect API build upload (or altool), processing wait. Native, Capacitor, React Native, Flutter, Unity projects.
play build / play upload: Gradle bundleRelease with the upload key passed through the environment, next versionCode read back from the AAB, testing tracks only.
originality: offline Guideline 4.3 check (name, listing text, icon and screenshot perceptual hashes, project structure).
New skill build-and-sign; zero new dependencies.
2026-10-11 23:42:48 +08:00
CRT
54dd012cfa Merge cli-workspace: multi-app workspace, credential profiles, release trains in the CLI and MCP
# Conflicts:
#	.cursor/rules/storebound.mdc
#	.github/copilot-instructions.md
#	AGENTS.md
#	CHANGELOG.md
#	GEMINI.md
#	docs/agents-core.md
2026-10-11 23:36:36 +08:00
CRT
2165322c02 CLI workspace for many apps and studios, credential profiles, release trains (release status / plan)
- storebound.workspace.json: app folders (globs), studios with accounts by credential profile, ignore list, the
  desktop's pairing overrides; ws init | list | status | check | validate | run | apps import | desktop export/import
- credential profiles in ~/.storebound/credentials/<profile>.json or env, --account <profile> on any command
- release status / release plan <version>, table or --json; MCP workspace_list, workspace_status, release_status,
  release_plan (read-only)
- pairing rules (cli/lib/identity.ts), release lanes (cli/lib/train.ts) and the per-app status read
  (cli/lib/app-status.ts) shared with the desktop instead of copied
- tests with sanitized fixtures, README, skills, CHANGELOG
2026-10-11 23:35:45 +08:00
CRT
ae58948abd storebound scan: offline rejection-risk scan of the project itself
Detects the stack (native iOS / Android, React Native, Expo, Capacitor, Cordova,
Flutter, Unity, Godot) and the SDKs from a maintained table with versions,
file:line and each vendor's disclosure page, then reports App Store and Google
Play rejection risks with guideline, evidence, fix, severity and skill: 2.1(a)
demo account and placeholder text, 5.1.1(v) account deletion, 4.8 login
services, 3.1.1 restore and outside payments, 3.1.2 subscription info, 5.1.2(i)
ATT, purpose strings, privacy link, privacy manifest, 2.3.10, 2.3.7, 2.3.8, 4.2
web wrappers, 2.5.1, 2.5.5, ATS, export compliance, 1.3 kids ads, review
prompts, privacy labels vs SDKs; Play target SDK 36, delete-account URL, App
access, foreground services, exact alarms, QUERY_ALL_PACKAGES, SMS / call log,
com.example package name.

Proposes Data safety / App Privacy / ATT / privacy manifest answers (detected
file:line or ask owner) as a diff against the config; --write adds only the
detected ones. scan.ignore records owner-accepted findings. MCP tool scan
(read-only), validate includes open findings, desktop Checks tab shows them.
Fixtures for iOS, Android, Capacitor, Unity, Flutter, clean and teaser.
2026-10-11 23:20:55 +08:00
CRT
5d485e01b5 Real-account read check of the desktop tabs: analytics snapshot stops when every store failed, compare names why a store has no days (Growth shows it), listing screenshot count skips contact sheets; Growth chart as an index on one axis; recorded price answers round-trip the price sheet 2026-10-11 08:06:26 +08:00
CRT
de681dd967 import fastlane: config, App Store and Play listings, languages and screenshot frames from a fastlane setup
storebound import fastlane [--dir] [--out] [--profile] [--dry] (MCP import_fastlane, dry run by default) reads
Appfile / Deliverfile plain option lines, deliver and supply metadata and screenshots, and writes
storebound.config.ts, store/appstore.md, store/listing.md, the Play icon and feature graphics, and frames with a
folder capture source. App Store and Play language codes are paired (cli/lib/store-locales.ts). Existing files are
never overwritten (.imported copies with a diff hint); keys, team ids, the review contact and demo account are only
named in the report. Fixture tests/fixtures/fastlane-sample, skill migrate-from-fastlane, README and CHANGELOG.
2026-10-11 04:57:03 +08:00