---
name: gametime-architecture-score-chrono
description: memory note gametime-architecture-score-chrono
metadata:
  type: project
---
# GameTime — Cadrage Architect : score chronométré (ticket #18, 2026-07-18)

## Stockage validé
- `actualScoreTimeMs` (nullable) sur `ActiveSetResult` et `WorkoutHistorySetResult`, distinct de `actualScore`.
- `targetScoreTimeMs` sur `ProgramExercise`, `targetScoreTimeMsOverride` sur `WorkoutTemplateExerciseOverride`, distincts de `targetScore`/`targetScoreOverride`.
- Invariant : `manual` utilise `actualScore/targetScore` ; `stopwatch` utilise `actualScoreTimeMs/targetScoreTimeMs` ; jamais les deux familles remplies simultanément. `skipped` implique aucune valeur `actual*` (y compris `actualScoreTimeMs`).

## État transitoire du chrono
Nouvelle table Drift `ActiveScoreStopwatchState`, sur le même principe que `ActiveRestState` (horodatages persistés, pas de compteur mémoire) :
- clé logique : `activeWorkoutSessionId + programIndex + exerciseIndex + setIndex`
- `status` : `running | stopped`
- `startedAt`, `accumulatedMs`, `stoppedAt?`
- Absence de ligne = chrono non démarré.
- À la validation de la série, copier la durée finale vers `ActiveSetResult.actualScoreTimeMs`.
- Pause de séance : si `running`, figer `accumulatedMs` ; reprise explicite après resume (pas de décompte pendant la pause).
- Terminer la série avec chrono `running` → auto-stop puis enregistrement (comportement UX demandé).

## Propagation du mode (snapshot pattern existant, réutilisé tel quel)
`ScoreInputMode` (`manual|stopwatch`) : source de vérité sur `Exercise`, copié dans `ProgramExercise`, puis dans le snapshot de séance (`WorkoutTemplateProgram`/exercice snapshotté), puis dans `ActiveSetResult` et `WorkoutHistorySetResult` pour affichage autonome sans dépendre de la source.
**Important** : `WorkoutTemplateExerciseOverride` ne porte JAMAIS le mode lui-même (cohérent avec la règle produit déjà en place : l'override en séance-modèle ne change que des valeurs numériques, jamais la structure/le mode) — seulement `targetScoreTimeMsOverride` en plus des overrides numériques existants.

## Migration Drift
Le schéma est actuellement en `schemaVersion = 2` (ticket #21). Le ticket #18 doit passer en **3** : ajout des colonnes mode/chrono sur les tables concernées + création de la table `ActiveScoreStopwatchState`, migration `from < 3`.