feat(mobile): recette terrain iPhone 15 Pro — 7/7 validés (ADR-005)

Expo Go (App Store) bloqué par le retard d'approbation Apple (SDK 54
contre notre SDK 57) — décision : builds natifs locaux (Xcode/Android
Studio, signature Apple ID personnel gratuite) pour la vraie recette
terrain, Expo Go conservé pour l'aperçu sans installation ailleurs.

- ADR-005 : deux canaux de distribution, managed workflow conservé.
- apps/mobile/.env : EXPO_PUBLIC_USE_RN_FETCH=1 — fix permanent d'un
  bug SDK 57 (fetch global expo/fetch incompatible avec l'upload
  multipart natif {uri,name,type}, classé à tort « réseau
  indisponible » par notre propre gestion d'erreur).
- package.json/app.json : scripts ios/android pointés sur les builds
  natifs (expo run:*) plutôt que expo start (Expo Go), identifiants
  de bundle générés par le prebuild.
- Recette iOS (iPhone 15 Pro physique) : connexion démo, scan caméra
  réel (résolution locale + rejet QR étranger), vrai mode avion (D1),
  conflit de version tranché par l'humain avec photo réellement
  téléversée (D2/D5), persistance de la file à travers fermeture et
  reconstruction de l'app, suggestions R5 au vrai modèle, purge de
  sécurité au changement de compte (ADR-003) — 7/7 validés, la
  validation terrain due depuis release/r4 est levée pour iOS.
- Android : build natif sur émulateur, passage santé complet
  (connexion, navigation, écran Scanner, résolution de référence).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
pr-daaif
2026-07-19 20:12:48 +01:00
parent 557435b81a
commit 189e8e8162
7 changed files with 157 additions and 5 deletions

View File

@@ -0,0 +1,91 @@
# ADR-005 — Distribution mobile : builds natifs + Expo Go pour l'aperçu
- **Statut** : acceptée (recette terrain R4/R5, 19 juillet 2026)
- **Contexte** : la recette « mode avion » sur téléphone (due depuis `release/r4`) a buté
sur Expo Go dès la première tentative — l'app installée depuis l'App Store affiche
« supported SDK 54 » et refuse le projet (SDK 57). Apple a un retard d'approbation
structurel sur les nouvelles versions d'Expo Go (constaté : seul SDK 54 installable sur
iPhone physique début mai 2026, alors qu'Expo publiait déjà SDK 57) — ce n'est pas un
problème côté projet, et rétrograder le SDK du projet pour suivre Expo Go serait
disproportionné pour un aléa hors de notre contrôle.
## Décision
**Deux canaux de distribution mobile, en parallèle :**
1. **Builds natifs locaux** (Xcode pour iOS, Android Studio pour Android) — la voie pour
tester *vraiment* sur un appareil (mode avion réel, caméra réelle, persistance),
indépendante des aléas de version d'Expo Go. Signature gratuite (Apple ID personnel →
Personal Team Xcode, sans compte payeur Apple Developer Program) pour les builds de
recette ; à revoir si une distribution à plusieurs testeurs (étudiants) est nécessaire.
2. **Expo Go conservé** comme canal léger « sans installation » pour prévisualiser
rapidement sur d'autres appareils, quand la version d'Expo Go de l'appareil correspond
au SDK du projet — rien à changer, c'est le fonctionnement actuel de `expo start`.
Le **managed workflow est conservé** : `ios/` et `android/` restent générés à la demande
par `expo prebuild`/`expo run:*` (déjà gitignorés), jamais committés.
## Obstacles rencontrés en recette — et leurs correctifs
Cinq heures de recette terrain ont débusqué des problèmes réels, chacun avec un
correctif ou une limite désormais documentés pour ne pas être redécouverts à froid :
1. **Nom d'appareil avec apostrophe** rejeté par `expo run:ios --device "iPhone d'aziz"`
→ cibler par **UDID** (`xcrun xctrace list devices`), jamais par nom.
2. **Ne jamais court-circuiter `expo run:ios`/`expo run:android` par un `xcodebuild`
manuel** : un contournement de signature via `xcodebuild -allowProvisioningUpdates`
a produit une app qui compilait mais **crashait au lancement**
(`dyld: Library not loaded: @rpath/React.framework/React`, signal 6) — la commande
officielle inclut des étapes (embed frameworks, configuration Expo) qu'un appel manuel
saute silencieusement.
3. **Incompatibilité des modules précompilés (SDK 56/57)** : Expo distribue par défaut
des XCFrameworks précompilés pour ses modules, et React Native 0.86 précompile aussi
son cœur (`RCT_USE_PREBUILT_RNCORE`) — ce projet, en liaison statique (pas de
`use_frameworks!`), n'est pas compatible avec ces frameworks dynamiques. C'est la
cause RÉELLE du crash ci-dessus (le contournement xcodebuild n'a fait que le révéler
plus tôt). **Fix** : `EXPO_USE_PRECOMPILED_MODULES=0` et `RCT_USE_PREBUILT_RNCORE=0`
avant `pod install`/`expo run:ios` — tout se recompile depuis les sources.
4. **`fetch` global incompatible avec l'upload multipart natif** : depuis SDK 57, `fetch`
pointe vers `expo/fetch` (WinterCG), qui ne comprend pas le raccourci FormData
`{uri, name, type}` utilisé pour joindre une photo (D5) — échec
`Unsupported FormDataPart implementation`, **classé à tort comme une coupure réseau**
par notre gestion d'erreur (`catch` générique de `envoyerSaisie`), donc invisible et
mis en boucle de retry silencieuse. **Fix permanent** : `EXPO_PUBLIC_USE_RN_FETCH=1`
dans `apps/mobile/.env` (committé — ce n'est pas un secret), qui restaure le `fetch`
classique de React Native, compatible avec ce raccourci.
5. **Découverte réseau de Metro par le dev client peu fiable** : un `git clone` de
dépendance (`ZXingObjC`) a échoué deux fois sur timeout réseau (retry + réglages git
`http.postBuffer`/`http.lowSpeedTime` ont suffi) ; l'IP LAN du Mac a changé quatre fois
pendant la session ; et **rouvrir l'app depuis son icône après une fermeture complète
échoue souvent** (`No script URL provided`, RCTFatal) même quand Metro tourne et est
joignable — la découverte Bonjour du dev client n'est pas fiable sur ce Wi-Fi. **Fix
fiable** : relancer via `npx expo run:ios --device <udid>` (ou `run:android`), qui
transmet l'adresse de Metro explicitement par deep link
(`siop://expo-development-client/?url=...`) au lieu de compter sur la découverte
automatique.
6. **Limite de fond, pas un bug** : un build **dev client** n'a aucun bundle JS embarqué —
il ne peut PAS démarrer à froid en étant déjà hors-ligne (aucun fallback possible). La
persistance de la file (D1) reste validée par un scénario réaliste et suffisant :
app **déjà ouverte**, saisie mise en file hors-ligne, **process tué puis relancé** avec
le réseau qui revient — la file survit et se rejoue (vérifié : un OT passé `ON_HOLD`
après fermeture complète + reconstruction de l'app). Valider un vrai démarrage à froid
totalement hors-ligne exigerait un build **Release** (bundle embarqué) — hors périmètre
d'une recette de développement, à envisager si ce cas précis devient un critère de
recette client.
7. **Détail UX, pas un bug** : le badge de compte (avatar) répond à un **appui long**
(`onLongPress`), pas un tap simple — c'est le déclencheur de déconnexion
(`app/(tabs)/journee.tsx`), volontairement sans confirmation puisqu'il est purement
local (jeton, file, cache — jamais le réseau, ADR-003). Vaut d'être rendu plus
visible si un retour utilisateur le demande.
## Conséquences
- La recette terrain iOS (7 points : connexion, scan caméra réel avec rejet de QR
étranger, vrai mode avion, conflit D2 tranché par l'humain, persistance, suggestions R5
au vrai modèle, purge de sécurité au changement de compte) est **validée sur iPhone 15
Pro physique** — la validation due depuis `release/r4` est levée.
- Android : build natif installé et passage santé fait sur **émulateur**
(`Pixel_3a_API_34`, connexion démo, navigation, écran Scanner sans plantage, résolution
de référence confirmée via la saisie manuelle — équivalent du scan sans caméra réelle).
La recette terrain sur **appareil Android physique** (vrai mode avion, vraie caméra)
reste un reste, comme pour iOS avant aujourd'hui, en l'absence d'appareil disponible.

View File

@@ -4,6 +4,53 @@ Trace chronologique des sessions (la plus récente en premier). Le **playbook**
---
## 2026-07-19 — Pr. Daaif (+ Claude) — Recette terrain mobile sur iPhone 15 Pro : les 7 points validés (ADR-005)
**Actions**
- **La recette « mode avion » due depuis `release/r4`** a enfin pu se jouer sur un vrai appareil (iPhone 15 Pro
du référent). Premier obstacle immédiat : Expo Go (App Store, dernière version) refuse le projet — « supported
SDK 54 » contre notre SDK 57, retard d'approbation Apple structurel, hors de notre contrôle. **Décision du
référent** : construire des builds natifs locaux (Xcode/Android Studio, signature gratuite via Apple ID
personnel) pour la vraie recette terrain, tout en conservant Expo Go comme canal léger pour un aperçu sans
installation ailleurs — actée dans **ADR-005**.
- **Cinq obstacles techniques réels** rencontrés et corrigés en route (détaillés dans ADR-005) : ciblage par UDID
plutôt que nom d'appareil (apostrophe) ; ne jamais contourner `expo run:ios` par un `xcodebuild` manuel (a
produit un crash `dyld: Library not loaded React.framework`) ; **incompatibilité des modules Expo/RN
précompilés (SDK 56/57) avec la liaison statique du projet** — fix `EXPO_USE_PRECOMPILED_MODULES=0` +
`RCT_USE_PREBUILT_RNCORE=0` ; **`fetch` global (`expo/fetch`) incompatible avec l'upload multipart natif**
(`Unsupported FormDataPart implementation`, classé à tort « réseau indisponible » par notre propre gestion
d'erreur) — fix permanent `EXPO_PUBLIC_USE_RN_FETCH=1` committé dans `apps/mobile/.env` ; découverte réseau du
dev client peu fiable sur ce Wi-Fi (IP du Mac changée 4 fois, reprises via `expo run:ios --device <udid>` qui
transmet l'adresse de Metro par deep link plutôt que par découverte automatique).
- **Recette terrain iOS — 7/7 points validés sur iPhone physique** : (1) connexion démo ; (2) scan caméra réel —
résolution locale d'une étiquette réelle (D4) + rejet propre d'un QR étranger ; (3) vrai mode avion (D1) — file
d'écriture visible ; (4) verrou optimiste (D2) — conflit provoqué en modifiant l'OT côté serveur pendant la
coupure, écran Synchro affichant le conflit, tranché par l'humain, OT clos avec bilan complet et **photo
réellement téléversée** (885 Ko, compression D5 confirmée) ; (5) persistance — saisie en file survivant à une
fermeture complète + reconstruction de l'app, resynchronisée au retour réseau (limite documentée : un build dev
client ne peut PAS démarrer à froid hors-ligne, aucun bundle embarqué — un vrai test « cold start offline »
demanderait un build Release, hors périmètre aujourd'hui) ; (6) suggestions IA R5 au vrai modèle sur le
téléphone, chips appliqués pré-remplissant les sélecteurs ; (7) sécurité ADR-003 — déconnexion (appui long,
purement locale) puis bascule de compte démo, file et cache vides, aucune fuite entre comptes.
- **Android** : build natif installé sur l'émulateur (`Pixel_3a_API_34`, Android Studio déjà en place) ; passage
santé complet — connexion démo, navigation Ma journée → fiche appareil (résolution de référence manuelle,
équivalent du scan sans caméra d'émulateur, confirmée fonctionnelle), écran Scanner sans plantage.
**Décisions**
- ADR-005 actée : deux canaux de distribution mobile (builds natifs pour la vraie recette, Expo Go pour l'aperçu
sans installation), managed workflow conservé (`ios/`/`android/` jamais committés).
- La validation terrain due depuis `release/r4` est **levée pour iOS**. La recette sur **appareil Android
physique** (vrai mode avion, vraie caméra) reste un reste, faute d'appareil disponible aujourd'hui — même
situation qu'iOS avant cette session.
**Prochaine étape** : recette Android sur appareil physique quand disponible ; redéploiement Dokploy
(`release/r3` puis r5 avec `AI_SERVICE_TOKEN`) ; calibrage `AI_SEUIL_*` sur corpus SPELEV réel ; secret
`DOKPLOY_WEBHOOK_URL`.
---
## 2026-07-17 — Pr. Daaif (+ Claude) — 🏁 R5 CLOSE : tag `release/r5`
**Actions**