docs: cadrage initial Storytime (specs par jalon, roadmap, CLAUDE.md)

Lecteur d'histoires cadenassé pour le coucher (Android/Flutter).
- CLAUDE.md : principes craftsmanship/TDD/clean code/clean archi + decisions techniques
- ROADMAP.md : suivi haut niveau des 7 jalons, a tenir a jour par etape
- docs/specs/ : specs completes decoupees par jalon, etapes en sous-fichiers
- .gitignore Flutter (pubspec.lock versionne, projet applicatif)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vincent Bourdon
2026-06-19 17:03:33 +02:00
commit 16fd4c8c36
32 changed files with 1360 additions and 0 deletions
@@ -0,0 +1,39 @@
# 5.1 — Création du code au premier lancement
## Objectif
Au tout premier lancement, obliger le parent à créer un code à 4 chiffres (saisi
deux fois pour confirmation) avant d'accéder à l'app.
## Périmètre & hors-périmètre
- Inclus : détection « pas de code défini », écran de création (double saisie + validation), enregistrement.
- Exclus : stockage technique détaillé (5.2), porte d'accès (5.3).
## Dépendances
Jalon 0. (Le stockage de 5.2 peut être développé en parallèle/avant ; ici on consomme son interface.)
## Conception
- **Domain** (`features/parental/domain/`) :
- `ParentalCode` (value object) : exactement 4 chiffres ; invariant validé à la construction (sinon `Err(InvalidCodeFormat)`).
- `ParentalCodeRepository` (interface) : `Future<bool> isConfigured()`, `Future<Result<Unit>> setCode(ParentalCode code)`, `Future<bool> verify(ParentalCode code)`. (Impl. en 5.2.)
- **Application** :
- `IsCodeConfiguredUseCase`, `SetParentalCodeUseCase` (refuse si déjà configuré, ou autorise via flux dédié — documenter).
- **Presentation** :
- Au démarrage, `IsCodeConfiguredUseCase` décide : non configuré → `CreateCodeView` bloquant ; configuré → espace enfant.
- `CreateCodeView` : saisie 1 + saisie 2, contrôle d'égalité, format 4 chiffres, gros pavé numérique adapté.
## Plan TDD
1. **Red** : `parental_code_test.dart``ParentalCode('1234')` OK ; `'12'`, `'12a4'`, `''` → invalides.
2. **Green** : implémenter le value object.
3. **Red** : `set_parental_code_use_case_test.dart` — délègue à `setCode` ; refuse si déjà configuré (selon règle documentée).
4. **Green** : implémenter.
5. **Red** : `create_code_view_test.dart` (widget) — deux saisies différentes → erreur affichée, pas d'enregistrement ; deux saisies égales valides → `SetParentalCodeUseCase` appelé.
6. **Green** : implémenter l'écran.
7. **Refactor**.
## Definition of Done
- Tests value object + use case + widget verts.
- Premier lancement impose la création ; lancement ultérieur ne la redemande pas.
- `tool/check.sh` passe ; étape 5.1 cochée dans `ROADMAP.md`.
## Risques / notes
- Ne jamais logguer le code. Le value object ne doit pas exposer la valeur en clair dans `toString`.
@@ -0,0 +1,36 @@
# 5.2 — Stockage sécurisé & vérification
## Objectif
Implémenter `ParentalCodeRepository` : stocker le code de façon sûre (haché, jamais
en clair) et vérifier une saisie.
## Périmètre & hors-périmètre
- Inclus : hachage du code, stockage via `flutter_secure_storage`, vérification.
- Exclus : écrans (5.1/5.3).
## Dépendances
5.1 (interface + `ParentalCode`).
## Conception
- **Data** (`features/parental/data/`) :
- `SecureParentalCodeRepository implements ParentalCodeRepository`.
- Stocke un **hachage** du code (+ sel) dans `flutter_secure_storage` — jamais la valeur en clair.
- Pour 4 chiffres, l'espace est petit (10 000 combinaisons) : le hachage protège contre la lecture directe du stockage, sans prétendre à une résistance forte au brute-force hors-ligne. C'est cohérent avec le modèle de menace (garde-fou enfant, cf. CLAUDE.md §1). Documenter ce choix.
- `isConfigured()` = présence de la clé ; `verify(code)` = comparaison des hachages (comparaison à temps constant si simple à faire).
- **DI** : `parentalCodeRepositoryProvider`.
## Plan TDD
1. **Red** : `secure_parental_code_repository_test.dart` — avec un `flutter_secure_storage` mocké :
- `setCode` écrit une valeur **différente** du code en clair (hachée).
- `verify` renvoie `true` pour le bon code, `false` sinon.
- `isConfigured` reflète la présence de la clé.
2. **Green** : implémenter hachage + repository.
3. **Refactor**.
## Definition of Done
- Tests verts ; aucune écriture du code en clair (vérifié par le test).
- `tool/check.sh` passe ; étape 5.2 cochée dans `ROADMAP.md` ; choix du modèle de menace consigné.
## Risques / notes
- `flutter_secure_storage` s'appuie sur le Keystore Android : tester aussi le cas « valeur absente » au premier lancement.
- Ne pas réinventer de crypto : utiliser une fonction de hachage standard de `crypto`.
@@ -0,0 +1,37 @@
# 5.3 — Accès à l'espace parent & abritage de la gestion podcasts
## Objectif
Relier l'icône discrète de l'écran enfant à une porte parentale (saisie du code)
qui ouvre l'espace parent, et y intégrer la gestion des abonnements (J3).
## Périmètre & hors-périmètre
- Inclus : porte parentale (saisie + vérification), espace parent, déplacement de la gestion podcasts (J3) derrière cette porte.
- Exclus : réglages de limites (J6, qui s'ajouteront à l'espace parent).
## Dépendances
5.1, 5.2 (code + vérification), 3.x (gestion podcasts), 4.2 (icône posée).
## Conception
- **Application** : `VerifyParentalCodeUseCase` (utilise `ParentalCodeRepository.verify`).
- **Presentation** (`features/parental/presentation/`) :
- `ParentGateView` : pavé numérique, saisie 4 chiffres → `VerifyParentalCodeUseCase`. Bon code → navigation espace parent ; mauvais → message + reset saisie. Anti-spam simple (léger délai après N échecs) optionnel, documenté.
- `ParentHomeView` : menu de l'espace parent → « Mes podcasts » (écrans de J3), « Réglages » (changer le code), placeholder « Limites » (rempli en J6).
- Brancher les écrans de gestion podcasts de J3 ici, et **retirer l'accès dev temporaire** mis en place au J3.
- Sortie de l'espace parent → retour à l'écran enfant (qui réactive l'épinglage, J4).
## Plan TDD
1. **Red** : `verify_parental_code_use_case_test.dart` — bon code → `true` ; mauvais → `false` (repo mocké).
2. **Green** : implémenter.
3. **Red** : `parent_gate_view_test.dart` (widget) — saisie correcte → navigation vers l'espace parent (provider mocké) ; incorrecte → message d'erreur, pas de navigation.
4. **Green** : implémenter la porte.
5. **Red** : `parent_home_view_test.dart` — les entrées de menu mènent aux écrans attendus (podcasts, réglages).
6. **Green** : implémenter l'espace parent + câbler la gestion podcasts.
7. **Refactor** : supprimer l'accès dev temporaire de J3 ; vérifier qu'on n'atteint plus la gestion podcasts sans code.
## Definition of Done
- Tests use case + porte + accueil parent verts.
- Depuis l'écran enfant : ⚙️ → code correct → gestion des podcasts ; code incorrect → refus ; aucun accès à la gestion sans code.
- `tool/check.sh` passe ; étape 5.3 cochée dans `ROADMAP.md`.
## Risques / notes
- Vérifier qu'aucune route résiduelle (dev J3) ne contourne la porte parentale.
@@ -0,0 +1,27 @@
# Jalon 5 — Code parental & espace parent
## Objectif
Mettre en place le code parental (création au premier lancement, vérification) et
l'espace parent protégé qui abrite la gestion des podcasts (J3) et, plus tard, les
limites (J6).
## Périmètre
- Création du code à 4 chiffres au premier lancement (double saisie).
- Stockage sécurisé du code (haché, jamais en clair).
- Porte parentale : icône discrète → saisie du code → espace parent.
- Espace parent abritant la gestion des abonnements (déplacée depuis l'accès dev de J3).
## Hors-périmètre
- Récupération de code oublié : **exclu v1** (réinstaller réinitialise tout — décision actée).
- Réglages de limites : posés en J6 (l'espace parent les accueillera).
## Étapes
1. [5.1 — Création du code au premier lancement](01-creation-code.md)
2. [5.2 — Stockage sécurisé & vérification](02-stockage-securise.md)
3. [5.3 — Accès à l'espace parent & abritage gestion podcasts](03-acces-espace-parent.md)
## Definition of Done (jalon)
- Premier lancement → création du code obligatoire ; relances suivantes → pas redemandé.
- Code stocké haché ; vérification correcte/incorrecte gérée.
- L'icône ⚙️ ouvre la porte parentale ; bon code → espace parent (gestion podcasts) ; mauvais code → refus.
- `tool/check.sh` passe ; `ROADMAP.md` 5.1→5.3 cochées.