Files
coach-ios/COWORK.md
Sylvain Bettinelli 1a5b9cbcad COWORK : le moteur d'intervalles, et une commande qui n'existait pas
Deux corrections au fichier qui sert de canal entre le dev Linux et le Mac.

1. Le chantier IntervalEngine (f2fa3cc, ce matin) n'y figurait pas — or c'est
   par ce fichier que le poste Mac apprend ce qu'il y a à faire. Ajouté avec
   l'étape bloquante : le fichier n'est PAS membre de la cible CoachWatch
   (grep rend 0), donc Xcode l'ignore et le projet compile très bien sans lui.
   Même piège que VmaTestView.swift.

   Noté aussi que les 42 tests Swift passent sur Linux (./tests-linux/run.sh),
   dont les 17 du moteur : la logique est validée, seule l'intégration reste.

2. La procédure de build disait `open ios/App/App.xcworkspace`. Ce fichier
   n'existe pas : le dépôt ne contient que App.xcodeproj, Capacitor 8 étant
   passé à SPM. La commande échouait telle quelle.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 16:24:12 +00:00

10 KiB
Raw Blame History

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 <Nom>.swift ios/App/App.xcodeproj/project.pbxproj (doit être ≠ 0). Si Xcode ne le voit pas après ouverture → clic droit dossier AppAdd 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).

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 AppSigning & 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.

🆕 Moteur d'intervalles (2026-08-20, dev — non câblé, non compilé)

Branche feat/watch-outdoor (poussée sur Gitea le 2026-08-20), commit f2fa3cc. IntervalEngine guide les intervalles d'une séance au poignet.

Pourquoi ce code existe : WorkoutKit ne sait pas exécuter une séance structurée dans une app tierce — son seul point d'exécution public est WorkoutPlan.openInWorkoutApp(), qui ouvre l'app Exercice d'Apple. Il faut donc écrire la machine à états nous-mêmes.

Le moteur est volontairement pur : aucun HealthKit, aucun timer, aucune horloge interne. Il répond à « où en sommes-nous ? » à partir du temps et de la distance qu'on lui pousse. Le temps de référence est elapsedTime du builder, qui exclut déjà les pauses.

42 tests Swift verts sur Linux (./tests-linux/run.sh, toolchain ~/workspace/toolchains/bin), dont les 17 d'IntervalEngine. La logique est donc validée — ce qui reste est l'intégration, que Linux ne peut pas compiler.

⚠️ ÉTAPE MAC OBLIGATOIRE — le fichier n'est PAS dans le projet Xcode. Vérifié le 2026-08-20 : grep -c "IntervalEngine" ios/App/App.xcodeproj/project.pbxproj rend 0. Xcode l'ignore donc totalement, et ça compile très bien sans lui — même piège que VmaTestView.swift. À faire :

  1. cp ios/App/App.xcodeproj/project.pbxproj ios/App/App.xcodeproj/project.pbxproj.bak-interval
  2. Clic droit sur le dossier CoachWatchAdd Files to "App"…ios/App/CoachWatch/IntervalEngine.swiftdécocher « Copy items if needed », cocher la cible CoachWatch.
  3. Contrôle : grep -c "IntervalEngine.swift in Sources" ios/App/App.xcodeproj/project.pbxproj doit rendre 1.
  4. Commit + push du pbxproj immédiatement (règle 3 de ce fichier).

👉 À 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.xcodeproj → Clean Build Folder → Archive → Upload.
    • ⚠️ Corrigé le 2026-08-20 : cette ligne disait open ios/App/App.xcworkspace. Ce fichier n'existe pas (vérifié : le dépôt ne contient que App.xcodeproj). Depuis Capacitor 8, les dépendances passent par SPM et il n'y a plus de workspace CocoaPods. La commande échouait donc telle quelle.
    • ⚠️ 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.