App Store Precheck
Une commande unique à exécuter avant toute soumission iOS sur l’App Store. Elle minimise le risque de rejet en analysant statiquement les vecteurs de rejet les plus courants, en exécutant le linter de métadonnées officiel d’Apple, en surveillant les dérives des directives, et en simulant une revue contradictoire.
Cette compétence est en lecture seule. Elle ne modifie jamais le code, les métadonnées ou les
ressources. Elle se contente de rapporter les résultats et d’écrire un jeton de passage. La
méthode détaillée (chaque vecteur de rejet, les mécanismes de vérification des dérives) se
trouve dans references/methodology.md ; lisez-la lorsque vous avez
besoin des spécificités derrière une vérification.
Quand l’exécuter
- Avant d’archiver pour TestFlight.
- Avant de cliquer sur « Soumettre pour révision » dans App Store Connect.
- Avant toute commande
fastlane deliver/release/pilot(le hook de garde optionnel se déclenche ici). - À chaque mise à jour mineure.
Exécutez-la délibérément. C’est une porte de contrôle déclenchée par l’humain, pas une étape automatique en arrière-plan.
Configuration (optionnelle)
L’analyseur détecte automatiquement une disposition standard fastlane + Xcode. La plupart des
projets n’ont donc aucune configuration nécessaire. Pour forcer la détection, copiez
config.example.json vers .appstore-precheck.json à la racine du
dépôt. Clés : bundleId (obligatoire pour la Phase 2), iosSourceDir, metadataDir,
screenshotsDir, xcstringsPath, paywallGlobs, locales, disclosureKeys.{subscription,trial},
optionalChecks.familyControls, reviewPrepNotes. Consultez config.example.json pour la liste
détaillée.
Contrat de sortie
La compétence atteint l’un des trois états finaux :
| État | Signification | Jeton .precheck-pass | Comportement de la garde |
|---|---|---|---|
| VERT | Aucun FAIL, ≤2 WARN | Écrit (valide 60 min) | Envoi autorisé |
| JAUNE | Aucun FAIL mais 3+ WARN | Non écrit | La garde bloque ; demande une confirmation explicite |
| ROUGE | Au moins 1 FAIL | Supprimé | La garde bloque ; affiche la liste des FAIL |
Lorsque vous présentez le verdict à l’utilisateur, commencez par une seule ligne dans le
personnage de Pierre (le critique de l’App Store français, voir Phase 3), puis passez
directement au détail chirurgical. La voix est une fine enveloppe. Les données en dessous –
la liste FAIL/WARN, les références fichier:ligne et les correctifs – restent propres et
fidèles à la machine. Ne réécrivez ni ne paraphrasez jamais la sortie de scan.sh, et
limitez Pierre à une seule ligne.
Déroulement (5 phases : 0 à 4)
Phase 0 : Vérification des dérives des directives en direct
Comparez les numéros de section des Règles de l’App Store
avec guidelines-baseline.json pour détecter toute section qu’Apple a ajoutée ou supprimée
depuis la dernière réconciliation. Toujours non-bloquant (WARN au maximum). Une dérive est
une lacune de notre couverture, jamais une faute de la build. La page est tronquée lors de sa
récupération, donc elle nécessite une technique en deux passages ; les invites exactes et la
procédure de réconciliation se trouvent dans
references/methodology.md. La
référence n’est jamais mise à jour automatiquement. La réconciliation est une étape humaine
délibérée.
Phase 1 : Analyse statique
bash skills/appstore-precheck/scripts/scan.sh
Émet des lignes FAIL: / WARN: / PASS: couvrant 20 vecteurs de rejet : parité du manifeste de
confidentialité (5.1.1(v)), chaînes de finalité (5.1.1), ATT (5.1.2), mentions d’autres
plateformes (2.3.10), limites des métadonnées (2.3.1), parité localisée (2.3.7), captures
d’écran (2.3.3), mentions des essais et renouvellements automatiques (3.1.2), liens
Restore/Termes/Confidentialité (3.1.2), API privée (2.5.1), fonctionnalité minimale (4.0),
parité « Connexion avec Apple » (4.8), liens d’achat externes (3.1.1(a)), justification
optionnelle pour Screen Time / FamilyControls (5.1.5), SDK de suivi/IDFA sans invite ATT
(5.1.2), clé de conformité à l’exportation (ITSAppUsesNonExemptEncryption), URLs de support
et de confidentialité dans les métadonnées fastlane (2.3), types de données SDK analytics vs
PrivacyInfo (5.1.1), et contenu de métadonnées factice (2.1). Les vérifications d’achats
intégrés (8–10) sont automatiquement ignorées en l’absence de signaux d’achats intégrés. Le
tableau complet des vérifications se trouve dans
references/methodology.md.
L’analyseur est un script Bash portable, vous pouvez donc l’exécuter directement, en dehors de tout agent, pour une vérification rapide de CI ou pre-commit.
Phase 2 : fastlane precheck officiel d’Apple
Nécessite bundleId dans la configuration (ou passez app_identifier directement) et des
identifiants API App Store Connect. Ne commitez jamais la clé. L’enveloppe fournie construit
le fichier JSON de la clé API ASC à partir de votre environnement, exécute precheck et supprime
la clé à la sortie (utilisez --dry-run pour prévisualiser la commande sans identifiants et
sans réseau) :
ASC_KEY_ID=… ASC_ISSUER_ID=… ASC_P8_PATH=/chemin/AuthKey.p8 \
bash skills/appstore-precheck/scripts/phase2-precheck.sh com.example.app
Ou exécutez-la manuellement : générez le fichier JSON de la clé depuis votre environnement, lancez precheck, puis supprimez la clé.
fastlane run precheck \
app_identifier:"<VOTRE_BUNDLE_ID>" \
api_key_path:"/tmp/asc-key.json" \
include_in_app_purchases:false \
default_rule_level:":error"
rm -f /tmp/asc-key.json # supprimez le secret immédiatement
Le moteur de règles d'Apple vérifie les URLs, les mentions GitHub, les grossièretés, les marques
déposées Apple, le langage de tarification et les mots-clés bêta. Result: true → PASS ; toute
ligne de violation → FAIL. Les achats intégrés sont déjà couverts par la Phase 1, donc
include_in_app_purchases:false évite la limitation d’achats intégrés liée à la clé API.
Phase 3 : Revue contradictoire (la plus importante)
Envoyez un sous-agent qui incarne Pierre, un examinateur sceptique de l’App Store avec le palais exigeant d’un critique français. Utilisez cette invite textuellement, en remplissant les spécificités de l’application :
Vous êtes Pierre, un examinateur vétéran de l’App Store qui critique comme un critique français. Vous avez vu dix mille rejets et aucun ne vous impressionne. Une nouvelle soumission vient d’atterrir sur votre bureau. Votre travail est de tenter de la rejeter de manière réaliste, sans aucun biais d’approbation. Choisissez 5 éléments aléatoires des directives répartis sur les sections (un 2.x, deux 3.x avec un poids sur le paywall, un 4.x, un 5.x) ; choisissez une combinaison différente à chaque exécution (incluez une ligne de graine pour que les relances varient). Pour chaque élément : (Pass A) grep les fichiers pertinents pour au moins 2 preuves concrètes : métadonnées pour 2.3.x ; la vue paywall + le catalogue de chaînes pour 3.1.x ; Core/navigation pour 4.x ; Info.plist + PrivacyInfo pour 5.1.x. (Pass B) demandez-vous « en tant qu’examinateur, sur quelle base signalerais-je cela ? » (Pass C) rédigez un brouillon de rejet dans la vraie voix d’Apple (Directive X.Y.Z – Catégorie / Nous avons remarqué… / Spécifiquement… / Prochaines étapes… / Ressources…). Attribuez à chaque élément un risque : REJET-CERTAIN / RISQUE-DE-REJET / AVERTISSEMENT / PASS. Terminez par une recommandation de soumission (SUSPENDRE / SOUMETTRE AVEC AVERTISSEMENTS / OK). Restez sous les 500 mots. Incluez au moins un PASS et au moins un élément comportant un risque, pour donner une distribution réaliste.
Phase 4 : Consolidation + jeton
La décision VERT/JAUNE/ROUGE et l’action sur le jeton sont déterministes, dérivées
uniquement des nombres de FAIL/WARN. scripts/verdict.sh les calcule pour
que le verdict soit testable par machine, et pas seulement un jugement d’agent ; pipez la sortie
de l’analyse dedans :
bash skills/appstore-precheck/scripts/verdict.sh < scan-output.txt # affiche VERDICT / COUNTS / TOKEN
Il se termine avec 0 VERT / 1 ROUGE / 2 JAUNE, et avec --apply écrit ou supprime
.precheck-pass en conséquence (JAUNE conserve le jeton pour confirmation humaine explicite).
Les phases 0 à 3 produisent toujours la narration ; verdict.sh fige le calcul des seuils.
- Rassemblez la sortie des phases 0 à 3 ; comptez les FAIL + WARN + PASS dans le tableau du contrat de sortie.
- Pour chaque FAIL, donnez une référence
fichier:ligneet un correctif suggéré. - Ouvrez le verdict avec une ligne dans la voix de Pierre (variez le libellé à chaque
exécution ; restez concis et dans son registre de critique français impassible), puis passez au
détail brut. Décidez :
- VERT : Pierre, à contrecœur : « Hmf. Je ne trouve rien. Acceptable. Ne me fais pas
regretter ça. » puis
date +%s > .precheck-pass && echo "token written"(valide 60 min). - JAUNE : Pierre : « Quelques petites laideurs. Je ne rejette pas, mais j’ai remarqué. » Listez les WARN simplement, demandez à l’utilisateur « confirmer et soumettre quand même ? », écrivez le jeton seulement sur confirmation.
- ROUGE : Pierre : « Non. {n} fautes. Apple en aurait trouvé moins. Suivant. » Pas de
jeton ; puis la liste brute des FAIL avec
fichier:ligne+ correctifs, et dites clairement que la soumission est BLOQUÉE. La ligne de Pierre n’est qu’une touche. La liste FAIL/WARN, lesfichier:ligneet les correctifs en dessous restent simples et chirurgicaux, jamais paraphrasés.
- VERT : Pierre, à contrecœur : « Hmf. Je ne trouve rien. Acceptable. Ne me fais pas
regretter ça. » puis
- Affichez la liste de contrôle manuelle finale (voir
references/methodology.md).
Règles
- LECTURE SEULE : ne modifiez jamais le code ou les ressources. Rapportez uniquement et écrivez le jeton.
- Vitesse > exhaustivité :
scan.shutilise des grep/jq parallèles et se termine en quelques secondes. - Pas d’erreur ignorée : si une commande d’analyse échoue, cette ligne est rapportée comme FAIL et l’analyse continue.
- Emplacement du jeton :
.precheck-passà la racine du dépôt ; la garde le teste avec un filtremmin -60. - Local uniquement : conçu pour des exécutions manuelles locales ; gardez-le hors de l’intégration continue pour éviter de faux signaux.
Limites connues
- Aucun test de crash à l’exécution ; ce rôle revient à TestFlight et à un rapporteur de crash. Analyse statique uniquement.
- Plusieurs vérifications sont des WARN consultatifs déclenchés par des signaux détectés (Connexion avec Apple 4.8, achats externes 3.1.1(a), suivi/IDFA sans ATT, analytics vs manifeste de confidentialité, URLs de métadonnées et contenu factice). La clé de conformité à l’exportation est signalée lorsqu’elle est absente, mais la réponse réelle sur le chiffrement appartient toujours à App Store Connect.
- La revue contradictoire est une simulation heuristique, pas une garantie de la décision d’Apple.
- Plus précise pour du Swift / SwiftUI natif. Les vérifications de métadonnées, manifeste de confidentialité, captures d’écran et conformité à l’exportation s’appliquent à toute application iOS, mais les vérifications au niveau du code lisent le code source Swift ; donc sur React Native (JavaScript) ou Flutter (Dart), elles sous-détectent plutôt que de produire de faux positifs.
- iOS uniquement.
- La Phase 0 détecte uniquement les dérives structurelles (numéros de section ajoutés/supprimés) ; consultez la référence pour savoir pourquoi.
Optionnel : hook de garde d’envoi
hooks/fastlane-guard.sh bloque fastlane deliver/pilot/release sauf si un jeton .precheck-pass
récent existe. Dans Claude Code, il s’active automatiquement via hooks/hooks.json. Dans d’autres
environnements, branchez-le vous-même comme vérification pré-commande, ou traitez le jeton comme
un signal manuel go/no-go.