Skip to content

Apple App Store release journey in /ship (v1.64.0.0)#29

Merged
time-attack merged 15 commits into
mainfrom
time-attack/appleship
Jul 23, 2026
Merged

Apple App Store release journey in /ship (v1.64.0.0)#29
time-attack merged 15 commits into
mainfrom
time-attack/appleship

Conversation

@time-attack

@time-attack time-attack commented Jul 23, 2026

Copy link
Copy Markdown
Owner

What this ships

/ship now runs the full Apple release journey when the target is an Apple platform app (.xcodeproj, .xcworkspace, or app-product Swift package), via a new ship-tree reference contract references/APPLE-RELEASE.md:

  • Paid-membership gate first: one explicit question ($99/yr Apple Developer Program); without it, honest free-account ceilings (7-day own-device installs, no TestFlight/App Store) and an offered enrollment walk.
  • Release preflight: signing team, version/build number, resolved SPM/Pod/Carthage deps, 1024pt icon, privacy strings + manifests, export compliance.
  • Consent-gated store-asset offers (never auto-runs, never spends image-API budget unprompted): SnapAI icons (single 1024², Xcode derives sizes), aso-appstore-screenshots skill for marketing shots, free local asc device framing, free multi-size decks via the app-store-screenshots editor skill, or user-supplied.
  • Archive → upload with the native toolchain only (xcodebuild, export-options app-store-connect + destination: upload, ASC API key auth). No fastlane, no new project dependency.
  • CLI-first storefront completion: consent-gated asc (rorkai) for metadata, localizations, screenshots, TestFlight, build attachment, Submit for Review, and status monitoring. Browser/manual fallback only for what the API refuses (initial app record, agreements, banking/tax).
  • Durable effects: uploads and submissions run through the EXTERNAL-EFFECTS wrapper (appstore.upload.* / appstore.submit.*) so crashed sessions never double-submit.

Asset-tool choices are backed by a 6-tool benchmark against 5 local iOS apps (SnapAI vs design binary for icons; ASO skill / asc framing / deck editor for screenshots).

Test evidence

  • bun run test:gstack2 green end-to-end post-merge with main's /design removal: parity 4,381 checks (4,378 + 3 pinned for the Apple adapter: membership gate, durable-upload binding, no-new-dependency rule).
  • bun run eval:select: diff selects 0/178 E2E, 0/28 LLM-judge paid tests.
  • bun test: 4 failures, all reproduced on clean origin/main worktree (3 ship golden-baseline + extension audit-compliance — pre-existing, receipts in run log). A 5th failure (package.json/VERSION sync test, new on main) was caused by this branch's VERSION bump and is fixed in 560622d.

🤖 Generated with Claude Code


Summary by cubic

Adds the full Apple App Store release flow to /ship. Apple apps now go from working tree to App Store submission using native Xcode tools and a consent-gated App Store Connect CLI, with no new project dependencies.

  • New Features
    • Runs the end-to-end Apple release when a repo has an .xcodeproj, .xcworkspace, or Swift app package; the Apple adapter auto-loads and is parity-pinned.
    • Upfront membership gate for the paid Apple Developer Program, with clear free-account limits and an enrollment handoff.
    • Preflight checks for signing, version/build, dependency resolution (SPM/Pods/Carthage), 1024pt icon, privacy strings/manifests, and export compliance.
    • Archives and uploads with native xcodebuild using an App Store Connect API key; no fastlane or new project dependency.
    • CLI-first App Store Connect completion via asc (metadata, localizations, screenshots, TestFlight, attach build, Submit for Review, status). Browser only for the initial app record, agreements, and banking/tax.
    • Upload and submission run through the durable external-effects wrapper to avoid double actions.
    • Consent-gated asset options: SnapAI icon, aso-appstore-screenshots, asc device framing, app-store-screenshots deck editor, or user-supplied.

Written for commit 510f168. Summary will update on new commits.

Review in cubic

Sinabina and others added 14 commits July 23, 2026 13:18
Ship-tree reference contract APPLE-RELEASE.md: paid-membership gate,
native-toolchain preflight (signing, versioning, deps, validation
blockers), archive/upload via xcodebuild with App Store Connect API key
auth, uploads bound to the durable external-effects wrapper, and App
Store Connect completion routed through THIRD-PARTY-ACTIONS consent.
Dispatcher gains protocol line 10 to load it for Apple targets; parity
pins the gate, upload binding, and no-new-dependency rule (5080 -> 5083).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The App Store Connect API covers metadata, screenshots, TestFlight,
build attachment, Submit for Review, and status monitoring. The
adapter now offers a consent-gated machine-level CLI (asc by rorkai;
ittybittyapps and Codemagic equivalents) authenticated with the same
API key, submissions bound to the durable wrapper. Browser/manual
fallback shrinks to what the API refuses: initial app record,
agreements, banking/tax.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Missing icon or screenshots trigger one question, never unprompted
generation: SnapAI icons (single 1024 source, Xcode derives sizes),
aso-appstore-screenshots skill for marketing shots, free local asc
framing, or user-supplied assets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… (parity 4381)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 23, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 9 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/gstack2/run-parity.ts">

<violation number="1" location="scripts/gstack2/run-parity.ts:218">
P3: The parity check does not preserve the paid-membership gate behavior; it only proves the membership phrase remains somewhere in the document. Checking the explicit pre-archive question and STOP outcome would make removal of the gate fail parity.</violation>
</file>

<file name="CHANGELOG.md">

<violation number="1" location="CHANGELOG.md:10">
P1: This publishes a dated 1.64.0.0 release entry while the authoritative GStack 2 status remains `BLOCKED`. Defer the entry until `docs/gstack-2/STATUS.md` supplies the required completion evidence, or resolve that gate before publishing it.</violation>

<violation number="2" location="CHANGELOG.md:23">
P3: The release-summary table omits the required `Δ` column, leaving the new entry out of the repository's mandatory changelog schema. Adding the deltas also makes the claimed +3-check change visible in the benchmark itself.</violation>
</file>

<file name="scripts/gstack2/generate-skill-tree.ts">

<violation number="1" location="scripts/gstack2/generate-skill-tree.ts:658">
P1: A first release can reach upload without an App Store Connect app record, where the build cannot be associated and upload fails; record creation is only mentioned later in the completion section. Add record existence/creation to preflight before archive/upload.</violation>

<violation number="2" location="scripts/gstack2/generate-skill-tree.ts:664">
P1: Workspace-based releases can archive the wrong container or fail to resolve workspace dependencies because this command never passes `-workspace`; the same ambiguity affects repositories with multiple projects. Select the discovered workspace/project explicitly before archiving.</violation>

<violation number="3" location="scripts/gstack2/generate-skill-tree.ts:665">
P1: The archive is never uploaded by the documented steps because no `xcodebuild -exportArchive` command consumes the archive and export-options plist. Include that exact invocation so step 4 has an external effect to wrap.</violation>

<violation number="4" location="scripts/gstack2/generate-skill-tree.ts:681">
P2: Users who consent based on this text will unknowingly run `asc` with its default telemetry enabled. Disclose the upstream default and disable it before use unless the user opts in.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread CHANGELOG.md
> completion state and remaining P0 gates. No version bump or release claim is
> made here while that status holds.

## [1.64.0.0] - 2026-07-23

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This publishes a dated 1.64.0.0 release entry while the authoritative GStack 2 status remains BLOCKED. Defer the entry until docs/gstack-2/STATUS.md supplies the required completion evidence, or resolve that gate before publishing it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At CHANGELOG.md, line 10:

<comment>This publishes a dated 1.64.0.0 release entry while the authoritative GStack 2 status remains `BLOCKED`. Defer the entry until `docs/gstack-2/STATUS.md` supplies the required completion evidence, or resolve that gate before publishing it.</comment>

<file context>
@@ -7,6 +7,43 @@
 > completion state and remaining P0 gates. No version bump or release claim is
 > made here while that status holds.
 
+## [1.64.0.0] - 2026-07-23
+
+## **/ship now takes an Apple app from working tree**
</file context>
Fix with cubic


## Archive, validate, upload

1. Archive the Release configuration: \`xcodebuild archive -scheme <scheme> -destination 'generic/platform=iOS' -archivePath <name>.xcarchive\` (substitute the actual platform).

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Workspace-based releases can archive the wrong container or fail to resolve workspace dependencies because this command never passes -workspace; the same ambiguity affects repositories with multiple projects. Select the discovered workspace/project explicitly before archiving.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/gstack2/generate-skill-tree.ts, line 664:

<comment>Workspace-based releases can archive the wrong container or fail to resolve workspace dependencies because this command never passes `-workspace`; the same ambiguity affects repositories with multiple projects. Select the discovered workspace/project explicitly before archiving.</comment>

<file context>
@@ -635,6 +635,57 @@ Do not put secrets in run IDs, effect keys, or command arguments. Existing appro
+
+## Archive, validate, upload
+
+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. Authenticate with an App Store Connect API key (\`-authenticationKeyPath\`, \`-authenticationKeyID\`, \`-authenticationKeyIssuerID\`). The \`.p8\` key is a secret under the \`THIRD-PARTY-ACTIONS.md\` rules: stored outside the repository, referenced by path, never echoed.
</file context>
Fix with cubic

## Archive, validate, upload

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.

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The archive is never uploaded by the documented steps because no xcodebuild -exportArchive command consumes the archive and export-options plist. Include that exact invocation so step 4 has an external effect to wrap.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/gstack2/generate-skill-tree.ts, line 665:

<comment>The archive is never uploaded by the documented steps because no `xcodebuild -exportArchive` command consumes the archive and export-options plist. Include that exact invocation so step 4 has an external effect to wrap.</comment>

<file context>
@@ -635,6 +635,57 @@ Do not put secrets in run IDs, effect keys, or command arguments. Existing appro
+## Archive, validate, upload
+
+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. Authenticate with an App Store Connect API key (\`-authenticationKeyPath\`, \`-authenticationKeyID\`, \`-authenticationKeyIssuerID\`). The \`.p8\` key is a secret under the \`THIRD-PARTY-ACTIONS.md\` rules: stored outside the repository, referenced by path, never echoed.
+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.
</file context>
Fix with cubic

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.
- Versioning: a marketing version users should see and a build number strictly greater than any build already uploaded for that version.

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: A first release can reach upload without an App Store Connect app record, where the build cannot be associated and upload fails; record creation is only mentioned later in the completion section. Add record existence/creation to preflight before archive/upload.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/gstack2/generate-skill-tree.ts, line 658:

<comment>A first release can reach upload without an App Store Connect app record, where the build cannot be associated and upload fails; record creation is only mentioned later in the completion section. Add record existence/creation to preflight before archive/upload.</comment>

<file context>
@@ -635,6 +635,57 @@ Do not put secrets in run IDs, effect keys, or command arguments. Existing appro
+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.
+- 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.
</file context>
Fix with cubic


## App Store Connect completion

The App Store Connect API covers nearly all remaining work; drive it from a CLI, not a browser. Offer to install a dedicated App Store Connect CLI with one explicit consent question: \`asc\` (rorkai/App-Store-Connect-CLI, \`brew install asc\`) is the default offer; ittybittyapps/appstoreconnect-cli and codemagic-ci-cd cli-tools are equivalents if the user prefers them. It is a machine-level tool authenticated with the same App Store Connect API key, never a project dependency, and its telemetry is 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.

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Users who consent based on this text will unknowingly run asc with its default telemetry enabled. Disclose the upstream default and disable it before use unless the user opts in.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/gstack2/generate-skill-tree.ts, line 681:

<comment>Users who consent based on this text will unknowingly run `asc` with its default telemetry enabled. Disclose the upstream default and disable it before use unless the user opts in.</comment>

<file context>
@@ -635,6 +635,57 @@ Do not put secrets in run IDs, effect keys, or command arguments. Existing appro
+
+## App Store Connect completion
+
+The App Store Connect API covers nearly all remaining work; drive it from a CLI, not a browser. Offer to install a dedicated App Store Connect CLI with one explicit consent question: \`asc\` (rorkai/App-Store-Connect-CLI, \`brew install asc\`) is the default offer; ittybittyapps/appstoreconnect-cli and codemagic-ci-cd cli-tools are equivalents if the user prefers them. It is a machine-level tool authenticated with the same App Store Connect API key, never a project dependency, and its telemetry is 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>\`.
</file context>
Fix with cubic

if (fs.existsSync(appleReleasePath)) {
const apple = fs.readFileSync(appleReleasePath, 'utf8');
check(
apple.includes('paid Apple Developer Program membership')

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The parity check does not preserve the paid-membership gate behavior; it only proves the membership phrase remains somewhere in the document. Checking the explicit pre-archive question and STOP outcome would make removal of the gate fail parity.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/gstack2/run-parity.ts, line 218:

<comment>The parity check does not preserve the paid-membership gate behavior; it only proves the membership phrase remains somewhere in the document. Checking the explicit pre-archive question and STOP outcome would make removal of the gate fail parity.</comment>

<file context>
@@ -208,6 +210,18 @@ export function runParity(): ParityResult {
+  if (fs.existsSync(appleReleasePath)) {
+    const apple = fs.readFileSync(appleReleasePath, 'utf8');
+    check(
+      apple.includes('paid Apple Developer Program membership')
+        && apple.includes('The upload is an external effect')
+        && apple.includes('the release itself adds no new dependency'),
</file context>
Suggested change
apple.includes('paid Apple Developer Program membership')
apple.includes('Before any archive work, ask one explicit question: does the user have a paid Apple Developer Program membership')
&& apple.includes('No: STOP the App Store path')
Fix with cubic

Comment thread CHANGELOG.md Outdated
Comment on lines +23 to +27
| Metric | Before | After |
|---|---|---|
| /ship coverage of the Apple release journey | archive advice | membership gate through Submit for Review |
| New project dependencies required to release | n/a | 0 |
| Pinned parity checks | 4,378 | 4,381 |

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The release-summary table omits the required Δ column, leaving the new entry out of the repository's mandatory changelog schema. Adding the deltas also makes the claimed +3-check change visible in the benchmark itself.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At CHANGELOG.md, line 23:

<comment>The release-summary table omits the required `Δ` column, leaving the new entry out of the repository's mandatory changelog schema. Adding the deltas also makes the claimed +3-check change visible in the benchmark itself.</comment>

<file context>
@@ -7,6 +7,43 @@
+
+Source: `bun run scripts/gstack2/run-parity.ts` on this release.
+
+| Metric | Before | After |
+|---|---|---|
+| /ship coverage of the Apple release journey | archive advice | membership gate through Submit for Review |
</file context>
Suggested change
| Metric | Before | After |
|---|---|---|
| /ship coverage of the Apple release journey | archive advice | membership gate through Submit for Review |
| New project dependencies required to release | n/a | 0 |
| Pinned parity checks | 4,378 | 4,381 |
| Metric | Before | After | Δ |
|---|---|---|---|
| /ship coverage of the Apple release journey | archive advice | membership gate through Submit for Review | full App Store journey |
| New project dependencies required to release | n/a | 0 | 0 |
| Pinned parity checks | 4,378 | 4,381 | +3 |
Fix with cubic

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@time-attack
time-attack merged commit 729134b into main Jul 23, 2026
2 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant