chore(ideai): synchronise etat de suivi (tickets, agents)

This commit is contained in:
2026-07-29 11:22:17 +02:00
parent 30c6259748
commit dd0c124818
32 changed files with 1415 additions and 75 deletions

View File

@ -1,6 +1,63 @@
---
issueRef: "#157"
version: 5
updatedBy: {"kind":"user"}
updatedAt: 1785252668623
version: 7
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
updatedAt: 1785256180427
---
## Cadrage UX — agent UX (2026-07-28)
Statut : **cadrage exploitable**, aucune nouvelle surface à créer. Ce ticket branche une donnée (distance live) sur des emplacements déjà spécifiés et déjà construits par [[gametime-ux-online-client]]-adjacent (#155 cadrage transverse) et #159 (surface montre, fermé). Ne pas concevoir de nouvel écran ni de nouveau composant : la distance vient simplement remplir un slot déjà réservé, aujourd'hui silencieusement absent.
### Rappel des deux emplacements concernés (déjà cadrés par #155, à ne pas rouvrir)
**Téléphone, barre stats live compacte sous le bloc d'exécution :**
```
FC 142 bpm · 0,84 km · 186 kcal
```
- ordre fixe **FC → Distance → Calories**, la distance est toujours au milieu ;
- aujourd'hui la barre affiche seulement `FC 142 bpm` (distance et calories absentes) — c'est le comportement silencieux attendu, pas un bug ;
- dès que la distance devient disponible, elle s'insère à sa place dans l'ordre, sans changement de layout, sans réanimation d'apparition particulière au-delà du refresh discret déjà en place pour la FC.
**Montre, écran secondaire `Stats` (glissement inverse d'`Actions`, cf. #159) :**
- une ligne `Distance` vient s'ajouter aux côtés de `FC` (icône cœur, cf. #161) et `Calories`, toujours dans l'ordre FC / Distance / Calories ;
- pas d'icône dédiée à inventer pour la distance : libellé texte `Distance` en Archivo au-dessus de la valeur, même traitement typographique que `Calories` (aucune des deux n'est la donnée dominante de l'écran montre, contrairement à la FC sur l'écran principal) ;
- écran principal montre : **la distance n'y apparaît jamais**, seule la FC y a droit (règle #155/#161 déjà actée, ce ticket ne la touche pas).
### Format d'affichage
- Unité `km`, séparateur décimal virgule (locale FR), deux décimales : `0,84 km` (cohérent avec l'exemple historique `2,84 km` déjà utilisé dans le cadrage #155/carte historique).
- Pas de conversion m/km dynamique à ce stade (pas de "840 m" sous 1km) — rester simple, un seul format, cohérent partout où la distance est affichée (téléphone, montre, futur après-séance/historique #158/#160).
### Garde-fous
- **Silence total si la donnée est absente** (pas de capteur GPS/distance sur la montre, pas de fix GPS encore acquis, montre non compatible) : la ligne/le segment disparaît, jamais de `--` ni de placeholder. Le comportement actuel (barre réduite à `FC` seule) est déjà la bonne référence.
- **Perte temporaire de mesure live** : garder la dernière valeur connue plutôt que de faire disparaître puis réapparaître le segment en boucle (cf. règle #155 "perte temporaire → dernière valeur connue si la session l'utilise déjà").
- Ne pas transformer la distance en donnée dominante nulle part sur ce lot : elle reste secondaire au téléphone (barre compacte) et secondaire à la montre (écran Stats uniquement, jamais écran principal).
- Aucun message bloquant, aucune demande de permission spécifique visible à l'utilisateur au-delà de ce qui est déjà géré par les tickets permissions capteurs montre déjà livrés (cf. commits f71a1551/58272e35).
### Compatibilité avec #155/#159/#161 déjà traités
Ce ticket ne modifie aucune décision de #155 (surfaces), #159 (navigation montre par glissement inverse) ni #161 (icône cœur FC, dédoublonnage écran principal). Il alimente uniquement les emplacements distance déjà prévus et actuellement vides faute de donnée.
---
## Cadrage Architect (2026-07-28)
Statut : **surprise majeure — ce ticket n'a rien de "Frontend" restant à faire**. Vérifié en lecture directe :
- `lib/presentation/workout_execution_screen.dart` (`_LiveSensorBar`, ~L2221) affiche déjà FC + Distance + Calories en pills conditionnelles (`if (sensorState.latestHeartRateBpm case final heartRate?)`, `if (_distanceLabel(sensorState) case final distanceLabel?)`, `if (_caloriesLabel(sensorState) case final caloriesLabel?)`) — silence total déjà implémenté exactement comme cadré par UX.
- `watch_app/lib/presentation/watch_session_screen.dart` (~L1187-1196) affiche déjà `Distance`/`Calories` dans l'écran secondaire `Stats`, via `_distanceLabel`/`_caloriesLabel` sur `WatchSensorSample`.
- Le contrat `packages/watch_bridge_contract/.../watch_bridge_contract.dart` porte déjà `distanceMeters` et `caloriesKcal` sur `WatchSensorSample`/`WatchSensorSummary` (schemaVersion 4), sérialisation JSON incluse.
- Donc l'intégralité de la chaîne UI téléphone + UI montre + contrat de transport est **déjà livrée**. Le titre "[DevFrontend] Distance live montre" ne correspond plus à l'état réel du code.
### Root cause probable du "distance toujours vide" — risque architecture réel côté watch natif
`WatchHeartRateCollector.kt:106-107` enregistre `DataType.DISTANCE` et `DataType.CALORIES` via **`MeasureClient.registerMeasureCallback`**, le même client que pour la FC. Or Health Services `MeasureClient` est documenté par Google comme ne supportant en pratique de façon fiable que quelques types passifs (FC, et sur certains devices SpO2) : **la distance et les calories nécessitent normalement un `ExerciseClient`** (session d'exercice Health Services explicite, avec `ExerciseType`, capacités GPS le cas échéant), pas le mode passif "measure" utilisé ici.
Conséquence probable : sur device réel, `registerMeasureCallback(DataType.DISTANCE, ...)` échoue ou ne remonte simplement jamais de données — et comme `onRegistrationFailed` n'est que loggé (jamais remonté), ce échec est **indiscernable du fallback silencieux voulu par l'UX** ("pas de donnée = rien ne s'affiche"). C'est très probablement pour cela que le ticket reste ouvert malgré une UI déjà terminée : la donnée source ne remonte jamais côté device réel, mais aucun signal ne le révèle sans instrumentation ciblée.
### Plan exécutable
1. **Vérification (watch bridge natif, DevBackend/watch)** : appeler `measureClient.getCapabilities()` sur un device Wear OS réel compatible et confirmer si `DataType.DISTANCE`/`DataType.CALORIES` sont listés comme supportés en mode `MeasureClient`. C'est un test de quelques lignes, pas un chantier.
2. **Si non supporté (cas attendu)** : cadrer un sous-lot dédié `ExerciseClient` pour distance/calories, distinct du pipeline FC actuel — cycle de vie propre (start/pause/stop de session d'exercice aligné sur la séance GameTime), permissions supplémentaires possibles (`ACCESS_FINE_LOCATION` selon device pour la distance GPS-based). Ne pas le faire rentrer de force dans `WatchHeartRateCollector` : il mérite son propre collecteur (`WatchExerciseMetricsCollector` ou équivalent) branché sur le même pipeline de sample/summary existant (mêmes champs `WatchSensorSample.distanceMeters/caloriesKcal`, pas de nouveau contrat nécessaire).
3. **Si supporté mais silencieux pour une autre raison** (permission refusée, capability disponible mais jamais déclenchée) : corriger l'enregistrement/l'octroi de permission ciblé, toujours sans toucher au contrat ni à l'UI.
4. **Aucun travail Frontend Dart/Flutter requis** dans les deux cas — l'UI est prête et attend juste que le sample porte une valeur non nulle.
5. Reclassement recommandé : ce ticket devrait passer de `[DevFrontend]` à `[DevBackend]`/watch bridge natif dans son titre, pour éviter qu'il soit repris à tort comme un chantier Flutter.
### Risque transverse
Si le sous-lot `ExerciseClient` s'avère nécessaire, il ajoute une complexité (permission localisation, gestion double session Health Services FC+Exercise en parallèle) non anticipée par le cadrage initial #156, qui supposait implicitement une seule source de collecte. À valider avec Main si le produit veut assumer cette complexité maintenant ou accepter un dégradé silencieux permanent (distance/calories jamais peuplées) sur les devices où seul `MeasureClient` HR fonctionne.

View File

@ -8,9 +8,9 @@ sprint: "d5c18b44-0eec-46db-b8ab-506cfee0bfea"
links: [{"target":"#142","kind":"blocks"},{"target":"#106","kind":"relatesTo"},{"target":"#145","kind":"dependsOn"}]
agentRefs: []
createdBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
updatedBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
createdAt: 1785180656723
updatedAt: 1785252668623
version: 5
updatedAt: 1785256180427
version: 7
---
Ajouter la distance parcourue live remontée par la montre compatible, avec affichage léger côté téléphone pendant la séance et réutilisation côté montre dans le panneau secondaire. Dégradation silencieuse si donnée indisponible.