# COWORK — coordination multi-Claude (coach-ios) Ce repo est travaillé **en parallèle par 2 instances de Claude** : - **Claude « dev » (serveur Linux, sans Xcode)** : écrit le **code** (Swift des plugins, config projet) + le backend/web (repo `coach_sportif`). **Ne peut PAS builder iOS** (pas de Xcode). - **Claude « Cowork » (app Claude sur le Mac mini de Sylvain)** : fait les **builds Xcode, la signature, les tests sur device**. → C'est toi si tu lis ça depuis le Mac. Les deux ne communiquent **que via git + ce fichier**. ⚠️ La mémoire de chaque Claude est **locale à sa machine** (non partagée) → **l'état partagé, c'est CE fichier** : tiens-le à jour. ## 🔑 Règles d'or (pour ne pas se bloquer) 1. **`git pull` AVANT de toucher au projet** ; **`git push` après chaque changement.** 2. ⚠️ **Ne jamais laisser Xcode ouvert pendant un `git pull`** — Xcode garde/réécrit le `pbxproj` en mémoire → fichiers Swift non compilés + conflits (ça nous a bloqués 2× le 2026-06-05). Quitter Xcode (`Cmd+Q`), pull, rouvrir. 3. Après toute modif faite **dans Xcode** (package SPM, capability, signing) → **commit + push `project.pbxproj` (+ `Package.resolved`) immédiatement**, sinon le prochain `git pull` est bloqué par les modifs locales. 4. Zone sensible = `ios/App/App.xcodeproj/project.pbxproj` : le Claude dev y ajoute les fichiers Swift (entrées manuelles), le Claude Mac y ajoute les packages SPM. → committer de suite limite les collisions. ## 🛠️ Workflow type « le dev a poussé du code natif, build-le » 1. **Quitter Xcode.** 2. `git pull` 3. Nouveau `*.swift` ? vérifier qu'il est dans le projet : `grep -c .swift ios/App/App.xcodeproj/project.pbxproj` (doit être ≠ 0). Si Xcode ne le voit pas après ouverture → clic droit dossier **App** → *Add Files to "App"…* (décocher *Copy items*, cocher target **App**). 4. Ouvrir Xcode → **Clean Build Folder** (`Cmd+Shift+K`) → **Run** (`Cmd+R`) sur l'iPhone. 5. Erreurs → corriger, committer le fix, push. 6. Diagnostic plugins : Safari → Développement → iPhone → coach.hypnotruck.ch → console → `Object.keys(Capacitor.Plugins).filter(k=>k.indexOf('Coach')===0)`. ## 📍 État natif actuel (2026-06-05) - **Capacitor 8 n'auto-découvre PAS les plugins in-app** → tous enregistrés dans `MainViewController.capacitorDidLoad()` : CoachWorkoutKit, CoachHealthRoute, CoachWorkoutObserver, CoachLiveBridge, **CoachAppleAuth**, **CoachGoogleAuth**. - **Login natif Apple + Google = OK, validé device.** Apple via `AuthenticationServices` (intégré). Google via **SDK GoogleSignIn** (SPM, lié à la target App) + `GIDClientID`/URL scheme dans Info.plist. Les deux POSTent l'id_token sur `/auth/apple|google/web` (backend) → cookie de session. - watchOS `CoachWatch` + Live Activity en place (chantiers antérieurs, cf. `HANDOFF-WATCHOS.md`). - **Widgets natifs iPhone + complication Watch (2026-06-29)** : code écrit côté dev, **reste le câblage Mac**. - iPhone : plugin `CoachWidgetBridge` (enregistré dans `capacitorDidLoad()`), modèle partagé `CoachWidgetSnapshot.swift`, widgets `CoachWidgets.swift` (Séance du jour + Score de forme) dans l'extension `CoachLiveActivity`. App Group `group.ch.hypnotruck.coach`. **Hook web déjà en prod** (`widget-bridge.js`). - Watch : `CoachWidgetBridge.pushToWatch` (WCSession) → `CoachWatch/ConnectivityManager` reçoit → App Group montre + reload. Complications `CoachWatchWidgets/CoachWatchComplications.swift` (Forme circular/corner, Séance rectangular/inline). **Nouvelle target widget watchOS à créer dans Xcode.** - ⚠️ Étapes Mac obligatoires (App Group portail + capabilities + Target Membership + target Watch + build) : **TOUT est détaillé dans `docs/widgets-runbook-mac.md`.** ## 🆕 Routine quotidienne sur la Watch (2026-08-03, dev — non compilé) Nouveau chantier, code complet côté dev, **build Mac requis**. Tout est détaillé dans **`docs/routine-watch-runbook-mac.md`** (archi, étapes, débogage). - **Nouveaux fichiers, DÉJÀ référencés dans `project.pbxproj`** (aucun « Add Files » à faire) : `App/CoachRoutineBridge.swift` (target App), `CoachWatch/RoutineStore.swift` + `CoachWatch/RoutineView.swift` (target CoachWatch). Backup : `project.pbxproj.bak-routine`. - **Modifs** : `MainViewController.capacitorDidLoad()` enregistre `CoachRoutineBridgePlugin` ; `CoachWatch/ContentView` ouvre `RoutineView` en sheet ; `ConnectivityManager` (watch) gagne `sendRoutine()` + réception du `routineSnapshot`. - ⚠️ **`CoachLiveBridge` touché** — un seul ajout : `routeIfNotLiveSample()` aiguille les messages `routineDone` vers `NotificationCenter` au lieu de les émettre comme samples. Le délégué `WCSession` est unique côté iPhone, on ne pouvait pas en ajouter un second. **À re-vérifier au build** que le live workout (validé E2E le 31/05) fonctionne toujours. - Côté backend `coach_sportif` : **déjà en prod** (`/api/routine`, `/api/routine/check`, `/api/routine/day`, `data/routine_log.json`). ## 🔗 Universal Links (2026-08-06, dev — non compilé) Constat : **aucun** `.widgetURL` ne ramenait dans l'app. La Live Activity (`CoachLiveActivityWidget`), les deux widgets iPhone (`CoachWidgets` → `/calendar` et `/forme`) et les deux complications Watch pointent tous sur des URL `https://coach.hypnotruck.ch/…`, mais l'entitlement `associated-domains` était absent et `/.well-known/apple-app-site-association` renvoyait 404 → **un tap ouvrait Safari**, où le cookie d'auth injecté dans la WKWebView n'existe pas : écran de login. - **Backend — déjà en prod** (commit `8ec0047`) : route AASA publique déclarant `TZ2PVTRKNY.ch.hypnotruck.coach` sur `/live`, `/routine`, `/forme`, `/calendar` (chemins énumérés un par un pour ne pas détourner `/calendar.ics`), et `static/deeplink.js` qui écoute `appUrlOpen` et amène la WebView sur le bon chemin — Capacitor ne le fait pas tout seul. - **Côté ce repo** : `App.entitlements` gagne `com.apple.developer.associated-domains = applinks:coach.hypnotruck.ch`. - ⚠️ **Étape Mac** : ouvrir la cible **App** → *Signing & Capabilities* → vérifier qu'**Associated Domains** apparaît avec `applinks:coach.hypnotruck.ch` (l'entitlement est là, Xcode doit régénérer le profil ; si erreur de signing, décocher/recocher *Automatically manage signing*). Puis rebuild. - Effet attendu : tap sur la Live Activity → app → `/live` → la vue Liquid Glass `CoachLiveView` s'ouvre d'elle-même (le JS de `/live` appelle `openNativeLive`). C'est le point d'entrée qui manquait. ## 👉 À reprendre - ✅ **Phase 4 login — FAIT & DÉPLOYÉ** (révocation Apple à la suppression de compte) : code complet côté backend `coach_sportif` (commit `5ae6e2d`) + clé `.p8` déployée sur le VPS prod (vérifié 2026-06-26). Rien à coder. Détails : `coach_sportif/COWORK.md`. - **iOS — bloqué Mac** : builder + uploader TestFlight le natif accumulé (login Apple+Google natif, watchOS live, HeartRateRangeAlert). Sur le Mac : `cd ~/coach-ios && git pull && npm install && npx cap sync ios && open ios/App/App.xcworkspace` → Clean Build Folder → Archive → Upload. - ⚠️ **Fixes robustesse 2026-07-01 (dev, non compilés)** à valider au prochain build : `CoachWidgetBridge` (score lu en `NSNumber.intValue` → corrige un score float perdu ; `save` renvoie Bool → `call.reject` si App Group KO), `CoachAppleAuth` (double-tap : rejette l'ancien `pendingCall` ; erreur testée par domaine `ASAuthorizationError`). Swift pur, aucun nouveau fichier ni capability. - ⚠️ **Checklist pré-App-Store (audit sécu 2026-07-01)** : `ios/App/App/App.entitlements` a `aps-environment = development`. Pour la soumission **App Store/TestFlight**, l'APNs prod exige `production`. NON changé côté dev (casserait le push en dev device — arbitrage de signing à faire sur Mac : soit basculer `production` avant l'archive de distribution, soit laisser Xcode le gérer via le profil de distribution auto). À trancher/tester au moment du build release. - **watchOS — bloqué Mac** : test device + TestFlight de la cible `CoachWatch` (Phases 1-3 réalisées, build vert sim). Cf. `HANDOFF-WATCHOS.md`. - **Android — bloqué externe** : Play Console ($25 validé), upload AAB + Internal Testing depuis machine avec Android SDK. - Backend/web : voir `coach_sportif/COWORK.md`.