diff --git a/.ideai/memory/MEMORY.md b/.ideai/memory/MEMORY.md index 43334f1..214c6ec 100644 --- a/.ideai/memory/MEMORY.md +++ b/.ideai/memory/MEMORY.md @@ -14,3 +14,5 @@ - [gametime-ux-series-counter](gametime-ux-series-counter.md) — memory note gametime-ux-series-counter - [gametime-ux-exercise-media-viewer](gametime-ux-exercise-media-viewer.md) — memory note gametime-ux-exercise-media-viewer - [gametime-server-architecture-sync-sharing](gametime-server-architecture-sync-sharing.md) — memory note gametime-server-architecture-sync-sharing +- [gametime-ux-exercise-steps](gametime-ux-exercise-steps.md) — memory note gametime-ux-exercise-steps +- [gametime-architecture-exercise-steps](gametime-architecture-exercise-steps.md) — memory note gametime-architecture-exercise-steps diff --git a/.ideai/memory/gametime-architecture-exercise-steps.md b/.ideai/memory/gametime-architecture-exercise-steps.md new file mode 100644 index 0000000..82f91b8 --- /dev/null +++ b/.ideai/memory/gametime-architecture-exercise-steps.md @@ -0,0 +1,167 @@ +--- +name: gametime-architecture-exercise-steps +description: memory note gametime-architecture-exercise-steps +metadata: + type: project +--- +# GameTime — Architecture exercices à plusieurs étapes + +Décision d'architecture pour le ticket #54, basée sur la mémoire UX `gametime-ux-exercise-steps` et les patterns existants : snapshots, `ActiveSetResult` distinct des résultats détaillés, timers persistés via tables dédiées (`ActiveRestState`, `ActiveScoreStopwatchState`). + +## Principe métier + +Les étapes sont un rythme interne d'un exercice, pas une nouvelle mesure de série. Elles coexistent avec les mesures existantes `Temps`, `Répétitions`, `Score`. + +- Si `Répétitions` est active sur la série, un passage complet de la séquence d'étapes = une répétition/passsage réalisé. +- Si `Temps` est actif sur la série, il reste une durée/fenêtre globale de série, indépendante des timers d'étapes. +- Le score de série reste porté par `ActiveSetResult` / `WorkoutHistorySetResult`. +- Les résultats d'étapes restent dans des entités dédiées et ne se mélangent jamais avec les résultats de série. + +## Domaine exercice + +Ajouter : + +```dart +enum ExerciseStepType { time, reps } + +final class ExerciseStep { + final String id; + final int position; + final String name; + final ExerciseStepType type; + final int defaultTargetValue; + final bool hasScore; + final ScoreInputMode scoreInputMode; + final String? scoreLabel; + final String? scoreUnit; + final double? defaultTargetScore; + final int? defaultTargetScoreTimeMs; +} +``` + +Ajouter `List steps` sur `Exercise`. + +Invariants : +- `steps.length <= 8`. +- positions uniques, contiguës et `>= 0` dans l'ordre affiché. +- `name` non vide. +- `defaultTargetValue > 0` ; unité métier : secondes si `type=time`, répétitions si `type=reps`. +- si `hasScore=false`, aucune valeur/label de score d'étape ne doit être significative. +- si `hasScore=true` et `scoreInputMode=manual`, `scoreLabel` et `scoreUnit` obligatoires ; `defaultTargetScore` nullable mais si renseigné `>= 0`. +- si `hasScore=true` et `scoreInputMode=stopwatch`, `defaultTargetScoreTimeMs` nullable mais si renseigné `> 0`; pas d'unité libre. +- score manuel et score chrono d'étape sont exclusifs. +- `type=time` + score chrono d'étape est autorisé mais doit rester un avertissement UX non bloquant. + +## Snapshot pattern + +Les étapes doivent suivre le même pattern que les autres propriétés d'exercice : + +`Exercise.steps` -> snapshot dans `ProgramExercise` -> inclus dans `ProgramExercise.toSnapshotJson()` -> inclus dans `WorkoutTemplateProgram.programSnapshotJson` -> résolu dans `ActiveWorkoutSession.resolvedTemplateSnapshotJson` -> copié dans l'historique. + +Recommandation Drift : +- source normalisée : table `exercise_steps` liée à `exercises`. +- snapshot programme : colonne `exercise_steps_snapshot_json` sur `program_exercises` plutôt qu'une table normalisée de snapshots pour le MVP. +- historique : les snapshots utiles sont copiés dans `WorkoutHistoryStepResult`, et le snapshot global reste dans `historySnapshotJson`. + +Les modifications ultérieures d'un Exercise ne modifient pas les ProgramExercise existants. + +## Modèle d'exécution + +Ne pas étendre `ActiveSetResult` pour porter les détails d'étapes. `ActiveSetResult` reste le résultat global de série. + +Ajouter une table/entité dédiée pour la progression courante : `ActiveExerciseStepProgressState`. + +Champs recommandés : +- champs sync communs. +- `activeWorkoutSessionId`. +- `programIndex`, `exerciseIndex`, `setIndex`. +- `currentPassageIndex` : `>= 0`. +- `currentStepIndex` : `>= 0`. +- `currentStepSnapshotId`. +- `status` : `notStarted | waitingManual | runningTimer | pausedTimer | stoppedTimer | sequenceComplete`. +- `startedAt?` : horodatage du run timer courant. +- `accumulatedMs` : `>= 0`, temps déjà accumulé pour l'étape timer courante. +- `lastTransitionAt`. + +Contraintes : +- unique `(active_workout_session_id, program_index, exercise_index, set_index)`. +- pas de compteur uniquement mémoire ; tout timer d'étape actif est reconstituable depuis `startedAt + accumulatedMs`. +- pause séance : un `runningTimer` devient `pausedTimer` en figeant `accumulatedMs`. +- kill app : à la reprise, recalculer depuis les horodatages et avancer automatiquement les étapes chronométrées écoulées jusqu'à la première étape manuelle ou fin de séquence. + +Ajouter une table/entité de résultats actifs : `ActiveExerciseStepResult`. + +Champs recommandés : +- champs sync communs. +- `activeWorkoutSessionId`. +- `programSnapshotId`, `exerciseSnapshotId`. +- `programIndex`, `exerciseIndex`, `setIndex`. +- `passageIndex`, `stepIndex`, `stepSnapshotId`. +- snapshots : `stepNameSnapshot`, `stepTypeSnapshot`, `targetValueSnapshot`, `hasScoreSnapshot`, `scoreInputModeSnapshot`, `scoreLabelSnapshot?`, `scoreUnitSnapshot?`, `targetScoreSnapshot?`, `targetScoreTimeMsSnapshot?`. +- `status` : `completed | skipped`. +- `startedAt?`, `completedAt?`. +- `actualTimeMs?` pour étape `time`. +- `actualReps?` pour étape `reps` si correction future ; MVP peut enregistrer la cible quand validée ou laisser null avec statut completed selon choix UI, mais l'historique doit rester lisible. +- `actualScore?` pour score manuel d'étape. +- `actualScoreTimeMs?` pour score chrono d'étape. +- `note?` optionnel. + +Contraintes : +- unique `(active_workout_session_id, program_index, exercise_index, set_index, passage_index, step_index)`. +- `skipped` implique toutes les valeurs `actual*` nulles. +- `actualTimeMs` seulement pour `stepType=time`. +- `actualReps` seulement pour `stepType=reps`. +- `actualScore` seulement si `hasScoreSnapshot=true` et `scoreInputModeSnapshot=manual`. +- `actualScoreTimeMs` seulement si `hasScoreSnapshot=true` et `scoreInputModeSnapshot=stopwatch`. +- score manuel et score chrono jamais remplis simultanément. + +## Historique + +Ajouter `WorkoutHistoryStepResult`, distinct de `WorkoutHistorySetResult`. + +Champs analogues à `ActiveExerciseStepResult`, avec `workoutHistoryId` au lieu de `activeWorkoutSessionId` et snapshots complets pour affichage autonome. + +À la clôture d'une séance : +- créer `WorkoutHistory` et `WorkoutHistorySetResult` comme aujourd'hui pour les résultats globaux de série. +- copier tous les `ActiveExerciseStepResult` de la session vers `WorkoutHistoryStepResult`. +- ne jamais recalculer `WorkoutHistorySetResult.actualReps` depuis les step results sans décision explicite du use case ; les passages réalisés peuvent alimenter l'UI, mais le résultat de série reste sa propre source. + +## Drift / migration + +Le schéma actuel est `schemaVersion = 7`. Le ticket #54 doit passer à `schemaVersion = 8`. + +Ajouts recommandés : +- table `exercise_steps`. +- colonne `exercise_steps_snapshot_json` sur `program_exercises`. +- table `active_exercise_step_progress_states`. +- table `active_exercise_step_results`. +- table `workout_history_step_results`. +- index de session sur les tables actives. +- index history sur `workout_history_step_results(workout_history_id)`. +- contraintes CHECK pour types, statuts, valeurs positives/non négatives. + +## Audio / bips + +Choix recommandé : introduire un port applicatif/presentation `ExerciseStepAudioCuePlayer` ou équivalent, avec méthodes métier `playShortCountdownBeep()` et `playLongCompletionBeep()`. + +Adapter Flutter recommandé : `audioplayers` avec deux assets très courts bundlés (`short_beep`, `long_beep`), préchargés et joués en mode faible latence si possible. + +Raison : solution mature et multiplateforme iOS/Android, fiable pour distinguer bip court et bip long. `SystemSound` est plus simple mais ne garantit pas un bip long distinct ni un contrôle suffisant. Une génération synthétique pure éviterait les assets mais augmente la complexité native/test. + +Invariants de test : +- les widgets/use cases dépendent du port, jamais directement du lecteur audio réel. +- tests unitaires/widget avec fake player uniquement. +- l'absence/échec audio ne doit pas bloquer la progression d'étape. + +## Tickets créés + +- #56 `[DevBackend] Modèle domain + Drift pour exercices à étapes`. +- #58 `[DevBackend] Exécution persistante des étapes et résultats par passage`, dépend de #56. +- #59 `[DevFrontend] Éditeur d'exercice avec séquence d'étapes`, dépend de #56. +- #60 `[DevFrontend] Exécution de séance avec module séquence et bips`, dépend de #58 et #59. +- #61 `[DevFrontend] Plan de séance et historique avec résultats d'étapes`, dépend de #58. +- #62 `[QA] Validation exercices à étapes, persistance et historique`, dépend de #60 et #61. + +Ordre recommandé : #56 -> #58 -> #59 -> #60 et #61 -> #62. + +Note orchestration : le sprint dédié `Exercice editor enhancement` existe avec id `1bb8bdf2-9c35-4f53-9a31-1390a47bec63`, mais l'outil de création de ticket exposé à Architect ne permet pas de renseigner `sprintId`. Les tickets ont donc été créés liés à #54 et devront être rattachés au sprint par l'orchestrateur si nécessaire. \ No newline at end of file diff --git a/.ideai/memory/gametime-ux-exercise-steps.md b/.ideai/memory/gametime-ux-exercise-steps.md new file mode 100644 index 0000000..3387614 --- /dev/null +++ b/.ideai/memory/gametime-ux-exercise-steps.md @@ -0,0 +1,437 @@ +--- +name: gametime-ux-exercise-steps +description: memory note gametime-ux-exercise-steps +metadata: + type: project +--- +# GameTime — Exercice à plusieurs étapes (ticket #54, UX 2026-07-19) + +Conception UX pour ajouter des **séquences d'étapes** aux exercices, sans remplacer les mesures existantes `Temps / Répétitions / Score` au niveau de la série. + +## Décision structurante + +Les étapes sont un **rythme interne de l'exercice**, pas un nouveau mode exclusif d'exécution. + +- Un exercice peut conserver toutes ses mesures de série existantes : `Temps`, `Répétitions`, `Score`. +- La séquence d'étapes s'ajoute par-dessus ces mesures. +- Si la mesure `Répétitions` est active au niveau de la série, elle représente le nombre de **passages complets dans la séquence**. +- Si la mesure `Temps` est active au niveau de la série, elle représente une fenêtre globale ou une durée cible de série, indépendante des chronos d'étapes. +- Le score de série reste disponible tel que déjà conçu, y compris score libre ou score chrono. + +Exemple validé par le besoin utilisateur : une série peut demander de répéter 10 fois la séquence `dribble gauche -> dribble droite`, ou d'enchaîner cette séquence pendant 10 minutes, ou les deux. + +## A. Création / édition d'exercice + +Surface concernée : `ExerciseFormScreen`, section existante `Mesures disponibles`. + +Ajouter une nouvelle section après les mesures disponibles et leurs valeurs par défaut : + +```text +Séquence d'étapes +[ ] Rythmer cet exercice avec des étapes +``` + +Texte d'aide quand désactivé : + +```text +Ajoute des étapes si l'exercice doit suivre un ordre précis pendant chaque série. +``` + +Quand activé : + +```text +Séquence d'étapes +Chaque passage suit ces étapes dans l'ordre. Si la série suit des répétitions, une répétition correspond à un passage complet. + +[Ajouter une étape] +``` + +### Limite d'étapes + +Recommandation UX MVP : limite dure à **8 étapes** par exercice. + +Raison : 5-6 étapes restent lisibles pendant l'effort ; 8 couvre les exercices complexes sans rendre la progression mobile trop dense. Si l'utilisateur atteint la limite : + +```text +Limite atteinte +Un exercice peut contenir jusqu'à 8 étapes. +``` + +### Liste des étapes dans le formulaire + +Chaque étape est une carte compacte réordonnable, style Court Blazer : surface plate, rayon 6 px, bordure, liseré supérieur crimson si ouverte/active. + +Carte repliée : + +```text +[drag] 1. Dribble main droite [modifier] + Temps · 10 s [menu] +``` + +ou : + +```text +[drag] 3. Pompes [modifier] + Répétitions · 10 [menu] +``` + +Actions accessibles : + +- poignée drag-and-drop pour réordonner ; +- menu `...` avec `Monter`, `Descendre`, `Dupliquer`, `Supprimer` ; +- bouton/icône `Modifier l'étape` ouvrant le détail. + +Ne pas dépendre uniquement du drag-and-drop : `Monter` / `Descendre` doivent exister pour l'accessibilité tactile. + +### Détail d'étape + +Ouvrir un écran ou une bottom sheet haute `Modifier l'étape`. Recommandation : **écran dédié** si le formulaire principal est déjà long ; bottom sheet acceptable seulement si elle reste plein écran. + +Champs : + +```text +Nom de l'étape +[ Dribble main droite ] +``` + +Validation : + +```text +Le nom de l'étape est obligatoire. +``` + +Type d'étape, exclusif : + +```text +Type d'étape +(•) Temps +( ) Répétitions +``` + +Si `Temps` : + +```text +Durée par défaut (s) +[ 10 ] +``` + +Validation : + +```text +Saisis une durée supérieure à 0. +``` + +Si `Répétitions` : + +```text +Répétitions par défaut +[ 10 ] +``` + +Validation : + +```text +Saisis un nombre de répétitions supérieur à 0. +``` + +### Score d'étape + +Chaque étape peut avoir un score optionnel. + +```text +Score d'étape +[ ] Ajouter un score pour cette étape +``` + +Si activé : + +```text +Mode de score +(•) Saisie libre +( ) Chrono intégré +``` + +Score libre : + +```text +Score à saisir +[ Réussites ] + +Unité +[ paniers ] + +Score par défaut +[ 5 ] +``` + +Validation : + +- `Le libellé du score est obligatoire.` +- `L'unité du score est obligatoire.` +- `Saisis un score supérieur ou égal à 0.` + +Score chrono : + +```text +Objectif de chrono par défaut (optionnel) +[ 00:12 ] +``` + +Validation seulement si rempli : + +```text +Saisis un objectif supérieur à 0. +``` + +Recommandation UX : autoriser le score chrono surtout sur les étapes à répétitions. Sur une étape de type `Temps`, si l'utilisateur choisit aussi `Score chrono`, afficher un avertissement non bloquant : + +```text +Cette étape utilise déjà un compte à rebours. Le score chrono ajoute un second temps mesuré ; garde-le seulement si tu veux enregistrer une performance distincte. +``` + +### États et validations du formulaire exercice + +- Si `Rythmer cet exercice avec des étapes` est activé, il faut au moins une étape. +- Message : `Ajoute au moins une étape ou désactive la séquence.` +- Chaque étape doit avoir un nom, un type, et une cible par défaut strictement positive pour son type. +- Supprimer une étape demande confirmation seulement si elle contient déjà des champs remplis ou un score configuré. +- Pour un exercice déjà utilisé dans des programmes, conserver l'avertissement existant : les programmes existants restent inchangés. Les étapes doivent être snapshotées comme le reste de l'exercice. + +## B. Exécution pendant une séance + +Surface concernée : écran d'exécution déjà structuré avec : header discret, bloc `SÉRIE X/Y`, nom d'exercice, bouton médias, inputs de mesures, actions de série. + +### Placement du module séquence + +Si l'exercice a des étapes, ajouter un module `Séquence` entre le nom de l'exercice et les inputs de mesures de série. + +Structure générale : + +```text +00:12 +Programme 1/2 · Exercice 3/8 + +SÉRIE +2 / 4 + +Dribble combo [Voir médias] + +SÉQUENCE +Passage 1 / 10 +[1] [2] [3] [4] + +ÉTAPE 1 / 4 +Dribble main droite +10 s + +00:10 +[Démarrer la séquence] + +Résultat de la série +Passages réalisés : 0 / 10 +Score : -- + +[Terminer la série] +[Passer la série] +``` + +Si la série n'a pas la mesure `Répétitions` active : + +```text +Passage en cours +``` + +Si la série a `Répétitions` active : + +```text +Passage 1 / 10 +``` + +Le mot `Passage` est utilisé dans l'UI pour éviter de confondre les répétitions de série avec les répétitions internes d'une étape. + +### Progression des étapes + +Afficher une progression compacte compatible 5-6 étapes et jusqu'à 8 : + +- chips carrées ou petits segments `1 2 3 4` ; +- étape courante : fond primaire or, texte fond ; +- étapes terminées : succès ou contour primaire ; +- étapes passées : contour/texte secondaire ; +- étapes à venir : surface neutre. + +Pour plus de 6 étapes, la rangée peut défiler horizontalement, mais le label `ÉTAPE X / Y` reste toujours visible. + +### Étape chronométrée + +État initial si c'est la première étape active de la série : + +```text +ÉTAPE 1 / 4 +Dribble main droite +Objectif : 10 s + +00:10 +[Démarrer la séquence] +[Passer l'étape] +``` + +Après démarrage : + +```text +00:07 +[Passer l'étape] +``` + +Style : + +- compte à rebours en Anton, 56-64 px, couleur primaire or ; +- label et nom en Archivo ; +- liseré crimson 2 px sur le panneau ; +- dans les 3 dernières secondes, flash discret du liseré ou du fond du compteur en crimson, sans nuire à la lisibilité. + +Son : + +- à `3`, `2`, `1` : bip court à chaque seconde ; +- à `0` : bip long ; +- à `0`, passage automatique à l'étape suivante. + +Enchaînement : + +- Si l'étape suivante est aussi chronométrée, son compte à rebours démarre immédiatement, sans pause ni bouton intermédiaire. +- Si l'étape suivante est à répétitions, l'écran affiche l'étape suivante et attend l'action utilisateur `Étape suivante`. +- Si c'est la dernière étape et qu'un nouveau passage doit commencer, le premier chrono du passage suivant démarre immédiatement si la première étape est chronométrée. + +### Étape à répétitions + +Affichage : + +```text +ÉTAPE 3 / 4 +Pompes + +10 +RÉPÉTITIONS + +[Étape suivante] +[Passer l'étape] +``` + +- Le nombre cible utilise Anton, couleur primaire or. +- `Étape suivante` valide l'étape et avance. +- Il n'y a pas de compteur manuel des répétitions internes au MVP : l'utilisateur confirme quand l'étape est faite. + +### Score d'étape pendant l'exécution + +Si l'étape a un score libre : afficher un champ compact sous le bloc principal de l'étape : + +```text +Score de l'étape (paniers) +[ ] +``` + +Si l'étape est chronométrée, le score libre ne bloque jamais l'enchaînement automatique. Si l'utilisateur ne l'a pas renseigné, il pourra le corriger via le plan de séance / détail de série. + +Si l'étape a un score chrono et que l'étape est à répétitions : afficher un petit module `Chrono score d'étape` avec `Démarrer / Arrêter / Réinitialiser`, même logique que le score chrono de série. + +Recommandation UX pour éviter la surcharge : ne pas afficher de second gros compteur si l'étape elle-même est déjà chronométrée. Dans ce cas, si le score chrono est configuré, afficher l'avertissement en configuration et, en exécution, garder le compteur d'étape prioritaire. + +### Passage d'une répétition/passage à l'autre + +Quand la dernière étape d'un passage est validée ou terminée : + +- incrémenter `Passages réalisés` de 1 ; +- si la série a une cible de répétitions et que la cible n'est pas atteinte, commencer le passage suivant ; +- si le prochain passage commence par une étape chronométrée, démarrer immédiatement le chrono ; +- si le prochain passage commence par une étape à répétitions, afficher l'étape et attendre `Étape suivante`. + +Quand la cible de passages est atteinte : + +```text +Séquence terminée +10 / 10 passages réalisés +``` + +Le bouton principal devient ou reste : + +```text +Terminer la série +``` + +La fin de séquence ne doit pas forcément clôturer la série automatiquement, car la série peut aussi avoir un score global, un chrono score global ou une correction à faire. L'utilisateur garde le contrôle via `Terminer la série`. + +Si la série a `Temps` mais pas `Répétitions`, la séquence boucle tant que l'utilisateur ne termine pas la série. Le temps de série reste indépendant ; à expiration, afficher un feedback `Temps de série terminé`, mais ne pas interrompre brutalement une étape en cours. + +### Articulation avec les mesures de série existantes + +Quand un exercice a des étapes, renommer visuellement la mesure `Répétitions` de série en contexte : + +```text +Passages réalisés +0 / 10 +``` + +Ce compteur est alimenté automatiquement par les passages terminés, avec action secondaire `Corriger` si l'utilisateur doit ajuster. + +Les autres mesures de série restent dans un bloc `Résultat de la série`, sous le module séquence : + +- `Temps de série` si actif ; +- `Passages réalisés` si répétitions actif ; +- `Score de série` ou `Chrono score` si actif. + +Ce bloc peut être compact par défaut pour ne pas écraser la séquence, mais les champs nécessaires doivent rester accessibles sans changer d'écran. + +### Skip / passer + +Renommer le bouton de série existant en contexte : + +```text +Passer la série +``` + +Dans le module séquence : + +```text +Passer l'étape +``` + +Menu secondaire recommandé : + +```text +Passer ce passage +``` + +Comportements : + +- `Passer l'étape` marque l'étape comme passée et avance à la suivante. Si un chrono d'étape tourne, confirmation : `Le chrono de cette étape sera arrêté.` +- `Passer ce passage` marque les étapes restantes du passage comme passées, ne compte pas ce passage dans `Passages réalisés`, puis démarre le passage suivant si la série doit continuer. +- `Passer la série` conserve le comportement existant : la série est passée sans résultat de série. Si une étape est en cours, demander confirmation : `La séquence en cours sera arrêtée.` + +### Pause, reprise, fermeture d'app + +- Pause de séance met aussi en pause le chrono d'étape et les éventuels chronos score d'étape. +- À la reprise, afficher l'étape courante avec son temps restant exact. +- Si l'app est tuée pendant une étape chronométrée, la reprise doit restaurer le passage, l'étape et le temps restant. UX attend la même robustesse que `ActiveRestState`. +- Si un chrono arrive à zéro pendant que l'app est en arrière-plan, à la réouverture afficher l'état recalculé : étape suivante ou passage suivant selon le temps écoulé. Si plusieurs étapes chronométrées se sont enchaînées, l'app peut avancer jusqu'à la première étape à répétitions ou jusqu'à la fin calculée du passage. + +### Plan de séance / édition ponctuelle + +Dans le `Plan de séance`, une série avec étapes garde son état global `À faire / En cours / Terminée / Passée`, mais le détail de série doit pouvoir afficher les étapes enregistrées : + +```text +Série 2 / 4 +Passages réalisés : 7 / 10 + +Passage 1 + Étape 1 · Terminée · 10 s + Étape 2 · Terminée · 10 reps + Étape 3 · Passée +``` + +Édition ponctuelle d'une série passée/terminée : ne rejoue pas la séquence en mode interactif. Elle permet de corriger les résultats enregistrés : passages réalisés, score de série, scores d'étapes. La position courante de séance ne bouge pas. + +## Exigences remontées à Architect + +- Les étapes doivent être snapshotées avec l'exercice dans les programmes/séances/historiques. +- Chaque étape : position, nom, type `time` ou `reps`, cible par défaut > 0, score optionnel avec mode et valeurs par défaut selon les règles existantes. +- L'état d'exécution doit suivre : passage courant, étape courante, états des étapes du passage, temps restant/accumulé des chronos d'étapes, scores d'étapes, et robustesse après pause/kill app. +- Les résultats historiques doivent pouvoir stocker les résultats d'étapes par série et par passage, sans remplacer les résultats de série existants. \ No newline at end of file diff --git a/.ideai/sprints/4237adfd-91eb-45ff-9f32-6ce1daa39822/sprint.md b/.ideai/sprints/4237adfd-91eb-45ff-9f32-6ce1daa39822/sprint.md new file mode 100644 index 0000000..22b8648 --- /dev/null +++ b/.ideai/sprints/4237adfd-91eb-45ff-9f32-6ce1daa39822/sprint.md @@ -0,0 +1,11 @@ +--- +id: "4237adfd-91eb-45ff-9f32-6ce1daa39822" +order: 3 +name: "Serveur-client" +status: "planned" +createdBy: {"kind":"user"} +updatedBy: {"kind":"user"} +createdAt: 1784482336164 +updatedAt: 1784482336164 +version: 1 +--- diff --git a/.ideai/sprints/index.json b/.ideai/sprints/index.json index f6734fb..0d01b57 100644 --- a/.ideai/sprints/index.json +++ b/.ideai/sprints/index.json @@ -18,6 +18,15 @@ "status": "planned", "updatedAt": 1784449859008, "version": 1 + }, + { + "id": "4237adfd-91eb-45ff-9f32-6ce1daa39822", + "path": "4237adfd-91eb-45ff-9f32-6ce1daa39822", + "order": 3, + "name": "Serveur-client", + "status": "planned", + "updatedAt": 1784482336164, + "version": 1 } ] } \ No newline at end of file diff --git a/.ideai/tickets/46/carnet.md b/.ideai/tickets/46/carnet.md index b069455..b59cdf9 100644 --- a/.ideai/tickets/46/carnet.md +++ b/.ideai/tickets/46/carnet.md @@ -1,6 +1,6 @@ --- issueRef: "#46" -version: 4 +version: 6 updatedBy: {"kind":"user"} -updatedAt: 1784380502263 +updatedAt: 1784482343900 --- diff --git a/.ideai/tickets/46/issue.md b/.ideai/tickets/46/issue.md index a5505fd..fc297b5 100644 --- a/.ideai/tickets/46/issue.md +++ b/.ideai/tickets/46/issue.md @@ -1,16 +1,16 @@ --- id: "dda499ac-7197-4321-bd2b-a303510b1bbe" number: 46 -title: "Créer un application headless serveur pour l'application" +title: "Créer un serveur headless pour l'application" status: "open" priority: "low" -sprint: null +sprint: "4237adfd-91eb-45ff-9f32-6ce1daa39822" links: [] agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}] createdBy: {"kind":"user"} updatedBy: {"kind":"user"} createdAt: 1784379695135 -updatedAt: 1784380502263 -version: 4 +updatedAt: 1784482343900 +version: 6 --- J'aimerais que l'application puisse proposer des services online aussi. Un peu a la manière de Heavy avec la possibilité de partager ses séances etc. A la manière de Heavy, j'aimerais que l'on puisse se créer un compte et s'y connecter, synchroniser son historique de seances, ses séances, ses programmes ainsi que ses exercices. Ce ticket est la partie serveur/web API, il faudra que ça soit un serveur headless linux qui tourne dans un container docker. Il faudra que je puisse y acceder via un reverse proxy qui tourne sur une machine différente de celle du serveur headless mais dans le même réseau local. J'y accederais via une adresse https. le container se lancera ensuite à partir d'un docker-compose.yaml dans lequel tu laissera les champs libre de tout ce qui sera necessaire de configurer (adresse ip du reverse proxy, de la machine sur laquelle tourne le docker si necessaire etc.). Pour la création du serveur, créé un subdirectory spécialement pour cette partie serveur. Dans le dossier serveur à lma racine, je veux un script pour pouvoir push le container sur le gitea qui est déjà lié au projet, je veux aussi le docker-compose.yaml demandé. \ No newline at end of file diff --git a/.ideai/tickets/53/carnet.md b/.ideai/tickets/53/carnet.md index f6c492d..d41e7a2 100644 --- a/.ideai/tickets/53/carnet.md +++ b/.ideai/tickets/53/carnet.md @@ -1,6 +1,9 @@ --- issueRef: "#53" -version: 1 -updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} -updatedAt: 1784412015509 +version: 3 +updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} +updatedAt: 1784469423570 --- +Contrat `server/openapi.yaml` (OpenAPI 3.0.3) documentant les 11 endpoints réels (health, auth register/login/logout, sync push/pull/exchange, shares create/inbox/accept/decline/revoke) avec sécurité bearerAuth et schémas de requêtes/réponses fidèles au code. Trou de couverture comblé : tests API HTTP directs pour les endpoints auth (register/login/logout), qui n'étaient testés qu'au niveau use case/middleware. `server/docs/integration-checklist.md` ajouté pour lister tout ce qui reste à vérifier manuellement une fois un vrai Docker/PostgreSQL disponible. Suite complète serveur verte (29/29), `dart analyze` clean. + +Non vérifié, pour toute la suite du chantier serveur #47-#53 (limite d'environnement du sandbox Main, pas d'accès Docker daemon) : aucun `docker build`/`docker compose up` réel, aucun test contre un vrai PostgreSQL, aucun appel HTTP de bout en bout sur le serveur réellement démarré avec DB. Tout le reste (logique métier, SQL relu manuellement, syntaxe Docker Compose validée via `docker compose config`, 29 tests avec fakes en mémoire) est vérifié. Voir `server/docs/integration-checklist.md` pour la checklist complète à dérouler quand Docker sera disponible. \ No newline at end of file diff --git a/.ideai/tickets/53/issue.md b/.ideai/tickets/53/issue.md index 53bb79d..82c964e 100644 --- a/.ideai/tickets/53/issue.md +++ b/.ideai/tickets/53/issue.md @@ -2,15 +2,15 @@ id: "d5fce252-f3c4-499f-ad0a-d3c81ea13305" number: 53 title: "[Server] Tests API, contrats OpenAPI et vérification d'intégration" -status: "open" +status: "qa" priority: "low" sprint: null links: [{"target":"#46","kind":"relatesTo"},{"target":"#50","kind":"dependsOn"},{"target":"#51","kind":"dependsOn"},{"target":"#52","kind":"dependsOn"}] agentRefs: [] createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} -updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} createdAt: 1784412015509 -updatedAt: 1784412015509 -version: 1 +updatedAt: 1784469423570 +version: 3 --- Ajouter la couverture de validation serveur : tests unitaires domain/application, tests repository PostgreSQL, tests API auth/sync/share, fixtures de payload GameTime, vérification Docker Compose locale. Produire un contrat OpenAPI ou équivalent documentant auth, sync et partage pour les futurs tickets d'intégration Flutter. \ No newline at end of file diff --git a/.ideai/tickets/56/carnet.md b/.ideai/tickets/56/carnet.md new file mode 100644 index 0000000..b97fdad --- /dev/null +++ b/.ideai/tickets/56/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#56" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784481516635 +--- diff --git a/.ideai/tickets/56/issue.md b/.ideai/tickets/56/issue.md new file mode 100644 index 0000000..a043bb9 --- /dev/null +++ b/.ideai/tickets/56/issue.md @@ -0,0 +1,16 @@ +--- +id: "c902b71c-8033-42dc-a52a-eb6822a1e863" +number: 56 +title: "[DevBackend] Modèle domain + Drift pour exercices à étapes" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#54","kind":"relatesTo"}] +agentRefs: [{"agentId":"10ee045b-1c41-479e-ba03-dceed9edd495","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784481516635 +updatedAt: 1784481516635 +version: 1 +--- +Implémenter le socle domain et Drift pour les exercices à étapes. Ajouter `ExerciseStep`, enum `ExerciseStepType time|reps`, liste `steps` sur `Exercise`, validation max 8 étapes, positions uniques, cible par défaut > 0, score d'étape optionnel avec `ScoreInputMode manual|stopwatch`, label/unité/valeurs par défaut. Ajouter snapshots d'étapes dans `ProgramExercise.toSnapshotJson` et dans les snapshots WorkoutTemplate/History. Migration Drift depuis schemaVersion 7 vers 8 : tables/colonnes nécessaires, contraintes et mappers repository. \ No newline at end of file diff --git a/.ideai/tickets/57/carnet.md b/.ideai/tickets/57/carnet.md new file mode 100644 index 0000000..24b7cb1 --- /dev/null +++ b/.ideai/tickets/57/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#57" +version: 4 +updatedBy: {"kind":"user"} +updatedAt: 1784482343900 +--- diff --git a/.ideai/tickets/57/issue.md b/.ideai/tickets/57/issue.md new file mode 100644 index 0000000..ee3c413 --- /dev/null +++ b/.ideai/tickets/57/issue.md @@ -0,0 +1,16 @@ +--- +id: "d5b0bd7e-a021-4824-a278-b2e9c14a1cf6" +number: 57 +title: "Allimenter la mémoire projet pour l'UI online de l'app" +status: "open" +priority: "medium" +sprint: "4237adfd-91eb-45ff-9f32-6ce1daa39822" +links: [] +agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}] +createdBy: {"kind":"user"} +updatedBy: {"kind":"user"} +createdAt: 1784481519943 +updatedAt: 1784482343900 +version: 4 +--- +L'application aura donc une couche online. L'application se veut cependant rester un maximum offline sur les ofnctionnalités de base. Je veux que cette couche online soit le plus transparente possible pour l'utilisateur. En clair, si le serveur n'est pas disponible, je ne veux pas de popup qui lui dise qu'il n'est pas connecté. Le but, c'est que les informations soient autant que possibles stockées en local pour tout ce qui est necessaire au bon affichage de l'application sans pour autant donner de mauvaises ou de fausses informations (par exemple, garder les exercices créés, les programmes, les séances etc en local, si plus tard on calcule des statistiques pareilles, les garder aussi en local, si il y a un systeme de compte, garder la photo de profile ainsi que le pseudo etc en local. En bref la référence reste encore une fois Heavy qui fait très bien ça). Ajoute ceci à la mémoire du projet pour les futurs features qui traiteront de ça. \ No newline at end of file diff --git a/.ideai/tickets/58/carnet.md b/.ideai/tickets/58/carnet.md new file mode 100644 index 0000000..bb18644 --- /dev/null +++ b/.ideai/tickets/58/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#58" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784481524706 +--- diff --git a/.ideai/tickets/58/issue.md b/.ideai/tickets/58/issue.md new file mode 100644 index 0000000..5e93c6d --- /dev/null +++ b/.ideai/tickets/58/issue.md @@ -0,0 +1,16 @@ +--- +id: "d18752bf-3e5b-4fb9-935c-064d0d2e837a" +number: 58 +title: "[DevBackend] Exécution persistante des étapes et résultats par passage" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#54","kind":"relatesTo"},{"target":"#56","kind":"dependsOn"}] +agentRefs: [{"agentId":"10ee045b-1c41-479e-ba03-dceed9edd495","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784481524706 +updatedAt: 1784481524706 +version: 1 +--- +Ajouter le modèle d'exécution des étapes distinct de `ActiveSetResult`. Créer une table dédiée type `ActiveExerciseStepProgressState` pour session+position de série, passage courant, étape courante, statut `idle|running|stopped`, `startedAt`, `accumulatedMs`, et actions pause/reprise/skip. Créer `ActiveExerciseStepResult` pour résultats par `activeWorkoutSessionId + programIndex + exerciseIndex + setIndex + passageIndex + stepSnapshotId/stepIndex`, statut `completed|skipped`, durée/rep cible snapshot, score manuel ou score chrono d'étape. À la clôture de séance, projeter vers `WorkoutHistoryStepResult` sans mélanger avec `WorkoutHistorySetResult`. \ No newline at end of file diff --git a/.ideai/tickets/59/carnet.md b/.ideai/tickets/59/carnet.md new file mode 100644 index 0000000..fe8c16b --- /dev/null +++ b/.ideai/tickets/59/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#59" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784481530478 +--- diff --git a/.ideai/tickets/59/issue.md b/.ideai/tickets/59/issue.md new file mode 100644 index 0000000..6e1b2ff --- /dev/null +++ b/.ideai/tickets/59/issue.md @@ -0,0 +1,16 @@ +--- +id: "be486426-a17d-4521-b475-9230a2e30169" +number: 59 +title: "[DevFrontend] Éditeur d'exercice avec séquence d'étapes" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#54","kind":"relatesTo"},{"target":"#56","kind":"dependsOn"}] +agentRefs: [{"agentId":"9933c93a-b8a1-4164-a3bb-7063fdad747d","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784481530478 +updatedAt: 1784481530478 +version: 1 +--- +Implémenter la section UX `Séquence d'étapes` dans `ExerciseFormScreen` : activation optionnelle, ajout/réordonnancement max 8, écran de détail d'étape, type exclusif temps/répétitions, cible obligatoire > 0, score d'étape optionnel avec mode manual/stopwatch. Préserver l'avertissement existant sur les exercices déjà utilisés : les programmes existants gardent leurs snapshots. \ No newline at end of file diff --git a/.ideai/tickets/60/carnet.md b/.ideai/tickets/60/carnet.md new file mode 100644 index 0000000..60722bd --- /dev/null +++ b/.ideai/tickets/60/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#60" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784481536357 +--- diff --git a/.ideai/tickets/60/issue.md b/.ideai/tickets/60/issue.md new file mode 100644 index 0000000..01d6e55 --- /dev/null +++ b/.ideai/tickets/60/issue.md @@ -0,0 +1,16 @@ +--- +id: "4807a946-834e-44a4-8a39-25ad951e6668" +number: 60 +title: "[DevFrontend] Exécution de séance avec module séquence et bips" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#54","kind":"relatesTo"},{"target":"#58","kind":"dependsOn"},{"target":"#59","kind":"dependsOn"}] +agentRefs: [{"agentId":"9933c93a-b8a1-4164-a3bb-7063fdad747d","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784481536357 +updatedAt: 1784481536357 +version: 1 +--- +Implémenter le module `Séquence` sur l'écran d'exécution : affichage passage/étape, étapes temps avec compte à rebours, étapes répétitions avec validation manuelle, boucle multi-passages si reps de série actives, skips distincts étape/passage/série, persistance après pause/kill via use cases backend. Ajouter un port audio testable et un adapter Flutter utilisant des bips courts/longs, sans dépendance des tests widget à un lecteur audio réel. \ No newline at end of file diff --git a/.ideai/tickets/61/carnet.md b/.ideai/tickets/61/carnet.md new file mode 100644 index 0000000..688926e --- /dev/null +++ b/.ideai/tickets/61/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#61" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784481541650 +--- diff --git a/.ideai/tickets/61/issue.md b/.ideai/tickets/61/issue.md new file mode 100644 index 0000000..4a55d16 --- /dev/null +++ b/.ideai/tickets/61/issue.md @@ -0,0 +1,16 @@ +--- +id: "1eed02ec-04af-4966-a0ff-449c4a957ea0" +number: 61 +title: "[DevFrontend] Plan de séance et historique avec résultats d'étapes" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#54","kind":"relatesTo"},{"target":"#58","kind":"dependsOn"}] +agentRefs: [{"agentId":"9933c93a-b8a1-4164-a3bb-7063fdad747d","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784481541650 +updatedAt: 1784481541650 +version: 1 +--- +Afficher les résultats d'étapes dans le plan de séance et l'historique sans remplacer les résultats de série. Une série avec étapes garde son état global `à faire/en cours/terminée/passée`; le détail affiche les passages, étapes terminées/passées, durées/répétitions et scores d'étape. L'édition ponctuelle d'une série passée/terminée permet de corriger les valeurs enregistrées sans rejouer la séquence interactive et sans déplacer la progression courante. \ No newline at end of file diff --git a/.ideai/tickets/62/carnet.md b/.ideai/tickets/62/carnet.md new file mode 100644 index 0000000..b9ea2f7 --- /dev/null +++ b/.ideai/tickets/62/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#62" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784481549150 +--- diff --git a/.ideai/tickets/62/issue.md b/.ideai/tickets/62/issue.md new file mode 100644 index 0000000..59a506e --- /dev/null +++ b/.ideai/tickets/62/issue.md @@ -0,0 +1,16 @@ +--- +id: "a53d84b5-06d9-4683-ac64-9100cb60669f" +number: 62 +title: "[QA] Validation exercices à étapes, persistance et historique" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#54","kind":"relatesTo"},{"target":"#60","kind":"dependsOn"},{"target":"#61","kind":"dependsOn"}] +agentRefs: [{"agentId":"7efa512f-3b3a-47b5-ade0-a2dd13073055","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784481549150 +updatedAt: 1784481549150 +version: 1 +--- +Préparer et exécuter le plan QA pour les exercices à étapes : création/édition et limites max 8, snapshots programme/séance, exécution étapes temps/répétitions, bips 3/2/1/0, enchaînement de chronos, skips étape/passage/série, pause/reprise/kill app, projection historique, édition ponctuelle sans déplacer la progression ni mélanger résultats d'étape et résultats de série. \ No newline at end of file diff --git a/.ideai/tickets/63/carnet.md b/.ideai/tickets/63/carnet.md new file mode 100644 index 0000000..2033c21 --- /dev/null +++ b/.ideai/tickets/63/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#63" +version: 6 +updatedBy: {"kind":"user"} +updatedAt: 1784482343900 +--- diff --git a/.ideai/tickets/63/issue.md b/.ideai/tickets/63/issue.md new file mode 100644 index 0000000..49ba802 --- /dev/null +++ b/.ideai/tickets/63/issue.md @@ -0,0 +1,16 @@ +--- +id: "17f54d9f-0317-429d-a275-480966976ced" +number: 63 +title: "Ajouter les features déjà présentes sur le serveur au client" +status: "open" +priority: "medium" +sprint: "4237adfd-91eb-45ff-9f32-6ce1daa39822" +links: [{"target":"#57","kind":"dependsOn"},{"target":"#46","kind":"dependsOn"}] +agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}] +createdBy: {"kind":"user"} +updatedBy: {"kind":"user"} +createdAt: 1784482089895 +updatedAt: 1784482343900 +version: 6 +--- +Une première version du serveur est disponible. J'aimerais que les features serveur soient ajouté à l'application. Le bon fonctionnement de l'applciation reste prioritaire sur le serveur, à savoir que le serveur sert l'applciation et pas l'inverse, c'est a dire que si une structure de donnée est actuellement différente entre le serveur et l'applciation, alors il faut modifier le serveur pour qu'il accepte la donnée du client. Je pense par exemple aux exercices, entre la création serveur et la réalisation du ticket, je pense que la forme des exercice a été modifiée (ajout entre autre des étapes dans une serie d'un exercice), il faudra alors adapter le serveur à ça. \ No newline at end of file diff --git a/.ideai/tickets/counter.json b/.ideai/tickets/counter.json index 5db1edc..79e9b2a 100644 --- a/.ideai/tickets/counter.json +++ b/.ideai/tickets/counter.json @@ -1,3 +1,3 @@ { - "nextNumber": 55 + "nextNumber": 64 } \ No newline at end of file diff --git a/.ideai/tickets/index.json b/.ideai/tickets/index.json index 5b18fec..7082eef 100644 --- a/.ideai/tickets/index.json +++ b/.ideai/tickets/index.json @@ -524,14 +524,14 @@ { "issueRef": "#46", "path": "46", - "title": "Créer un application headless serveur pour l'application", + "title": "Créer un serveur headless pour l'application", "status": "open", "priority": "low", - "sprint": null, + "sprint": "4237adfd-91eb-45ff-9f32-6ce1daa39822", "assignedAgentIds": [ "57695b92-24d0-4876-837c-76116e70a6ae" ], - "updatedAt": 1784380502263 + "updatedAt": 1784482343900 }, { "issueRef": "#47", @@ -597,11 +597,11 @@ "issueRef": "#53", "path": "53", "title": "[Server] Tests API, contrats OpenAPI et vérification d'intégration", - "status": "open", + "status": "qa", "priority": "low", "sprint": null, "assignedAgentIds": [], - "updatedAt": 1784412015509 + "updatedAt": 1784469423570 }, { "issueRef": "#54", @@ -614,6 +614,102 @@ "57695b92-24d0-4876-837c-76116e70a6ae" ], "updatedAt": 1784451193831 + }, + { + "issueRef": "#56", + "path": "56", + "title": "[DevBackend] Modèle domain + Drift pour exercices à étapes", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "10ee045b-1c41-479e-ba03-dceed9edd495" + ], + "updatedAt": 1784481516635 + }, + { + "issueRef": "#57", + "path": "57", + "title": "Allimenter la mémoire projet pour l'UI online de l'app", + "status": "open", + "priority": "medium", + "sprint": "4237adfd-91eb-45ff-9f32-6ce1daa39822", + "assignedAgentIds": [ + "57695b92-24d0-4876-837c-76116e70a6ae" + ], + "updatedAt": 1784482343900 + }, + { + "issueRef": "#58", + "path": "58", + "title": "[DevBackend] Exécution persistante des étapes et résultats par passage", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "10ee045b-1c41-479e-ba03-dceed9edd495" + ], + "updatedAt": 1784481524706 + }, + { + "issueRef": "#59", + "path": "59", + "title": "[DevFrontend] Éditeur d'exercice avec séquence d'étapes", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "9933c93a-b8a1-4164-a3bb-7063fdad747d" + ], + "updatedAt": 1784481530478 + }, + { + "issueRef": "#60", + "path": "60", + "title": "[DevFrontend] Exécution de séance avec module séquence et bips", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "9933c93a-b8a1-4164-a3bb-7063fdad747d" + ], + "updatedAt": 1784481536357 + }, + { + "issueRef": "#61", + "path": "61", + "title": "[DevFrontend] Plan de séance et historique avec résultats d'étapes", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "9933c93a-b8a1-4164-a3bb-7063fdad747d" + ], + "updatedAt": 1784481541650 + }, + { + "issueRef": "#62", + "path": "62", + "title": "[QA] Validation exercices à étapes, persistance et historique", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "7efa512f-3b3a-47b5-ade0-a2dd13073055" + ], + "updatedAt": 1784481549150 + }, + { + "issueRef": "#63", + "path": "63", + "title": "Ajouter les features déjà présentes sur le serveur au client", + "status": "open", + "priority": "medium", + "sprint": "4237adfd-91eb-45ff-9f32-6ce1daa39822", + "assignedAgentIds": [ + "57695b92-24d0-4876-837c-76116e70a6ae" + ], + "updatedAt": 1784482343900 } ] } \ No newline at end of file