104 lines
5.5 KiB
Markdown
104 lines
5.5 KiB
Markdown
---
|
|
issueRef: "#172"
|
|
version: 2
|
|
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
|
updatedAt: 1785272880017
|
|
---
|
|
|
|
## #172 — Cadrage Main (2026-07-28)
|
|
|
|
### Décision produit
|
|
Direction validée :
|
|
- la montre reste un **capteur + télécommande** ;
|
|
- le téléphone reste **source de vérité métier** ;
|
|
- on optimise le trafic et la chauffe par **batching côté montre**, pas par déplacement de logique métier.
|
|
|
|
### 1. Télémétrie stats montre
|
|
Deux modes à cadrer :
|
|
- **écran montre allumé** : cadence plus fréquente pour un live crédible ;
|
|
- **écran montre éteint / ambient** : flush agrégé toutes les ~30s, inspiré du comportement observé sur Heavy.
|
|
|
|
La montre ne calcule pas les agrégats métier finaux. Elle bufferise seulement des échantillons ou micro-agrégats transport :
|
|
- FC : min/max/moy locale de fenêtre + latest utile à l'affichage ;
|
|
- distance/calories : latest cumulée de fenêtre, jamais recalcul métier final ;
|
|
- timestamps de capture et contexte d'exécution.
|
|
|
|
Le téléphone :
|
|
- persiste ;
|
|
- fusionne ;
|
|
- construit les agrégats de séance / étape / série / exercice / historique ;
|
|
- résout les conflits et les trous.
|
|
|
|
### 2. Score manuel montre
|
|
Décision : **oui au debounce 500ms, non au "score final sans contexte"**.
|
|
|
|
Le flux cible minimal :
|
|
- la montre met à jour le score **optimistement** à chaque tap ;
|
|
- chaque tap reset un timer de 500ms ;
|
|
- au silence de 500ms, la montre envoie au téléphone une intention de type `setManualScore` contextualisée ;
|
|
- le téléphone valide, persiste, reprojette ;
|
|
- la montre se recale silencieusement sur la projection confirmée.
|
|
|
|
### 3. Garde-fous architecture
|
|
- Pas de logique métier durable sur la montre.
|
|
- Pas de calcul d'historique ni de règle d'invariants sur la montre.
|
|
- Chaque message montre -> téléphone doit embarquer le contexte minimal : `sessionId`, indices d'exécution, `baseRevision`, horodatage.
|
|
- Rejet/conflit côté téléphone : la projection confirmée gagne toujours.
|
|
- Crash montre avant flush : perte limitée à la fenêtre non flushée, acceptable pour le score seulement si la montre ne prétend jamais avoir persisté côté téléphone.
|
|
|
|
### 4. Contrat à cadrer ensuite
|
|
Ticket attendu après celui-ci :
|
|
- extension ou adaptation du contrat watch bridge pour batches télémétrie ;
|
|
- ajout d'une commande contextualisée `setManualScore` ou équivalent ;
|
|
- stratégie de flush explicite selon état écran / foreground / ambient ;
|
|
- QA device sur chauffe, batterie, cohérence de resync.
|
|
|
|
## Cadrage Architect (2026-07-28)
|
|
|
|
Statut : **cadrage exploitable, prêt pour découpage dev**.
|
|
|
|
### Architecture cible minimale
|
|
- Garder `phone = source de vérité`, `watch = capteur + télécommande`.
|
|
- Conserver `WatchCompanionCommandHandler` comme point d'entrée unique des intentions montre.
|
|
- Ajouter un flux applicatif téléphone dédié pour la télémétrie batchée : `WatchTelemetryBatchIngress` -> validation -> appel de `WorkoutTelemetryUseCases.recordTelemetrySample(...)` pour chaque sample -> `WorkoutHistoryUseCases.updateHeartRateSummary(...)` au flush final.
|
|
- La montre ne calcule ni score final ni agrégats métier ; elle ne bufferise que du transport.
|
|
|
|
### Télémétrie batchée watch -> phone
|
|
- Recommandation : nouveau DTO `WatchTelemetryBatch`, `schemaVersion = 5`, nouveau path message `/gametime/watch/telemetry-batch`.
|
|
- Le téléphone déduplique au niveau **sample** (`sampleId` stable), pas au niveau batch.
|
|
- `WatchSensorSummary` est conservé pour le résumé final de séance, pas remplacé.
|
|
|
|
### Stratégie interactive vs ambient/screenOff
|
|
- `interactive` : flush toutes les `5s` max.
|
|
- `ambient/screenOff` : flush toutes les `30s` max.
|
|
- Flush immédiat aussi sur : fin de séance, pause explicite si la collecte s'arrête, reconnexion téléphone, changement d'état écran, buffer plein.
|
|
- Cap de sécurité : couper les batches trop gros (~60 samples).
|
|
- Une petite outbox locale de télémétrie côté montre est acceptable pour survivre à un crash : c'est du transport, pas du métier.
|
|
|
|
### Score manuel debounce
|
|
- Décision Architect : **ne pas faire de `setManualScore(value)` comme contrat principal**.
|
|
- Recommandation : un batch d'intentions delta, type `applyManualScoreDeltaBatch`.
|
|
- La montre accumule les taps `+/-` pendant `500 ms`, puis envoie un seul delta net avec `sessionId`, position courante, `baseRevision`, horodatage.
|
|
- Le téléphone applique le delta seulement si la cible métier est toujours la même ; sinon rejet et resync.
|
|
|
|
### Invariants et risques
|
|
- La montre ne calcule jamais la valeur finale du score.
|
|
- Les écritures d'historique et agrégats durables restent côté téléphone.
|
|
- Une intention score batchée n'est applicable que si `sessionId + scope + position courante` correspondent encore.
|
|
- Les retries de télémétrie ne doivent jamais dupliquer les samples : `sampleId` stable obligatoire.
|
|
- En cas de crash montre :
|
|
- score pending perdu puis recalage sur projection téléphone ;
|
|
- télémétrie non flushée reprise depuis l'outbox local.
|
|
- En reconnexion :
|
|
- resync projection immédiat ;
|
|
- flush immédiat de l'outbox télémétrie ;
|
|
- aucun replay d'anciennes intentions score.
|
|
|
|
### Lots recommandés
|
|
1. `B1 [DevBackend] Contrat bridge v5 et ingress télémétrie batchée`
|
|
2. `B2 [DevBackend] Application téléphone score debounce + validation de cible`
|
|
3. `B3 [DevBackend] Déduplication, retries, flush final`
|
|
4. `F1 [DevFrontend] Buffer montre télémétrie et stratégie interactive vs ambient`
|
|
5. `F2 [DevFrontend] Score optimiste debounce 500 ms`
|
|
6. `QA1 [QA] Matrice de cohérence et robustesse`
|