fortunto2/solo-factory/ios-release

Claude Code and agent skill by fortunto2/solo-factory

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Ship an iPhone/iOS app to testers (TestFlight) or to the App Store, or push an update to an app already listed there. Use when the user says "make the iOS app available to testers", "TestFlight link", "publish to the App Store", "upload the build", "submit for review", "App Store Connect", "ASC", "upload a new version", or wants store attribution set up (campaign links, `ct` tokens, App Analytics, an in-app counter and the matching App Privacy declaration). CLI-first via the `asc` tool (official App Store Connect API) — the browser is a fallback for the few things the API cannot do. Pair with the `ios-app` skill for the build/signing/archive checklist.

Install

npx vskill@latest install fortunto2/solo-factory/ios-release

SKILL.md

ios-release — ship iPhone via CLI

Everything runs through `asc` (App Store Connect CLI — official API, JWT-signed). The browser is a fallback for the handful of flows Apple never exposed publicly — see references/browser.md.

Sibling skill: `android-release`. Build/signing/archive details live in `ios-app`. There is no APK-style sideload on iOS — testers install via TestFlight.

Golden rules:

  • Never submit for review without the user's explicit OK — outward-facing and hard to reverse.

TestFlight internal-tester rollout is low-risk (no Apple review); a prior "set it up" covers it.

  • Dry-run before every mutation. The high-level commands take --dry-run; add --confirm only

after the printed plan matches what the user asked for.

  • `asc validate` is the source of truth, not your reading of the ASC web UI. Its ordered

remediation plan is the task list — fix the first item, re-validate, repeat.

  • Confirm flags with `--help` before running. The CLI moves fast; don't trust memory.
  • **No App Review without an end-to-end walk on a physical iPhone running the newest iOS the code

branches on** — the largest #available(iOS N in the sources. Install the exact build being submitted (TestFlight or a device install), walk the main flow to its result, leave the app once mid-task, and keep a receipt. See End-to-end gate.


End-to-end gate — before every submission

A green suite and a working simulator do not mean the build works on the phones it ships to. The case that made this a rule: a background-task handler written as a closure inside a @MainActor type compiled with an executor check, and the system ran it off the main thread. It trapped seconds after the main button, only on iOS 26. The simulator never runs background tasks, the unit test that called the handler off-main stayed green with the defect planted, and the dev phone ran iOS 18, so the #available(iOS 26 branch had never executed before the store. That version crashed about ten times as often as the one before it in its first week.

What the walk must be, and what the receipt proves:

ruleread from
devicephysical, never a simulatorxcrun devicectl list devices --json-output f → hardwareProperties.reality == "physical"
iOSmajor ≥ the newest #available(iOS N in the sourcesthe same JSON, deviceProperties.osVersionNumber
buildthe one being submitted is the one installeddevicectl device info apps --device ID --bundle-id B --include-all-apps → bundleVersion
walkthe main flow to its result, plus leaving the app mid-taskthe walker's own words

Read the first three off the device, never from a flag or a memory, and write them to a receipt file per build, committed beside the release metadata.

Make it a gate, not a habit. A habit fails on the night it matters. Put a PreToolUse hook on Bash that refuses asc … review submit, submissions-submit, submit create and release run / publish appstore … --submit unless a receipt exists for the build being submitted. Resolve a --build-id with asc builds info --build-id ID (→ attributes.version). Let a --dry-run and TestFlight's own --submit through. Parse the shell line with quotes respected, so a commit message that mentions the command is not mistaken for it. Check it against a known answer before trusting it: the dev phone on an older iOS must be refused, and a live review submit aimed at a fake asc on PATH must be stopped.

When no phone on the newest iOS is at hand, the release waits, or goes to an external TestFlight group with such phones first. It never goes to App Review on the strength of the simulator.


Setup (once per machine)

bash
brew install asc
asc auth login --name "default" --key-id "KEYID" --issuer-id "ISSUER-UUID" \
--private-key ~/Downloads/AuthKey_KEYID.p8 --network
asc auth status

The key comes from ASC → Users and Access → Integrations → App Store Connect API → Team Keys → + → role App Manager (Admin also works). The .p8 downloads exactly once. asc auth login copies the key material into the macOS Keychain, so the file can be deleted afterwards. Env-var fallbacks, CI setup and per-repo profiles: references/asc-cli.md.


Routing

IntentGo to
Get a build to testersA. TestFlight
Ship a new app to the StoreB. App Store
New version of a listed appC. Update
Crashes, feedback, testers, reviewsD. After release
App Review rejected the versionE. Rejection
Where installs and money come fromF. Analytics
asc validate reports blockersreferences/readiness.md
Description, keywords, ASO, What's Newreferences/metadata-aso.md
Screenshots — capture, frame, uploadreferences/screenshots.md
Archive, export, signing, CI pipelinereferences/build-upload.md
Create the app record, App Privacyreferences/browser.md
Flags, auth, IDs, command discoveryreferences/asc-cli.md

Open every session on an unfamiliar app with the dashboard:

bash
asc apps list --output table # find the APP_ID
asc status --app "APP_ID" --output table # builds + version + submission state

App registry. Before asking the user for app IDs, review contacts, copyright or store URLs, look for an ios-apps.yaml in their knowledge base or repo root (rg -l "asc_auth|bundle_id" --glob "*.yaml"). Releases need the same handful of values every time; a registry means asking once. Keep that file in a private repo — it holds a phone number and email that App Review sees but the store never shows.


A. MINIMAL — TestFlight

One command covers upload, processing wait, and group assignment:

bash
asc publish testflight --app "APP_ID" --ipa "./App.ipa" --group "Beta" --wait --output table

No .ipa yet? asc xcode archive + asc xcode export build one — see references/build-upload.md.

Then:

  • Internal testers (≤100, must be Users on the ASC team) — no review, available in minutes.
  • External testers / public link — needs a short Beta App Review plus the export-compliance

answer. Add --submit --confirm to trigger that review.

Monitor:

bash
asc builds list --app "APP_ID" --limit 5 --output table # processingState must reach VALID
asc testflight groups list --app "APP_ID" --output table

⚠️ BETA_CONTRACT_MISSING (422) on upload = check ASC → Agreements are all Active (Paid + Free Apps). If they are and it still fails, it's an Apple-side backend bug — contact support, don't just wait it out.


B. FULL — App Store submission

The version sits in "Prepare for Submission" until a checklist is complete. Drive that checklist from validate, never from clicking around the web UI.

bash
# 1. What's missing? Output is an ordered remediation plan — the first item is the next thing to fix.
asc validate --app "APP_ID" --version "1.0" --platform IOS --output table
# On an unstarted version this table is ~35 KB (an empty age rating alone is 24 rows).
# Collapse it to one line per problem before showing the user anything:
asc validate --app "APP_ID" --version "1.0" --platform IOS --output json \
| jq -r '[.. | objects | select(has("checkId"))] | group_by(.checkId)
| map({c: .[0].checkId, s: .[0].severity, n: length, fix: .[0].remediation})
| .[] | "\(.s|ascii_upcase) [\(.n)x] \(.c)\n → \(.fix)"'
# 2. Fix the first blocker → references/readiness.md. Re-run step 1. Repeat until clean.
# 3. Stage: apply metadata, attach the build, re-validate — creates NO review submission.
asc release stage --app "APP_ID" --version "1.0" --build "BUILD_ID" \
--metadata-dir "./metadata" --dry-run --output table
asc release stage --app "APP_ID" --version "1.0" --build "BUILD_ID" \
--metadata-dir "./metadata" --confirm
# 4. Submit — ONLY after the user says go AND this build has an end-to-end receipt
# (see "End-to-end gate" above). --build is REQUIRED even when already attached.
asc review submit --app "APP_ID" --version "1.0" --build "BUILD_ID" --dry-run --output table
asc review submit --app "APP_ID" --version "1.0" --build "BUILD_ID" --confirm
# 5. Monitor.
asc review status --app "APP_ID" --version "1.0" --output table

Starting from an .ipa with nothing staged, asc publish appstore collapses steps 3–4:

bash
asc publish appstore --app "APP_ID" --ipa "./App.ipa" --version "1.0" \
--submit --wait --dry-run --output table # then --confirm instead of --dry-run

Do not mix lanes. Once a lane has created a review submission, inspect it (asc submit status --version-id "VERSION_ID") and continue in that same lane.

Success = the version's appStoreState / reviewSubmission.state reads WAITING_FOR_REVIEW.

What validate enforces

Metadata lengths · required localizations · review details · primary category · attached and processed build · encryption declaration · content rights · pricing schedule and territory availability (a separate required item — the classic miss) · screenshot presence and sizes · age rating. Repair recipes for each: references/readiness.md.

App Privacy is not in the public API — use asc web privacy or the browser. See references/browser.md.

What validate does NOT catch — check these by hand before submitting

A clean asc validate is not a clean review. These pass validation and get the version rejected:

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
18
Repository forks
3
7-day trend score
15

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/fortunto2/solo-factory/ios-release/badge)](https://verified-skill.com/skills/fortunto2/solo-factory/ios-release)

Something wrong with this skill or its page? Tell us.