Pendant un test, le telephone est dans une poche : la montre est le seul ecran
consultable en courant, et c'est elle qui porte deja vitesse, distance et FC via
WorkoutManager.
Deux protocoles. Paliers d'une minute de 8 km/h par pas de 0,5, avec haptique au
changement et l'ecart a l'allure cible affiche — la vitesse brute demanderait un
calcul mental a chaque foulee. Ou six minutes, la distance parcourue donnant
directement la VMA.
⚠️ AUCUN calcul cote montre : elle transmet des mesures (paliers tenus,
distance, FC max), le serveur calcule. Deux implementations d'une meme formule
divergent toujours. Le POST passe par CoachRoutineBridge, qui a deja
l'abonnement NotificationCenter, la base d'API et le cookie — un fichier de plus
devrait etre cable a la main dans le pbxproj.
⚠️ Les paliers avancent sur `elapsedSec` de HKLiveWorkoutBuilder, PAS sur un
compteur accumule au fil du Timer de la vue : ce Timer ne tourne pas quand
l'ecran s'eteint, c'est-a-dire des que le poignet retombe — soit pendant
l'essentiel du test. Meme famille de piege que la Live Activity fantome.
L'envoi reprend la livraison differee de la routine : un test se fait dehors,
l'iPhone peut etre hors de portee, et refaire un test maximal parce qu'un
message s'est perdu n'est pas acceptable. Si l'envoi echoue, les chiffres
restent affiches pour une saisie manuelle.
VmaTestView.swift est un fichier NOUVEAU : cable dans le pbxproj (4 entrees,
sauvegarde project.pbxproj.bak-vma). Verification : `grep -c "VmaTestView.swift
in Sources"` doit rendre 2 (declaration + phase de compilation = 1 cible).
⚠️ NON COMPILE — a builder au prochain passage sur le Mac.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comme l'app Exercice : deux boutons sur la bannière de l'écran verrouillé et
dans l'île dépliée. iOS n'expose aucun geste de balayage pour révéler des
actions — depuis iOS 17, ce sont des boutons intégrés (App Intents), et c'est
le seul mécanisme public.
- Intents `CoachTogglePauseIntent` / `CoachEndWorkoutIntent`, conformes à
LiveActivityIntent (sans quoi `perform()` n'est jamais appelé).
- Ils vivent dans CoachLiveActivityAttributes.swift, seul fichier déjà membre
des deux targets : un fichier neuf imposerait une manip Target Membership
dans Xcode, source d'erreurs répétées ici.
- Commandes relayées à la montre par WCSession, avec repli transferUserInfo :
isReachable retombe à false quand la séance tourne en arrière-plan profond,
la commande est alors différée au réveil de l'app montre.
- L'état de pause remonte depuis HKWorkoutSession (seule source fiable : la
montre peut mettre en pause d'elle-même) et bascule le libellé du bouton.
- Le watchdog de LiveStore est neutralisé pendant la pause : sans ça, l'absence
de samples aurait affiché « connexion perdue » puis terminé l'activité.
- L'app watchOS gagne le bouton Pause qui lui manquait, aligné sur le même état.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
La montre n'annonçait pas la fin : le flux de samples s'arrêtait, et
l'iPhone ne pouvait pas distinguer « séance terminée » de « montre hors de
portée ». La Live Activity restait donc figée sur la dernière FC reçue —
un cœur à 74 bpm en permanence dans la Dynamic Island.
Trois causes cumulées, toutes corrigées :
1. Aucun message de fin. WorkoutManager envoie maintenant `liveEnded` à
l'arrêt de la session HealthKit, en cas d'échec, et en mode test.
2. Le seul chemin de terminaison côté iPhone était le watchdog de
LiveStore, un Timer du main runloop — qui ne tourne pas quand l'app est
suspendue en arrière-plan, c'est-à-dire pendant toute la séance.
`endLive()` termine désormais l'activité dès réception du message.
3. `dismissalPolicy: .default` laissait la bannière ~4 h APRÈS la fin.
Passé en `.immediate`.
Filet de sécurité : `adoptExisting()` au lancement reprend la main sur une
activité orpheline (app tuée pendant une séance, `current` perdu) et
termine celles de plus de 6 h.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pendant natif du backend coach_sportif (commit 772385b, déjà en prod).
- CoachRoutineBridge (target App) : pousse blocs + état du jour vers la Watch
via updateApplicationContext, relaie les coches Watch vers /api/routine/day
puis notifie la WebView (event routineUpdated)
- CoachWatch/RoutineStore : état persisté en UserDefaults, reset au changement
de jour. Les blocs viennent de l'iPhone — rien codé en dur côté watchOS, donc
modifier la routine côté web ne demande aucun rebuild
- CoachWatch/RoutineView : liste cochable, progression, haptique, bouton de
renvoi si la synchro a échoué
- ConnectivityManager (watch) : sendRoutine() en sendMessage avec repli
transferUserInfo (coche faite iPhone hors de portée -> livraison différée)
⚠️ CoachLiveBridge touché : routeIfNotLiveSample() aiguille les messages
routineDone vers NotificationCenter. WCSession.delegate est unique côté iPhone,
impossible d'en ajouter un second. Le flux live workout n'est pas modifié mais
doit être re-vérifié au build.
Les 3 nouveaux fichiers sont référencés dans project.pbxproj (bonne target) :
aucun "Add Files" à faire sur le Mac. Runbook : docs/routine-watch-runbook-mac.md
NON COMPILÉ — nécessite une session Xcode sur le Mac mini.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
L'App Group n'étant pas partagé entre appareils, le snapshot transite par
WatchConnectivity : iPhone CoachWidgetBridge.pushToWatch (updateApplicationContext)
-> CoachWatch/ConnectivityManager reçoit -> App Group de la montre + reload des
complications. Nouvelles complications (CoachWatchWidgets/CoachWatchComplications.swift) :
Forme (accessoryCircular/corner, Gauge) + Séance du jour (accessoryRectangular/inline).
⚠️ Reste Mac : créer la target widget watchOS + App Group/capability + Target
Membership. Runbook complet dans docs/widgets-runbook-mac.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Logger unified (capturable via simctl) côté watch (envoi) et iPhone (réception)
pour valider le pont WCSession. Hook AUTOSIM (env var) démarre le mode test au
lancement, pour validation automatisée sur simulateur. Pont prouvé E2E sur sim
pair iPhone+Watch : HR identiques watch->iPhone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sendMessage si iPhone joignable, sinon buffer + transferUserInfo (livraison
différée). Délégué WCSession hors-main, hop vers main pour @Published isReachable,
buffer sérialisé sur file dédiée. Build watchsimulator OK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>