Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.**
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.64.6.0
1.64.7.0
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
40 changes: 14 additions & 26 deletions scripts/gstack2/generate-skill-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <apple-id>\` through the host's interactive command path (in Claude Code, the user types \`! fastlane spaceauth -u <email>\` 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 <scheme> -destination 'generic/platform=iOS' -archivePath <name>.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.<bundle-id>.<build>\`. 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.<bundle-id>.<build>\`. 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.<bundle-id>.<version>\`. 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.<bundle-id>.<version>\`.

The public API cannot create the initial app record — but the CLI can: \`asc web apps create --name <name> --bundle-id <id> --sku <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 <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.
`;
}

Expand Down
Loading
Loading