agentsclimarketplace

Appstore precheck

Skill berkayturk/appstore-precheck/skills/appstore-precheck

Read-only iOS App Store pre-submission check: scans 20 rejection vectors, wraps Apple's fastlane precheck, watches guideline drift, and runs an adversarial reviewer pass before you submit.

Install
npx -y skills add berkayturk/appstore-precheck --skill appstore-precheck

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Read-only pre-submission check for an iOS app before App Store review. Scans Swift and Objective-C code, fastlane metadata, screenshots, PrivacyInfo.xcprivacy, and the paywall for 52 rejection vectors, wraps Apple's official `fastlane precheck`, watches for live App Store Review Guideline drift, has Pierre explain every FAIL and WARN, then runs 23 semantic deep-review checks (Tier A) plus 6 heuristic checks (Tier B v1) — 29 total. Emits a GREEN/YELLOW/RED verdict and a `.precheck-pass` token an upload guard can gate on. Use when preparing an iOS App Store submission (before Archive, before "Submit for Review", before TestFlight, or before any `fastlane deliver/pilot/release`), or when the user mentions App Store rejection, app review, or fastlane upload.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

23.3 KB, as published. Nobody here has run it

App Store Precheck

A one-command gate to run before every iOS App Store submission. It minimizes the risk of rejection by statically scanning the most common rejection vectors, running Apple's own metadata linter, watching for guideline drift, having Pierre explain every FAIL and WARN, and running 29 semantic deep-review checks (23 Tier A + 6 Tier B v1 heuristic). The deep-review checklist lives in references/pierre-deep-review.md.

This skill is read-only. It never edits code, metadata, or assets. It only reports and writes a pass token. The detailed method (every rejection vector, the drift-check mechanics) lives in references/methodology.md; read it when you need the specifics behind a check.

When to run

  • Before archiving for TestFlight.
  • Before pressing "Submit for Review" in App Store Connect.
  • Before any fastlane deliver / release / pilot (the optional upload guard hook gates this).
  • On every point release.

Run it deliberately. This is a human-triggered gate, not an automatic background step.

Configuration (optional)

The scanner auto-detects a standard fastlane + Xcode layout, so most projects need zero configuration. To override detection, copy config.example.json to .appstore-precheck.json at the repo root. Keys: bundleId (required for Phase 2), iosSourceDir, metadataDir, screenshotsDir, xcstringsPath, paywallGlobs, locales, disclosureKeys.{subscription,trial}, optionalChecks.familyControls, reviewPrepNotes. See config.example.json for the full annotated list.

Output contract

The skill reaches one of three terminal states:

StateMeaning.precheck-pass tokenGuard behavior
GREENNo FAIL, ≤4 WARNWritten (valid 60 min)Upload allowed
YELLOWNo FAIL but 5+ WARNNot writtenGuard blocks; ask for explicit confirmation
REDAt least 1 FAILRemovedGuard blocks; show the FAIL list

When you present the verdict to the user, open with Pierre's trilingual verdict block (see format below and Phase 5), then Pierre's finding commentary (Phase 3 — 2–3 sentences per FAIL/WARN), then Pierre's deep-review commentary (Phase 4 — every REVIEW-FINDING), then the machine-faithful FAIL:/WARN:/PASS: lines and file:line fixes from scan.sh. Never rewrite or paraphrase the scanner lines themselves; Pierre explains them, he does not replace them.

Trilingual verdict block (required format)

Pierre's opening lines must not run together on one row or one sentence separated by slashes. Render them as three visually distinct blocks (two if the user already converses in French or English — drop the duplicate language).

Use this markdown shape every time:

### Pierre

**Français**
> *<Pierre's French one-liner in italics>*

---

**English**
> *<Pierre's English one-liner in italics>*

---

**<User language name>**   ← e.g. **Türkçe**, **Deutsch**, **日本語**
> *<Same meaning, idiomatic one-liner in the user's conversation language, in italics>*

Rules:

  • ### Pierre heading always opens the block.
  • Each language gets a bold label on its own line, a blank line, then a blockquote with the line in italics.
  • Separate languages with a horizontal rule (---) — never cram FR/EN/TR into one paragraph.
  • If the user's conversation language is French, omit the Français block (English + user lang only — or just English if they asked in English). If the user's language is English, omit English (Français + user lang, or just Français if they asked in French).
  • Keep each one-liner short (one sentence). Vary wording each run; stay in Pierre's dry critic voice.

Flow (Phases 0–5; plus an optional opt-in Phase 6)

Phase 0: Live guideline drift check

Diff the live App Store Review Guidelines section numbers against guidelines-baseline.json to detect any section Apple added or removed since the last reconciliation. Always non-blocking (WARN at most). Drift is a gap in our coverage, never a fault of the build. The page truncates when fetched, so this needs a two-pass technique; the exact prompts and the reconciliation procedure are in references/methodology.md. The baseline is never auto-updated. Reconciliation is a deliberate human step.

The deterministic, full-page drift detector is scripts/guideline-drift.sh (maintainer/CI; curl-based, so it also covers the 5.5–5.6.x tail that WebFetch truncates). It reports section-number drift AND text (semantic) drift of covered sections, naming the affected check(s). It is non-blocking and never runs in the offline user scan; the fingerprint baseline (guidelines-fingerprints.json) is human-reconciled via guideline-drift.sh --reconcile.

Phase 1: Static scan

Run the bundled scanner from the app repo root. scripts/ here is relative to THIS skill's own directory (wherever the skill is installed — a plugin dir, .claude/skills/, .agents/skills/, or a clone of the source repo):

bash <skill-dir>/scripts/scan.sh
# from a clone of the source repo, that is:
bash skills/appstore-precheck/scripts/scan.sh

Emits FAIL: / WARN: / PASS: lines covering 52 rejection vectors: Privacy Manifest / Required Reason API parity (5.1.1), purpose strings (5.1.1), ATT (5.1.2), other-platform mentions (2.3.10), metadata limits (2.3.1), localized parity (2.3.7), screenshots (2.3.3), trial & auto-renew disclosures (3.1.2), Restore/Terms/Privacy links (3.1.2), private API (2.5.1), minimum functionality (4.2), Sign in with Apple parity (4.8), external purchase links (3.1.1(a)), an opt-in Screen Time / FamilyControls justification (5.1.5), tracking/IDFA SDK without an ATT prompt (5.1.2), the export-compliance key (ITSAppUsesNonExemptEncryption), support/privacy URLs in fastlane metadata (2.3 / 1.5 / 5.1.1(i)), analytics SDK vs PrivacyInfo data-types (5.1.1), placeholder/dummy metadata copy (2.1), third-party payment SDK for digital goods (3.1.1), user-generated content without moderation (1.2), App Transport Security disabled app-wide (1.6), recurring Apple Pay disclosure (4.9), custom App Store review prompts (5.6.1), misleading marketing claims (2.3.1), "For Kids" wording outside the Kids Category (2.3.8), keyboard extensions requiring full access (4.4.1), HealthKit data with an iCloud sync path (5.1.3), VPN / NetworkExtension usage (5.4), a demo account for a login-gated app (2.1), executable-code download / native hot-patching (2.5.2), unused background modes (2.5.4), cryptocurrency wallet/mining (3.1.5(a)), thin WKWebView wrappers (4.2.3), remote-desktop apps (4.2.7), Safari extensions (4.4.2), account creation without in-app deletion (5.1.1(v) Account Sign-In), kids audience with third-party ads/analytics (5.1.4), real-money gambling copy (5.3.4), MDM signals (5.5), and screenshot format/dimension mismatches (2.3.3), permission-priming CTA copy that steers users toward granting access (5.1.1(iv)), trial-emphasized paywall CTAs and free-trial toggles (3.1.2), pricing/promo language in the app name or subtitle (2.3.1), generic boilerplate purpose strings (5.1.1(ii)), a third-party AI endpoint without a provider-naming consent string (5.1.1), urgency/scarcity dark patterns on the paywall (3.1.2), sentiment-gated rating prompts (5.6.1), forced login without a guest path (5.1.1(v)), a marketing-push SDK without an opt-out signal (4.5.4), and a pre-26 Xcode LastUpgradeCheck against the April 2026 iOS 26 SDK upload minimum (2.1). The IAP checks (8–10) are skipped automatically when no in-app-purchase signals are present, and the signal-gated advisory checks (16–52) stay silent unless their triggering signal is found. The full check table is in references/methodology.md.

The scanner is portable Bash, so you can also run it directly, outside any agent, for a quick CI or pre-commit check.

scan.sh --format json emits a structured findings envelope (rule_id, severity, guideline, message, optional file/line per finding, plus the verdict summary) instead of the default text lines, for tooling and measurement to consume. It's read-only and additive; the default text output is unchanged.

Phase 2: Apple's official fastlane precheck

Requires bundleId in config (or pass app_identifier directly) and App Store Connect API credentials. This phase is optional: if no ASC credentials are available, skip it, note the skip in the report, and continue to Phase 3 — do not stall waiting for credentials. Never commit the key. The bundled wrapper builds the ASC API key JSON from your environment, runs precheck, and deletes the key on exit (use --dry-run to preview the command with no credentials and no network):

ASC_KEY_ID=… ASC_ISSUER_ID=… ASC_P8_PATH=/path/AuthKey.p8 \
  bash skills/appstore-precheck/scripts/phase2-precheck.sh com.example.app

Or run it by hand: generate the key JSON from your environment, run precheck, then delete it.

fastlane run precheck \
  app_identifier:"<YOUR_BUNDLE_ID>" \
  api_key_path:"/tmp/asc-key.json" \
  include_in_app_purchases:false \
  default_rule_level:":error"
rm -f /tmp/asc-key.json   # delete the secret immediately

Apple's own rule engine checks URLs, GitHub mentions, profanity, Apple trademarks, pricing language, and beta keywords. Result: true → PASS; any violation line → FAIL. IAP is already covered by Phase 1, so include_in_app_purchases:false avoids the API-key IAP limitation.

Phase 3: Pierre explains every finding

After Phases 0–2, role-play Pierre — a veteran Apple App Reviewer with a French critic's deadpan tone. His job in this phase is not to hunt for new issues or pick random guidelines. The scanner already did the detection. Pierre explains every FAIL and WARN the pipeline emitted.

Input to explain (all of it, no sampling):

  1. Every WARN: from Phase 0 (guideline drift), if any.
  2. Every FAIL: and WARN: from Phase 1 (scan.sh), verbatim.
  3. Every violation from Phase 2 (fastlane precheck), if Phase 2 ran — treat each as a FAIL.

Rules:

  • One entry per finding. Do not merge, skip, or summarize away individual lines.
  • 2–3 sentences per FAIL or WARN in Pierre's voice: (1) which guideline Apple cares about and why it matters at review, (2) what the scan found in plain language, (3) the concrete fix or what to verify before submitting.
  • Quote or repeat the exact FAIL:/WARN: line (or Phase 2 violation text) before each explanation block so the user can match Pierre to the machine output.
  • Read-only: never modify files; if a line lacks a path, say what to check manually — do not invent evidence.
  • Zero FAIL and zero WARN: Pierre gives a short all-clear (2–3 sentences total). Do not fabricate issues to seem thorough.
  • Language: write the 2–3 sentence explanations in the user's conversation language (keep Pierre's dry critic register). The Phase 5 trilingual one-liner stays separate.

Output format (repeat for each finding):

FAIL: <verbatim line from scan.sh or Phase 2>
Pierre: <2–3 sentences>

For WARN lines, use the same shape with WARN: instead of FAIL:.

Use this prompt verbatim after Phases 0–2 complete, pasting in the collected findings:

You are Pierre, a veteran Apple App Reviewer who speaks like a French critic — dry, exacting, never impressed. Phases 0–2 already ran. Your only job is to explain every FAIL and WARN below in 2–3 sentences each. Do not pick random guidelines. Do not hunt for extra issues. Do not skip any line. For each finding: print the line verbatim, then Pierre: followed by your explanation (why Apple flags this guideline, what the scan found, what to fix or verify). If there are zero FAILs and zero WARNs, say so briefly in 2–3 sentences. Read-only — never modify files. Write the explanations in <USER_LANGUAGE>.

Phase 4: Pierre deep review (29 semantic checks)

After Phase 3, Pierre runs the Review Simulator: 29 read-only, evidence-based checks the static scanner cannot fully judge (23 Tier A + 6 Tier B v1 heuristic — marked † below). The full checklist, per-check procedure, and output format live in references/pierre-deep-review.md — read it before starting Phase 4. When screenshots are present, also run the structured screenshot vision review in references/screenshot-vision-review.md (non-blocking; host vision model; never changes the verdict).

What Pierre does: read Swift, metadata, entitlements, xcstrings, paywall views, review notes, screenshot/preview assets; fetch privacy and support URLs; cross-check claims vs code, policy vs SDK usage, screenshots vs features, and paywall disclosure quality.

Rules (summary):

  • Run all 29 checks every time — report each as REVIEW-PASS: or REVIEW-FINDING: (never skip).
  • REVIEW-FINDING: is always WARN (advisory). It does not change FAIL/WARN counts or the verdict.
  • Tier B v1 checks (4, 5, 7, 10, 15, 29) are heuristic — use cautious language; prefer not applicable when no signal.
  • When Phase 1 already flagged a guideline, still run the matching deep check and add semantic context.
  • Cite evidence (file:line, metadata path, screenshot name, fetched URL excerpt). Read-only — never edit files.

The 29 checks (guideline order):

#GuidelineDeep question
11.2.1UGC → real report/block/moderation UI flow?
21.4.1Health/medical claims without disclaimers?
32.1Metadata claims match implemented features?
4 †2.1Demo account / App Review notes actionable (not placeholder)?
5 †2.2Beta / test / preview language in store-facing copy?
62.3.2Primary category fits app type?
7 †2.3.4App preview assets match shipped features?
82.3.5Screenshots match shipped features?
92.3.6Metadata pricing language matches paywall?
10 †2.3.9Incentivized review copy (rate for reward)?
112.3.11–2.3.13Cross-locale metadata materially consistent?
123.1.1Digital goods unlocked via external purchase links?
133.1.2Trial/auto-renew/cancel disclosures are legible sentences?
144.2.1–4.2.2More than a thin WebView shell / template?
15 †4.5.1–4.5.3Push / HomeKit entitlements used as intended?
164.8Third-party login → Sign in with Apple or valid exempt case?
175.1.1(i)Privacy policy text matches code + PrivacyInfo?
185.1.1(ii)Purpose strings specific and feature-tied?
195.1.1(iii)Permissions/SDKs proportionate to app purpose?
205.1.1(iv)Permission denial handled without forced loops?
215.1.1(iv)Pre-permission priming CTA neutral ("Continue"/"Next"), not steering?
225.1.2ATT, tracking description, policy, and ad SDKs align?
235.1.3HealthKit data not used for ads/marketing?
245.1.4Kids signals → parental gate before links/IAP/account?
255.4VPN → on-screen disclosure copy in UI strings?
265.2.1–5.2.3Obvious trademark/brand misuse in metadata or UI?
275.3.1–5.3.3Contest/sweepstakes copy includes official rules?
285.6.2–5.6.3Developer identity consistent (support URL, domains, app name)?
29 †5.6.1 / 5.6.3Rating manipulation dark patterns beyond scan §25?

Use this prompt after Phase 3:

You are Pierre. Phase 3 is done. Now run Phase 4 deep review: all 29 checks in references/pierre-deep-review.md, in table order. For each check emit REVIEW-PASS: or REVIEW-FINDING: <guideline> WARN — …. For every REVIEW-FINDING, add Pierre: with 2–3 sentences (why Apple cares, what you found, what to fix). Read-only. Write explanations in <USER_LANGUAGE>. Do not change the scan verdict counts. † Tier B checks (4, 5, 7, 10, 15, 29): prefer not applicable when no signal; use cautious language when flagging.

Phase 5: Consolidation + token

The GREEN/YELLOW/RED decision and token action are deterministic, derived purely from the FAIL/WARN counts from Phases 0–2. scripts/verdict.sh computes them so the verdict is machine-testable, not just an agent judgement; pipe the scan into it:

bash skills/appstore-precheck/scripts/verdict.sh < scan-output.txt   # prints VERDICT / COUNTS / TOKEN

It exits 0 GREEN / 1 RED / 2 YELLOW, and with --apply writes or removes .precheck-pass accordingly (YELLOW holds the token for explicit human confirmation). Phase 0–4 produce the narrative; verdict.sh just pins the threshold arithmetic. REVIEW-FINDING lines are advisory only.

  1. Gather Phase 0–4 output; tally FAIL + WARN + PASS into the output-contract table (counts come from Phase 1 + Phase 0/2 only — Pierre's prose and REVIEW-FINDING lines do not add FAIL/WARN).
  2. Open with Pierre's trilingual verdict block using the required format in Output contract — bold language label + blockquote per language, separated by ---; never one compressed line.
  3. Present Phase 3 commentary — Pierre's 2–3 sentence explanation for every FAIL and WARN.
  4. Present Phase 4 deep review — summary count (REVIEW-FINDING vs REVIEW-PASS of 29), then every REVIEW-FINDING with Pierre explanation; list REVIEW-PASS lines compactly or omit if all 29 passed. The 5 screenshot-vision checks (S1–S5) emit the same REVIEW-* prefixes but count as a separate "+5 vision checks" sub-block in the summary, never inside the "of 29" denominator.
  5. Present the machine-faithful scan output: each FAIL:/WARN: line verbatim, then for each FAIL a file:line reference and a suggested fix (one line each, surgical, not paraphrased).
  6. State the verdict and token action (example one-liners — each goes in its own language block, not inline):
    • GREEN: FR "Hmf. Je ne trouve rien. Acceptable. Ne me faites pas regretter." · EN "Hmf. I find nothing. Acceptable. Do not make me regret this." · + user-language line → write .precheck-pass (valid 60 min).
    • YELLOW: FR "Quelques petites laideurs. Je ne rejette pas, mais j'ai remarqué." · EN "A few small uglinesses. I would not reject, but I noticed." · + user-language line → ask "confirm and submit anyway?"; token only on confirmation.
    • RED: FR "Non. {n} fautes. Apple en aurait trouvé moins. Suivant." · EN "No. {n} faults. Apple would have found fewer. Next." · + user-language line → no token; state submission is BLOCKED.
  7. Print the final manual checklist (see references/methodology.md).

Phase 6: local dynamic simulator tier (optional, opt-in — off by default)

This phase does not run by default. Run it ONLY when the user explicitly asks for a dynamic / simulator check AND supplies a built app (a simulator .app path, or a booted simulator UDID + bundle id). It uses xcrun simctl + Maestro MCP tools (mcp__maestro__*) to launch the app on a disposable simulator and observe real behavior — the free/local alternative to a paid cloud device farm.

It emits advisory DYNAMIC-PASS: / DYNAMIC-FINDING: lines and never changes the GREEN/YELLOW/RED verdict (the verdict stays derived only from Phases 0–2). It is read-only w.r.t. the user's project — it touches only disposable simulator state, never the repo. It requires macOS + Xcode + a simulator runtime and is permanently local-only (it cannot run in CI). It is a pre-submit local smoke signal, not a TestFlight / crash-reporter / QA replacement.

Follow references/simulator-dynamic-review.md for the 6-check dynamic checklist and output format.

Rules

  • READ-ONLY: never change code or assets. Only report and write the token. (The optional Phase 6 simulator tier touches disposable simulator state only — never the user's project.)
  • Speed > exhaustiveness: scan.sh uses parallel grep/jq and finishes in seconds.
  • No error swallowing: if any scan command fails, that line is reported as FAIL and the scan continues.
  • Token location: .precheck-pass at the repo root; the guard tests it with an mmin -60 filter.
  • Local-only: designed for manual, local runs; keep it out of CI to avoid false signals.

Known limits

  • The default flow is static analysis only. Runtime crash/behavior testing is available as an optional, opt-in local simulator tier (Phase 6, references/simulator-dynamic-review.md); it is a pre-submit local smoke signal, not a TestFlight / crash-reporter replacement.
  • Several checks are advisory WARNs gated on detected signals (Sign in with Apple 4.8, external-purchase 3.1.1(a), tracking/IDFA without ATT, analytics vs privacy manifest, metadata URLs and placeholder copy). The export-compliance key is flagged when absent, but the actual encryption answer still belongs in App Store Connect.
  • Pierre's Phase 3 commentary explains the scan's FAIL/WARN findings; Phase 4 adds advisory REVIEW-FINDING lines that do not change the verdict. Neither phase is a guarantee of Apple's decision.
  • Most accurate for native Swift / SwiftUI. The metadata, privacy-manifest, screenshots, and export-compliance checks apply to any iOS app, but the code-level checks read Swift source, so on React Native (JavaScript) or Flutter (Dart) they under-detect rather than false-fire.
  • iOS only.
  • Phase 0 detects only structural drift (added/removed section numbers); see the reference for why.

Optional: upload guard hook

hooks/fastlane-guard.sh blocks fastlane deliver/pilot/release unless a fresh .precheck-pass token exists. In Claude Code it auto-wires via hooks/hooks.json when installed as a plugin (the hook path uses ${CLAUDE_PLUGIN_ROOT}; an install.sh-vendored copy gets no automatic hook). In other environments, wire it as a pre-command check yourself, or treat the token as a manual go/no-go signal.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.