# Build du correctif « Live Activity fantôme » — runbook Mac **Écrit le 2026-08-11.** Concerne le commit `5e9b4a3` (*La Live Activity restait affichée après la fin de séance*). Compte **15 minutes**. C'est un build simple : aucun fichier nouveau, aucune target à créer, aucune capability à ajouter. Les cinq fichiers modifiés sont déjà membres de leurs targets (vérifié dans `project.pbxproj`). ⚠️ **La montre doit être rebuildée aussi**, pas seulement l'iPhone : le message de fin de séance part de `CoachWatch`. Un build iPhone seul ne corrige rien. --- ## Étape 1 — Récupérer le code **Xcode fermé** (`Cmd+Q`, pas juste la fenêtre) : ```bash cd ~/coach-ios git pull git log --oneline -1 # doit afficher 5e9b4a3 ou plus récent ``` Pas besoin de `npm install` ni de `npx cap sync ios` : aucune dépendance ni config Capacitor n'a bougé. Si Xcode râle sur `no such module 'Capacitor'`, alors seulement, lance `npx cap sync ios` (Xcode fermé). ## Étape 2 — Build sur l'iPhone ```bash open ios/App/App.xcodeproj ``` 1. iPhone branché et déverrouillé, **Apple Watch au poignet et appairée** 2. Schéma **App** + ton iPhone comme destination 3. **Run** (`Cmd+R`) Pas de *Clean Build Folder* nécessaire — cinq fichiers modifiés, la compilation incrémentale suffit. ## Étape 3 — Installer la nouvelle app sur la Watch L'app watchOS est embarquée dans l'app iPhone, mais watchOS ne la réinstalle pas toujours tout de suite. **Le plus fiable : builder directement dessus.** 1. Schéma **CoachWatch** (menu des schémas, en haut) 2. Destination : ton **Apple Watch Ultra 3** 3. **Run** (`Cmd+R`) Le premier lancement sur la montre peut demander 2-3 minutes et afficher « Waiting to attach » — c'est normal. > Alternative sans Xcode : iPhone → app **Watch** → *Apps installées* → Coach > → attendre la réinstallation. Plus lent et moins déterministe. **Vérifier que la montre a bien la nouvelle version** : dans les logs (étape 4), la ligne `fin de séance signalée à l'iPhone` n'existe que dans ce build. ## Étape 4 — Tester (3 minutes) Le test se fait dans cet ordre, en gardant Console.app ouvert sur le Mac : ``` Console.app → ton iPhone dans la colonne de gauche Filtre : subsystem:ch.hypnotruck.coach ``` 1. **Démarrer** une séance depuis l'app Coach sur la Watch → la Live Activity apparaît sur l'iPhone (cœur + FC qui bouge) → log `Live Activity started` 2. **Terminer** la séance sur la Watch → log côté montre : `fin de séance signalée à l'iPhone (reason=session-ended)` → log côté iPhone : `fin de séance reçue de la Watch` puis `Live Activity ended` → **la Live Activity disparaît immédiatement de l'écran** C'est ce dernier point qui valide le correctif. Avant, elle restait ~4 h avec la dernière FC figée. 3. **Écran verrouillé** : referme l'iPhone pendant la séance, termine sur la montre, rallume l'écran. La bannière ne doit plus être là. C'est le cas qui échouait vraiment — le watchdog reposait sur un `Timer` qui ne tourne pas quand l'app est suspendue. 4. Vérifie au passage que **la FC remonte toujours** sur `/live` (le flux de samples n'a pas changé, mais `CoachLiveBridge` a été touché). ### Le cœur fantôme actuellement à l'écran Il vient de l'ancien build. Il partira soit au premier `liveEnded` reçu (la nouvelle app reprend la main dessus via `adoptExisting()`), soit après 6 h, soit tout de suite si tu le fermes à la main (appui long → fermer). ## Étape 4bis — Les boutons Pause / Terminer *(build du 2026-08-11, second lot)* La Live Activity porte maintenant deux boutons, comme l'app Exercice : **Pause / Reprendre** et **Terminer**. Ils apparaissent sur la bannière de l'écran verrouillé et dans l'île dynamique **dépliée** (appui long sur l'île) — pas en mode compact, où iOS n'accepte aucun bouton. Aucune manipulation Xcode : les intents vivent dans `CoachLiveActivityAttributes.swift`, déjà membre des deux targets. À tester, séance en cours : 1. **Appui long sur l'île** → les deux boutons apparaissent 2. **Pause** → la séance se met en pause sur la montre, le bouton devient **Reprendre**, et la page `/live` ne bascule **pas** en « Connexion perdue » (le watchdog est neutralisé pendant la pause) 3. **Reprendre** → la FC repart 4. **Terminer** → la séance s'arrête sur la montre et la Live Activity disparaît immédiatement ⚠️ **Limite Apple, à connaître avant de crier au bug** : `WCSession.isReachable` retombe à false quand l'app watchOS tourne sa séance en arrière-plan profond (poignet baissé, écran éteint). La commande bascule alors sur `transferUserInfo` : elle est **garantie mais différée**, donc elle s'applique au réveil de l'app montre et non à la seconde. Poignet levé, c'est immédiat. Aucune API publique ne fait mieux. Détail mineur : en **mode test** (FC simulée), la pause bascule l'état mais la simulation continue d'émettre des samples. Sans effet sur une vraie séance. ## Étape 5 — TestFlight *(facultatif)* Rien de spécifique ici : c'est la procédure habituelle, étapes 7 à 9 de `docs/SESSION-XCODE.md`. Pense à incrémenter le numéro de build. --- ## Si ça ne compile pas | Erreur | Cause probable | Solution | |---|---|---| | `cannot find 'CoachAuth' in scope` | `CoachAuth.swift` absent (non versionné) | étape 2 de `docs/SESSION-XCODE.md` | | `no such module 'Capacitor'` | sync manquant | `npx cap sync ios`, Xcode fermé | | `value of type 'ConnectivityManager' has no member 'sendEnded'` | la target CoachWatch compile un ancien `ConnectivityManager.swift` | vérifie que le `git pull` a bien eu lieu et que Xcode n'a pas restauré un pbxproj en mémoire | | erreur de signing sur CoachWatch | profil pas régénéré | Signing & Capabilities → décoche/recoche *Automatically manage signing* | En cas de blocage, envoie-moi l'étape, le message exact, et : ```bash cd ~/coach-ios && git log --oneline -3 && git status -sb ```