feat(bibliothèque): migration Drift v16, seed basket et seeding au démarrage (ticket #80)

Ajoute la migration Drift v16 (enum ExerciseCategory, champ isExample sur
exercises/programs/workout_templates, table local_seed_metadata), le
contenu seed basket déterministe (21 exercices, 1 programme, 1 séance-
modèle) dans lib/application/starter_content/, les ports
StarterSeedStateRepository/StarterContentRepository et le use case
SeedStarterContentUseCase branché dans AppBootstrap.create().

Validé GO par QA/Main : dart analyze propre, suite complète 167 tests
(4 échecs préexistants sans rapport avec ce lot), 4 nouveaux tests seed
tous verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 07:29:49 +02:00
parent 75f52200a2
commit 24e266c0e9
12 changed files with 2052 additions and 16 deletions

View File

@ -193,6 +193,9 @@ class Exercises extends SyncableTable {
IntColumn get defaultTargetScoreTimeMs => integer().nullable()();
BoolColumn get autoStartNextTimedStep =>
boolean().withDefault(const Constant(true))();
TextColumn get category =>
text().withDefault(const Constant('uncategorized'))();
BoolColumn get isExample => boolean().withDefault(const Constant(false))();
DateTimeColumn get archivedAt => dateTime().nullable()();
@override
@ -209,6 +212,8 @@ class Exercises extends SyncableTable {
'CHECK (default_target_score IS NULL OR default_target_score >= 0)',
'CHECK (default_target_score_time_ms IS NULL OR '
'default_target_score_time_ms > 0)',
"CHECK (category IN ('shoot', 'freeThrows', 'dribble', 'finishing', "
"'conditioning', 'defense', 'mobility', 'uncategorized'))",
];
}
@ -278,6 +283,7 @@ class Programs extends SyncableTable {
TextColumn get name => text().withLength(min: 1)();
IntColumn get defaultRestSeconds => integer()();
BoolColumn get isExample => boolean().withDefault(const Constant(false))();
@override
List<String> get customConstraints => ['CHECK (default_rest_seconds >= 0)'];
@ -360,6 +366,20 @@ class WorkoutTemplates extends SyncableTable {
TextColumn get name => text().withLength(min: 1)();
DateTimeColumn get lastStartedAt => dateTime().nullable()();
BoolColumn get isExample => boolean().withDefault(const Constant(false))();
}
class LocalSeedMetadata extends Table {
@override
String get tableName => 'local_seed_metadata';
TextColumn get key => text()();
IntColumn get version =>
integer().customConstraint('NOT NULL CHECK (version >= 0)')();
DateTimeColumn get appliedAt => dateTime()();
@override
Set<Column> get primaryKey => {key};
}
class WorkoutTemplatePrograms extends SyncableTable {
@ -653,7 +673,7 @@ class ActiveExerciseStepResults extends SyncableTable {
'CHECK (step_index >= 0)',
"CHECK (step_type_snapshot IN ('time', 'reps'))",
'CHECK (target_value_snapshot > 0)',
"CHECK (score_input_mode_snapshot IS NULL OR "
'CHECK (score_input_mode_snapshot IS NULL OR '
"score_input_mode_snapshot IN ('manual', 'stopwatch'))",
"CHECK (status IN ('completed', 'skipped'))",
'CHECK (actual_time_ms IS NULL OR actual_time_ms >= 0)',
@ -813,7 +833,7 @@ class WorkoutHistoryStepResults extends SyncableTable {
'CHECK (step_index >= 0)',
"CHECK (step_type_snapshot IN ('time', 'reps'))",
'CHECK (target_value_snapshot > 0)',
"CHECK (score_input_mode_snapshot IS NULL OR "
'CHECK (score_input_mode_snapshot IS NULL OR '
"score_input_mode_snapshot IN ('manual', 'stopwatch'))",
"CHECK (status IN ('completed', 'skipped'))",
'CHECK (actual_time_ms IS NULL OR actual_time_ms >= 0)',