# Handoff — Backend `/live` (Mac → session Linux `coach_sportif`) **De** : Claude Code Mac mini, 2026-05-25 (chantier watchOS Live Workout). **Pour** : session Claude Code sur le repo `coach_sportif` (backend FastAPI + templates). **Objet** : finir le **point 1** — la page `/live` doit s'afficher dans l'app et recevoir les samples temps réel poussés par la Watch. ## Contexte : le natif est TERMINÉ et PROUVÉ Phases 1-3 (target watchOS `CoachWatch` + 4 fichiers Swift + plugin Capacitor `CoachLiveBridge`) livrées sur `coach-ios` (commits `5d60a40` → `678b5a4`). **Pont watch → iPhone validé end-to-end sur simulateur** (paire iPhone+Watch) : les logs montrent chaque sample envoyé par la Watch reçu à l'identique par le plugin iPhone (HR 130.69 → 148.32, latence ~300 ms). Donc dès que `/live` s'affiche dans l'app et écoute l'event, **les chiffres défileront**. Un **mode Test (FC simulée)** existe dans l'app watch (bouton sous les activités) : il envoie des samples synthétiques **sans HealthKit**. → tu peux valider `/live` **sans** dépendre de la vraie FC (la Santé de la Watch physique de Sylvain a un bug système séparé, hors scope ici). ## Le problème observé En ouvrant `/live` dans l'app, la page renvoie **« token invalide »**. → La route `/live` ne reconnaît pas la session authentifiée de l'app. Rappel : l'app (wrapper Capacitor) s'authentifie via **injection de cookie de session dans `AppDelegate`** (coach-ios). Toutes les pages authentifiées marchent via ce cookie. La route `/live` (Phase 4) avait été écrite avec `check_auth(request, token)` où `token` venait d'un `?token=` — ce qui ne matche pas le flux cookie de l'app. ## À faire côté `coach_sportif` ### 1. Auth de `/live` = même mécanisme que les autres pages authentifiées Faire en sorte que `/live` accepte la **session/cookie** de l'app (comme les autres routes protégées), `?token=` restant un fallback optionnel. Concrètement : utiliser le même `Depends`/helper d'auth que les autres pages, au lieu d'exiger le token en query. Objectif : ouvrir `/live` dans l'app authentifiée → 200, pas « token invalide ». ### 2. Un accès à `/live` depuis l'UI web Ajouter un lien/bouton vers `/live` dans la nav de l'app (là où c'est pertinent — ex. page séance / accueil), pour que l'utilisateur y arrive sans taper d'URL. ### 3. JS du template `live_workout.html` — contrat EXACT du plugin natif Le plugin iPhone émet l'event **`liveSample`** (jsName du plugin : **`CoachLiveBridge`**). Champs d'un sample : | champ | type | sens | |---|---|---| | `heartRate` | number | FC en bpm | | `activeEnergyKcal` | number | calories actives cumulées | | `distanceMeters` | number | distance cumulée en mètres | | `elapsedSec` | number | durée écoulée en secondes | | `ts` | number | timestamp epoch (secondes, fractionnaire) | | `sim` | bool | présent (=true) uniquement en mode Test | JS à mettre dans le template (page chargée DANS l'app Capacitor) : ```html ``` > Vérifier que `/static/vendor/capacitor-core.js` (ou équivalent) expose bien > `registerPlugin`. En navigateur pur (hors app) il n'y a aucun WCSession donc 0 > sample — c'est normal, la page n'est utile que dans le wrapper iOS. ## Test d'acceptation (sans device, sur simulateur) 1. App iOS (sim ou device) sur `/live`, page au premier plan. 2. App watch → bouton **Test (FC simulée)**. 3. `/live` doit afficher FC/calories/distance/durée qui défilent (~ toutes les 2 s) et le statut « En direct ». Côté natif c'est déjà prouvé (plugin `CoachLiveBridge` logue `liveSample reçu de la Watch hr=…`). Si `/live` ne bouge pas alors que ce log apparaît, c'est le JS/registerPlugin du template à corriger. ## État du fix + déploiement (VPS) **Fix point 1 DÉPLOYÉ EN PROD** (2026-05-25) : onglet nav « Live workout » → `/live` ajouté dans `NAV_ITEMS` (la route/auth/template existaient déjà — Phase 4 — mais `/live` n'était lié nulle part → page inatteignable → « token invalide » sur le redirect /login). Commit `ecf334a` mergé sur `main` (fast-forward) + déployé via webhook Gitea (service `coach-web` actif). Validé sur le VPS : import OK, onglet présent. L'onglet apparaît dans le menu « Plus » de la tab bar (NAV_DEFAULT_TABS ne montre que home/calendar/workouts/meals par défaut). Infra (depuis `Makefile` / `deploy.sh`) : - VPS : `ubuntu@83.228.246.229` - Prod : `/home/ubuntu/coach_sportif`, service systemd `coach-web` - Staging : `/home/ubuntu/coach_sportif_staging`, service `coach-web-staging` - Auto-deploy : webhook Gitea sur push `main` → `deploy.sh` (`git reset --hard origin/main` + `pip install -r requirements.txt` + restart `coach-web` + healthcheck) - Targets : `make test`, `make deploy-staging` (sans risque), `make deploy` (prod) **Pour finir** : sur l'env Linux/VPS (Python ≥ 3.11, le venv ne se monte pas sur le Mac : `anyio==4.13.0`), lancer `make test`, valider via `make deploy-staging`, puis merger `feat/live-nav-tab` → `main` (auto-deploy prod). ## Hors scope de ce handoff - Vraie FC sur la Watch physique de Sylvain (bug système Santé de SA montre — NanoSettings crashe, à régler par désappairage/réappairage). - TestFlight (signing/App Store Connect côté Sylvain).