diff --git a/CHANGELOG.md b/CHANGELOG.md index 53192ae9f2..abba74f296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ > completion state and remaining P0 gates. No version bump or release claim is > made here while that status holds. +## [1.64.7.0] - 2026-07-24 + +**One tool. One question. The rest is fastlane.** + +The Apple release adapter is rewritten around three assumptions from a live release test. First: the user has never heard of a .p8 — they paid $99 and want to ship, so credential vocabulary is banned from everything they see. Second: a concise dev installs ONE tool, not a zoo of App Store CLIs — fastlane now runs the entire release: produce, cert, sigh, gym, pilot, deliver, frameit. Third: the interaction is minimal — the whole journey asks exactly one thing, up front: "authorize this release." Sign-in happens inside that same moment (`! fastlane spaceauth` in-session, password and one 2FA code straight to Apple, the printed token kept out of the transcript), and after it there are zero further questions: fastlane install, asset generation, upload, and submission are all covered. Missing screenshots or icons no longer prompt mid-run — simulator capture plus frameit plus a SnapAI icon happen autonomously, with the marketing-grade generators reserved for when you ask for marketing. + +### Itemized changes + +### Changed + +- `references/APPLE-RELEASE.md` (ship tree): rewritten fastlane-only — no asc, no auth branches, no credential words in user-facing flow; one authorization moment covering installs, assets, upload, and submission; store assets generate autonomously (simulator capture + frameit + SnapAI icon), richer generators only on explicit request; in-session `!` sign-in with the fallback terminal window demoted to hosts without an interactive path. + ## [1.64.6.0] - 2026-07-24 **Authentication is not a question.** diff --git a/VERSION b/VERSION index e9aee1e697..751d848263 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.64.6.0 +1.64.7.0 diff --git a/package.json b/package.json index 4d8cb6b4a4..6d481eafc2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gstack", - "version": "1.64.6.0", + "version": "1.64.7.0", "description": "GStack 2 \u2014 six portable Agent Skills with an optional host-neutral runtime.", "license": "MIT", "type": "module", diff --git a/scripts/gstack2/generate-skill-tree.ts b/scripts/gstack2/generate-skill-tree.ts index 26f76c1ef6..aba11a436e 100644 --- a/scripts/gstack2/generate-skill-tree.ts +++ b/scripts/gstack2/generate-skill-tree.ts @@ -640,50 +640,38 @@ function appleReleaseContract(): string { Applies when the ship target is an Apple platform app: the repository contains an \`.xcodeproj\` or \`.xcworkspace\`, or a Swift package with an app product. This adapter extends the preserved ship judgment to the App Store journey end to end; it replaces no gate, and every upload or submission remains an external effect executed through \`references/EXTERNAL-EFFECTS.md\`. -Build and upload with the native Apple toolchain (\`xcodebuild\`, \`xcrun\`, \`agvtool\`); finish the storefront work with a consent-gated App Store Connect API CLI as described below. Never add fastlane or another release manager as a project dependency to ship; the release itself adds no new dependency to the user's project — the App Store Connect CLI is a machine tool, installed only with explicit consent. +One tool runs the entire release: machine-level fastlane — \`produce\` (app record and bundle ID), \`cert\` and \`sigh\` (signing), \`gym\` (archive and signed export), \`pilot\` (TestFlight), \`deliver\` (metadata, screenshots, Submit for Review), \`frameit\` (device frames). Install it when missing (\`brew install fastlane\`) with a one-line announcement, not a question — the release authorization covers machine-tool installs. Never install additional App Store CLI tools, and never mention API keys, \`.p8\` files, sessions, or any credential format to the user; they paid US$99 and want to ship, and the release itself adds no new dependency to the user's project. -## Membership gate +## The one authorization moment -Before any archive work, ask one explicit question: does the user have a paid Apple Developer Program membership (US$99/year)? App Store distribution and TestFlight both require it. +The user is asked exactly one thing in the whole journey, up front: confirm they hold a paid Apple Developer Program membership (US$99/year — the App Store and TestFlight both require it) and authorize the release. Apple sign-in happens inside this same moment: run \`fastlane spaceauth -u \` through the host's interactive command path (in Claude Code, the user types \`! fastlane spaceauth -u \` so their password and one two-factor code go directly to Apple in-session; a separate terminal window is the fallback only when the host has no interactive path). Keep the printed session token out of the transcript — the cached cookie in \`~/.fastlane/spaceship/\` is the credential fastlane actually uses; never store, echo, or log the password or token, and re-run the same one command when the session expires. After this moment there are ZERO further questions: tool installs, asset generation, upload, and submission are all covered by it. -- Yes: continue. -- No: STOP the App Store path. Offer to walk enrollment at developer.apple.com through \`references/THIRD-PARTY-ACTIONS.md\` (enrollment is a purchase the user completes themselves and can take a day or two to activate), or name the free-account ceiling honestly: personal-team signing installs only on the user's own devices and expires after 7 days, with no TestFlight and no App Store. +No membership: STOP the App Store path. Offer to walk enrollment at developer.apple.com through \`references/THIRD-PARTY-ACTIONS.md\` (a purchase the user completes themselves; activation can take a day or two), and name the free-account ceiling honestly: personal-team installs on the user's own devices only, expiring after 7 days, no TestFlight, no App Store. ## Release preflight Resolve and verify before archiving. Fix what the printed mutation boundary authorizes; report everything else as a blocking finding. -- Signing: development team set on the app target, automatic signing enabled (or a valid distribution certificate and provisioning profile), bundle identifier decided. +- Signing: development team on the app target; \`cert\` and \`sigh\` mint the distribution certificate and App Store profile when none exist. - Versioning: a marketing version users should see and a build number strictly greater than any build already uploaded for that version. - Dependencies: \`xcodebuild -resolvePackageDependencies\` succeeds; if a \`Podfile\` or \`Cartfile\` exists, its install step has been run and lockfiles are current. - App Store validation blockers: complete app icon set including the 1024pt marketing icon, launch screen, a usage-description string for every privacy-gated API the app touches, required privacy manifests, an export-compliance answer (\`ITSAppUsesNonExemptEncryption\`), and a sane deployment target. -## Archive, validate, upload +## Store assets — autonomous -1. Archive the Release configuration: \`xcodebuild archive -scheme -destination 'generic/platform=iOS' -archivePath .xcarchive\` (substitute the actual platform). -2. Author an export-options plist with method \`app-store-connect\`. Prefer \`destination: upload\`, which validates and uploads in one supported step; \`destination: export\` plus the Transporter app is the fallback. -3. Authentication is NOT a question. Never ask the user to choose an auth path, never present an API-key option to someone who has no key, and never offer a browser drive to create credentials. Resolve it silently: if a \`.p8\` App Store Connect API key already exists on this machine (a known path or env configuration), use it via \`xcodebuild -exportArchive\` (\`-authenticationKeyPath\`/\`-authenticationKeyID\`/\`-authenticationKeyIssuerID\`) — the \`.p8\` is a secret under the \`THIRD-PARTY-ACTIONS.md\` rules: stored outside the repository, referenced by path, never echoed. Otherwise — the baseline gstack user, who has an Apple ID and a just-paid membership and nothing else — state in one line that fastlane is required for the upload, install it (\`brew install fastlane\`; the /ship authorization covers this machine-tool install), and mint the session: \`fastlane spaceauth\`. The user's entire contribution is their password and one 2FA code (automatable via an approved local code source). Per fastlane's documented auth, \`FASTLANE_SESSION\` ALONE suffices for binary upload via \`deliver\`/\`pilot\` — \`FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD\` is an alternative, not a requirement. The session is an env credential: never argv, never echoed, re-minted by the same command when it expires. fastlane is a machine tool, still never a project dependency. -4. The upload is an external effect: run it through the durable state wrapper with a key like \`appstore.upload..\`. Never re-upload on ambiguity; inspect App Store Connect for the build first. +Missing assets never block the run with a question; the one authorization covers their generation. Cheapest path first: capture screens from the built app in the simulator, frame them with fastlane \`frameit\`, and generate the single 1024×1024 icon with SnapAI when an image-generation key is already configured (Xcode 15+ derives every size from that one image). The richer generators — the aso-appstore-screenshots skill for marketing-grade shots, the app-store-screenshots deck editor — run only when the user explicitly asked for marketing-grade assets. User-supplied files always win: validate dimensions and move on. Announce what was generated at exit. -## Store assets +## Archive and upload -When preflight finds a missing icon, or the storefront step needs screenshots, ask the user first — one question, then act on their choice. Never generate marketing assets or spend the user's image-API budget unprompted. Offer: +1. Archive and export the signed Release build with \`gym\` (it drives xcodebuild and the signing minted in preflight). Projects with custom archive requirements may drop to \`xcodebuild archive\` directly; the output either way is an App Store-signed \`.ipa\`. +2. The upload is an external effect: run \`pilot\` (TestFlight) or \`deliver\` (App Store) through the durable state wrapper with a key like \`appstore.upload..\`. Never re-upload on ambiguity; inspect App Store Connect for the build first. +3. The cached session is an env-level credential: never argv, never echoed, never committed. -- **App icon**: SnapAI (\`npx snapai\`, the app-icon agent skill) generates a 1024×1024 icon-styled square with the user's own OpenAI key (roughly $0.05–0.19 per icon); Xcode 15+ derives every size from that single image, so no resizer tool is needed. -- **Marketing screenshots**: the aso-appstore-screenshots agent skill (benefit-headline discovery, deterministic scaffold, Nano Banana Pro enhancement with the user's Gemini key, exact App Store dimensions). If installed, follow its workflow rather than reimplementing it. -- **Plain device framing**: \`asc screenshots capture\` and \`asc screenshots frame\` — free, local, upload-normalized output (requires the axe CLI and pinned Koubou). -- **Designed screenshot decks, free and local**: the app-store-screenshots agent skill (parthjadhav editor) — scaffold its editor, prefill \`app-store-screenshots.json\` with captures and copy, and export one bundle covering every required iPhone size per locale; the user can refine slides interactively before export. -- **User-supplied assets**: always a valid answer; validate dimensions and move on. +## Storefront completion -## App Store Connect completion +\`produce\` already created the app record and bundle ID during the run — never call the app record a manual gate. \`deliver\` owns everything the store listing needs: description, keywords, localizations, screenshot upload per device size, attaching the uploaded build, and Submit for Review; \`pilot\` manages TestFlight groups and testers as an intermediate round when the user asked for one. Submission is an external effect like the upload: durable key \`appstore.submit..\`. Monitor review status from the CLI afterward. -Drive the remaining storefront work from a CLI, not a browser, on the credential resolved in the upload step — same rule: not a question. Baseline session-only user: fastlane \`deliver\` owns this stage end to end — description, keywords, localizations, screenshot upload, attaching the build, and Submit for Review, all on \`FASTLANE_SESSION\`. Only a user whose \`.p8\` was found in the upload step routes here through \`asc\` (rorkai/App-Store-Connect-CLI, \`brew install asc\`; ittybittyapps/appstoreconnect-cli and codemagic-ci-cd cli-tools are equivalents) — JSON-first and the better agent surface; one tool per credential world, never both in one release. Any storefront CLI is a machine-level tool, never a project dependency, with telemetry disabled unless the user opts in. If the user already has dedicated App Store Connect agent skills installed (for example rorkai/app-store-connect-cli-skills via the standard skills installer), defer to those flows instead of duplicating them. - -Through the CLI: description, keywords, and localizations; screenshot upload per required device size; TestFlight groups and testers as an intermediate distribution before review; attaching the uploaded build to the version; Submit for Review; and review-status monitoring afterward. Submission is an external effect like the upload: run it through the durable state wrapper with a key like \`appstore.submit..\`. - -The public API cannot create the initial app record — but the CLI can: \`asc web apps create --name --bundle-id --sku \` uses an Apple web session (the same mechanism as fastlane produce) and is the canonical app-creation path. Never call the app record a manual gate. With a cached web session (\`asc web auth status\`) it runs fully headless; a fresh login (\`asc web auth login --apple-id \`) takes the password from a secure prompt or \`ASC_WEB_PASSWORD\` and can resolve two-factor codes programmatically via \`--two-factor-code-command\` (or \`ASC_WEB_2FA_CODE_COMMAND\`) when the user has approved a local code source; otherwise hand off for the single 2FA entry and resume. Bundle ID registration rides the same family (\`asc web bundle-ids\`, with create preflight built in). Never store or echo the Apple ID password; the session cache is the credential. - -The autonomy contract, written for the baseline user story — an Apple ID and a just-paid membership, nothing else: that is sufficient to run the ENTIRE release from the CLI. One session mint (\`fastlane spaceauth\` or \`asc web auth login\`; the single 2FA entry is automatable via an approved local code source), then app record and bundle ID via \`asc web\` or \`fastlane produce\`, and upload/metadata/TestFlight/submit via session-authenticated \`deliver\`/\`pilot\`. No \`.p8\`, no app-specific password, no browser. Never declare a manual gate that a session-authenticated tool covers. A free app needs nothing else, ever. What remains web-only: the membership purchase itself (a precondition, not a release step) and, for PAID apps only, the one-time Paid Apps agreement with banking/tax — offer the agentic-browser drive per \`references/THIRD-PARTY-ACTIONS.md\` before any manual checklist for those. After submission, report that App Review typically answers within a day or two and close the run; review outcome is not a gate this workflow can hold open. +What remains web-only, ever: the paid Apple Developer Program membership purchase itself (a precondition, not a release step) and, for PAID apps only, the one-time Paid Apps agreement with banking and tax — offer the agentic-browser drive per \`references/THIRD-PARTY-ACTIONS.md\` before any manual checklist for those. A free app needs no browser at any point. After submission, report that App Review typically answers within a day or two and close the run; review outcome is not a gate this workflow can hold open. `; } diff --git a/skills/ship/references/APPLE-RELEASE.md b/skills/ship/references/APPLE-RELEASE.md index 37ed81fa65..1b5b27242a 100644 --- a/skills/ship/references/APPLE-RELEASE.md +++ b/skills/ship/references/APPLE-RELEASE.md @@ -3,47 +3,35 @@ Applies when the ship target is an Apple platform app: the repository contains an `.xcodeproj` or `.xcworkspace`, or a Swift package with an app product. This adapter extends the preserved ship judgment to the App Store journey end to end; it replaces no gate, and every upload or submission remains an external effect executed through `references/EXTERNAL-EFFECTS.md`. -Build and upload with the native Apple toolchain (`xcodebuild`, `xcrun`, `agvtool`); finish the storefront work with a consent-gated App Store Connect API CLI as described below. Never add fastlane or another release manager as a project dependency to ship; the release itself adds no new dependency to the user's project — the App Store Connect CLI is a machine tool, installed only with explicit consent. +One tool runs the entire release: machine-level fastlane — `produce` (app record and bundle ID), `cert` and `sigh` (signing), `gym` (archive and signed export), `pilot` (TestFlight), `deliver` (metadata, screenshots, Submit for Review), `frameit` (device frames). Install it when missing (`brew install fastlane`) with a one-line announcement, not a question — the release authorization covers machine-tool installs. Never install additional App Store CLI tools, and never mention API keys, `.p8` files, sessions, or any credential format to the user; they paid US$99 and want to ship, and the release itself adds no new dependency to the user's project. -## Membership gate +## The one authorization moment -Before any archive work, ask one explicit question: does the user have a paid Apple Developer Program membership (US$99/year)? App Store distribution and TestFlight both require it. +The user is asked exactly one thing in the whole journey, up front: confirm they hold a paid Apple Developer Program membership (US$99/year — the App Store and TestFlight both require it) and authorize the release. Apple sign-in happens inside this same moment: run `fastlane spaceauth -u ` through the host's interactive command path (in Claude Code, the user types `! fastlane spaceauth -u ` so their password and one two-factor code go directly to Apple in-session; a separate terminal window is the fallback only when the host has no interactive path). Keep the printed session token out of the transcript — the cached cookie in `~/.fastlane/spaceship/` is the credential fastlane actually uses; never store, echo, or log the password or token, and re-run the same one command when the session expires. After this moment there are ZERO further questions: tool installs, asset generation, upload, and submission are all covered by it. -- Yes: continue. -- No: STOP the App Store path. Offer to walk enrollment at developer.apple.com through `references/THIRD-PARTY-ACTIONS.md` (enrollment is a purchase the user completes themselves and can take a day or two to activate), or name the free-account ceiling honestly: personal-team signing installs only on the user's own devices and expires after 7 days, with no TestFlight and no App Store. +No membership: STOP the App Store path. Offer to walk enrollment at developer.apple.com through `references/THIRD-PARTY-ACTIONS.md` (a purchase the user completes themselves; activation can take a day or two), and name the free-account ceiling honestly: personal-team installs on the user's own devices only, expiring after 7 days, no TestFlight, no App Store. ## Release preflight Resolve and verify before archiving. Fix what the printed mutation boundary authorizes; report everything else as a blocking finding. -- Signing: development team set on the app target, automatic signing enabled (or a valid distribution certificate and provisioning profile), bundle identifier decided. +- Signing: development team on the app target; `cert` and `sigh` mint the distribution certificate and App Store profile when none exist. - Versioning: a marketing version users should see and a build number strictly greater than any build already uploaded for that version. - Dependencies: `xcodebuild -resolvePackageDependencies` succeeds; if a `Podfile` or `Cartfile` exists, its install step has been run and lockfiles are current. - App Store validation blockers: complete app icon set including the 1024pt marketing icon, launch screen, a usage-description string for every privacy-gated API the app touches, required privacy manifests, an export-compliance answer (`ITSAppUsesNonExemptEncryption`), and a sane deployment target. -## Archive, validate, upload +## Store assets — autonomous -1. Archive the Release configuration: `xcodebuild archive -scheme -destination 'generic/platform=iOS' -archivePath .xcarchive` (substitute the actual platform). -2. Author an export-options plist with method `app-store-connect`. Prefer `destination: upload`, which validates and uploads in one supported step; `destination: export` plus the Transporter app is the fallback. -3. Authentication is NOT a question. Never ask the user to choose an auth path, never present an API-key option to someone who has no key, and never offer a browser drive to create credentials. Resolve it silently: if a `.p8` App Store Connect API key already exists on this machine (a known path or env configuration), use it via `xcodebuild -exportArchive` (`-authenticationKeyPath`/`-authenticationKeyID`/`-authenticationKeyIssuerID`) — the `.p8` is a secret under the `THIRD-PARTY-ACTIONS.md` rules: stored outside the repository, referenced by path, never echoed. Otherwise — the baseline gstack user, who has an Apple ID and a just-paid membership and nothing else — state in one line that fastlane is required for the upload, install it (`brew install fastlane`; the /ship authorization covers this machine-tool install), and mint the session: `fastlane spaceauth`. The user's entire contribution is their password and one 2FA code (automatable via an approved local code source). Per fastlane's documented auth, `FASTLANE_SESSION` ALONE suffices for binary upload via `deliver`/`pilot` — `FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD` is an alternative, not a requirement. The session is an env credential: never argv, never echoed, re-minted by the same command when it expires. fastlane is a machine tool, still never a project dependency. -4. The upload is an external effect: run it through the durable state wrapper with a key like `appstore.upload..`. Never re-upload on ambiguity; inspect App Store Connect for the build first. +Missing assets never block the run with a question; the one authorization covers their generation. Cheapest path first: capture screens from the built app in the simulator, frame them with fastlane `frameit`, and generate the single 1024×1024 icon with SnapAI when an image-generation key is already configured (Xcode 15+ derives every size from that one image). The richer generators — the aso-appstore-screenshots skill for marketing-grade shots, the app-store-screenshots deck editor — run only when the user explicitly asked for marketing-grade assets. User-supplied files always win: validate dimensions and move on. Announce what was generated at exit. -## Store assets +## Archive and upload -When preflight finds a missing icon, or the storefront step needs screenshots, ask the user first — one question, then act on their choice. Never generate marketing assets or spend the user's image-API budget unprompted. Offer: +1. Archive and export the signed Release build with `gym` (it drives xcodebuild and the signing minted in preflight). Projects with custom archive requirements may drop to `xcodebuild archive` directly; the output either way is an App Store-signed `.ipa`. +2. The upload is an external effect: run `pilot` (TestFlight) or `deliver` (App Store) through the durable state wrapper with a key like `appstore.upload..`. Never re-upload on ambiguity; inspect App Store Connect for the build first. +3. The cached session is an env-level credential: never argv, never echoed, never committed. -- **App icon**: SnapAI (`npx snapai`, the app-icon agent skill) generates a 1024×1024 icon-styled square with the user's own OpenAI key (roughly $0.05–0.19 per icon); Xcode 15+ derives every size from that single image, so no resizer tool is needed. -- **Marketing screenshots**: the aso-appstore-screenshots agent skill (benefit-headline discovery, deterministic scaffold, Nano Banana Pro enhancement with the user's Gemini key, exact App Store dimensions). If installed, follow its workflow rather than reimplementing it. -- **Plain device framing**: `asc screenshots capture` and `asc screenshots frame` — free, local, upload-normalized output (requires the axe CLI and pinned Koubou). -- **Designed screenshot decks, free and local**: the app-store-screenshots agent skill (parthjadhav editor) — scaffold its editor, prefill `app-store-screenshots.json` with captures and copy, and export one bundle covering every required iPhone size per locale; the user can refine slides interactively before export. -- **User-supplied assets**: always a valid answer; validate dimensions and move on. +## Storefront completion -## App Store Connect completion +`produce` already created the app record and bundle ID during the run — never call the app record a manual gate. `deliver` owns everything the store listing needs: description, keywords, localizations, screenshot upload per device size, attaching the uploaded build, and Submit for Review; `pilot` manages TestFlight groups and testers as an intermediate round when the user asked for one. Submission is an external effect like the upload: durable key `appstore.submit..`. Monitor review status from the CLI afterward. -Drive the remaining storefront work from a CLI, not a browser, on the credential resolved in the upload step — same rule: not a question. Baseline session-only user: fastlane `deliver` owns this stage end to end — description, keywords, localizations, screenshot upload, attaching the build, and Submit for Review, all on `FASTLANE_SESSION`. Only a user whose `.p8` was found in the upload step routes here through `asc` (rorkai/App-Store-Connect-CLI, `brew install asc`; ittybittyapps/appstoreconnect-cli and codemagic-ci-cd cli-tools are equivalents) — JSON-first and the better agent surface; one tool per credential world, never both in one release. Any storefront CLI is a machine-level tool, never a project dependency, with telemetry disabled unless the user opts in. If the user already has dedicated App Store Connect agent skills installed (for example rorkai/app-store-connect-cli-skills via the standard skills installer), defer to those flows instead of duplicating them. - -Through the CLI: description, keywords, and localizations; screenshot upload per required device size; TestFlight groups and testers as an intermediate distribution before review; attaching the uploaded build to the version; Submit for Review; and review-status monitoring afterward. Submission is an external effect like the upload: run it through the durable state wrapper with a key like `appstore.submit..`. - -The public API cannot create the initial app record — but the CLI can: `asc web apps create --name --bundle-id --sku ` uses an Apple web session (the same mechanism as fastlane produce) and is the canonical app-creation path. Never call the app record a manual gate. With a cached web session (`asc web auth status`) it runs fully headless; a fresh login (`asc web auth login --apple-id `) takes the password from a secure prompt or `ASC_WEB_PASSWORD` and can resolve two-factor codes programmatically via `--two-factor-code-command` (or `ASC_WEB_2FA_CODE_COMMAND`) when the user has approved a local code source; otherwise hand off for the single 2FA entry and resume. Bundle ID registration rides the same family (`asc web bundle-ids`, with create preflight built in). Never store or echo the Apple ID password; the session cache is the credential. - -The autonomy contract, written for the baseline user story — an Apple ID and a just-paid membership, nothing else: that is sufficient to run the ENTIRE release from the CLI. One session mint (`fastlane spaceauth` or `asc web auth login`; the single 2FA entry is automatable via an approved local code source), then app record and bundle ID via `asc web` or `fastlane produce`, and upload/metadata/TestFlight/submit via session-authenticated `deliver`/`pilot`. No `.p8`, no app-specific password, no browser. Never declare a manual gate that a session-authenticated tool covers. A free app needs nothing else, ever. What remains web-only: the membership purchase itself (a precondition, not a release step) and, for PAID apps only, the one-time Paid Apps agreement with banking/tax — offer the agentic-browser drive per `references/THIRD-PARTY-ACTIONS.md` before any manual checklist for those. After submission, report that App Review typically answers within a day or two and close the run; review outcome is not a gate this workflow can hold open. +What remains web-only, ever: the paid Apple Developer Program membership purchase itself (a precondition, not a release step) and, for PAID apps only, the one-time Paid Apps agreement with banking and tax — offer the agentic-browser drive per `references/THIRD-PARTY-ACTIONS.md` before any manual checklist for those. A free app needs no browser at any point. After submission, report that App Review typically answers within a day or two and close the run; review outcome is not a gate this workflow can hold open.