docs(t2-live): cloture documentaire Sprint 6e
Some checks are pending
CI / quality (push) Waiting to run

- CHANGELOG : bloc Sprint 6e (Voie A, Bugs 4/5/6, indicateur, cleanup, removed).
- PARCOURS : section 4 T2 Live (notes + gating 30 mots, candidat initie, timers prepa/dialogue).
- ROADMAP : Sprint 6 marque livre (6b/6c/6e).
- GOLDEN_DATASET : D3 corrige (candidat en premier) + annotations Groupe D (D6 partiel, D7-D11 sprints futurs).
This commit is contained in:
Hermann_Kitio 2026-06-29 14:35:43 +03:00
parent 72795e924e
commit 044a305019
4 changed files with 229 additions and 120 deletions

View file

@ -29,6 +29,44 @@ Chaque entrée suit ce format :
---
## [Unreleased] — 2026-06-29 — Sprint 6e — T2 Live « Voie A » (mix audio temps réel)
### Added
- `public/pcm-record-processor.js` — AudioWorklet « tap » branché sur le mix du contexte partagé : prélève le mix (micro candidat + voix IA) en temps réel et émet des chunks Int16 vers le hook d'enregistrement. Permet un WAV aligné temporellement sur une horloge unique.
- `useAudioCapture` expose désormais `contextRef` (AudioContext partagé) + `mixNodeRef` (GainNode point de convergence) pour partager une horloge unique entre capture, playback et enregistrement.
- Indicateur de prise de parole du candidat : VAD par RMS sur le flux micro Int16 (seuils `SPEAK_RMS=500` / `SILENCE_RMS=250`, debounce 700 ms) pilotant les transitions `speaking``listening`.
- Détection `newTurn` : un chunk audio IA reçu après > 800 ms de silence IA marque la reprise de parole de l'examinateur → réalignement de l'edge-tracking micro + `USER_SILENT`.
### Changed
- **Architecture audio « Voie A »** : passage à **UN SEUL AudioContext au rate NATIF** (≈ 48 kHz), partagé par capture / playback / enregistrement. Suppression du forçage `{ sampleRate: 16000 }` et des deux contextes séparés 16 k / 24 k.
- `useAudioPlayback({ contextRef, mixNodeRef })` ne crée plus son propre contexte : la source IA est routée vers `ctx.destination` (audible) ET vers `mixGain` (captée par le tap). Buffer créé à 24 k, rééchantillonné automatiquement par le contexte natif.
- `useAudioRecording` : enregistrement via tap worklet sur le mix (`mixGain → recordNode → gain(0) → destination`, sink muet pour pull cross-navigateur). Buffer Int16 hors cycle de vie du contexte (`exportWAV()` survit à la fermeture). **WAV mono au rate natif, single-track, zéro resample** (remplace l'ancien WAV 24 k multi-piste).
- `useT2LiveSession` : cycle de vie audio aligné sur la « Voie A » — start sur `ws.onopen` après `capture.start()` résolu ; stop sur `endDialogue` (débranche le tap, buffer conservé) ; cancel ferme le contexte (buffer abandonné, aucun export).
- **Bug 6 — « Nouvelle simulation »** : le routage vers la bonne tâche s'appuie désormais sur le champ `tache` propagé dans le rapport (`report/api.ts`, `types.ts`, `RapportPage.tsx`), sans query param.
### Fixed
- **Anti-blanc EO** : suppression des silences/blancs en début de dialogue grâce à l'horloge unique et au scheduling continu de la voix IA.
- Correction de l'écho de la voix candidat (`mixGain` jamais connecté à `destination`).
- **Bug 4 — « Voir le rapport »** : la navigation vers `/rapport/:id` aboutit bien (garde `navigatingAwayRef` empêchant le cleanup/teardown d'avorter la redirection).
- **Bug 5 — « Annuler » (`cancelDialogue`)** : arrête l'enregistrement, ne déclenche aucune évaluation, ne produit aucun WAV et ne persiste aucune production (WS fermée sans message de fin).
- Stabilité de l'uplink micro : l'architecture « Voie A » supprime l'état React réactif sur la `MediaStream` (source du _starving_ d'uplink), au profit de refs stables sur le contexte/mix partagés.
### Removed
- Helpers `resample16kTo24k` et `mixTracksToInt16` de `audio-utils.ts` (rendus inutiles par l'horloge unique et le single-track). Helpers purs conservés : `arrayBufferToBase64`, `base64ToArrayBuffer`, `int16ToFloat32`, `float32ToInt16`, `concatInt16`, `buildWavHeader`.
- Instrumentation de diagnostic `[BISECT]` retirée des hooks T2 Live (logique runtime VAD / garde-fous / routage des messages conservée).
### Notes
- Tous les bugs ciblés (anti-blanc, Voie A, bugs 4/5/6, indicateur de parole) validés **à l'oreille en navigation privée** — console sans `[BISECT]`.
- Tests frontend : 259 → **269 verts (37 fichiers)**.
- AudioContext / AudioWorklet / WebSocket non matérialisables en jsdom → validation audio à l'oreille (objectif de la session). `useAudioRecording` couvert sur sa surface pure (export WAV, reset).
---
## [Unreleased] — 2026-04-26 — Sprint 6c — Frontend T2 Live UI
### Added

View file

@ -16,120 +16,126 @@
4. En cas de doute : rejouer le groupe Z (smoke test complet)
**Environnement de test :**
- URL frontend local : `http://localhost:5173`
- URL backend : `https://api.expria.app` (ou local si dev simultané)
- Navigateurs à couvrir : Chrome + Firefox + Safari mobile (via DevTools mobile emulation minimum)
**Comptes de test (identiques au backend) :**
| Compte | Plan | Mot de passe |
|---|---|---|
| test.free@gmail.com | free | Expria2025!test |
| test.standard@gmail.com | standard | Expria2025!test |
| test.premium@gmail.com | premium | Expria2025!test |
| test.quota@gmail.com | free (5/5 utilisées) | Expria2025!test |
| Compte | Plan | Mot de passe |
| ----------------------- | -------------------- | --------------- |
| test.free@gmail.com | free | Expria2025!test |
| test.standard@gmail.com | standard | Expria2025!test |
| test.premium@gmail.com | premium | Expria2025!test |
| test.quota@gmail.com | free (5/5 utilisées) | Expria2025!test |
---
## Groupe A — Authentification et routing
| # | Test | Compte | Résultat attendu | ✅/❌ |
|---|---|---|---|---|
| A1 | Arriver sur `/` sans être connecté | — | Page Home publique affichée | |
| A2 | Cliquer "Se connecter" depuis Home | — | Redirection `/login`, formulaire visible | |
| A3 | Inscription avec email + mot de passe valides | nouveau | Compte créé, plan=free, redirection `/dashboard` | |
| A4 | Connexion avec identifiants corrects | test.free | Redirection `/dashboard`, plan Free affiché | |
| A5 | Connexion avec mot de passe incorrect | test.free | Message d'erreur en français, pas de redirection | |
| A6 | Déconnexion depuis le menu utilisateur | test.free | Redirection `/`, session invalidée | |
| A7 | Accès direct à `/dashboard` sans auth | — | Redirection `/login` (ProtectedRoute) | |
| A8 | Accès direct à `/t2-live` en tant que Free | test.free | Redirection ou PaywallModal "Exclusivité Premium" | |
| A9 | Session JWT expirée pendant navigation | test.free | Message "Session expirée", redirection `/login` | |
| A10 | Rafraîchir la page après login | test.free | Reste connecté, dashboard réaffiché | |
| # | Test | Compte | Résultat attendu | ✅/❌ |
| --- | --------------------------------------------- | --------- | ------------------------------------------------- | ----- |
| A1 | Arriver sur `/` sans être connecté | — | Page Home publique affichée | |
| A2 | Cliquer "Se connecter" depuis Home | — | Redirection `/login`, formulaire visible | |
| A3 | Inscription avec email + mot de passe valides | nouveau | Compte créé, plan=free, redirection `/dashboard` | |
| A4 | Connexion avec identifiants corrects | test.free | Redirection `/dashboard`, plan Free affiché | |
| A5 | Connexion avec mot de passe incorrect | test.free | Message d'erreur en français, pas de redirection | |
| A6 | Déconnexion depuis le menu utilisateur | test.free | Redirection `/`, session invalidée | |
| A7 | Accès direct à `/dashboard` sans auth | — | Redirection `/login` (ProtectedRoute) | |
| A8 | Accès direct à `/t2-live` en tant que Free | test.free | Redirection ou PaywallModal "Exclusivité Premium" | |
| A9 | Session JWT expirée pendant navigation | test.free | Message "Session expirée", redirection `/login` | |
| A10 | Rafraîchir la page après login | test.free | Reste connecté, dashboard réaffiché | |
---
## Groupe B — Plan Free (parcours complet)
| # | Test | Compte | Résultat attendu | ✅/❌ |
|---|---|---|---|---|
| B1 | Dashboard Free après connexion | test.free | Compteur "X/5 simulations", aperçu flouté du dashboard Premium visible | |
| B2 | Badge compteur simulations affiché | test.free | Visible en permanence dans le header du dashboard | |
| B3 | Lancer une simulation EE T1 | test.free (quota < 5) | Interface de production affichée, pas de tips visibles | |
| B4 | Soumettre une production EE | test.free | Rapport affiché : score + NCLC visibles, critères floutés avec cadenas | |
| B5 | Rapport flouté avec mentions correctes | test.free | "Disponible en Standard" + bouton upgrade visible | |
| B6 | Lancer une simulation EO T1 | test.free | Interface d'enregistrement audio, pas d'erreur microphone | |
| B7 | Tenter EO T2 live depuis le sélecteur de tâches | test.free | Cadenas + message "Exclusivité Premium" | |
| B8 | Atteindre la 6e simulation | test.quota | Modal de blocage : "5/5 utilisées" + 2 boutons (Standard/Premium) + "Plus tard" | |
| B9 | Cliquer "Plus tard" dans le modal | test.quota | Modal fermé, dashboard visible, pas de redirection | |
| B10 | Cliquer "Mode Examen" | test.free | Cadenas + message "Exclusivité Premium" | |
| B11 | Tenter accès URL direct `/exam-mode` | test.free | Redirection ou PaywallModal | |
| # | Test | Compte | Résultat attendu | ✅/❌ |
| --- | ----------------------------------------------- | --------------------- | ------------------------------------------------------------------------------- | ----- |
| B1 | Dashboard Free après connexion | test.free | Compteur "X/5 simulations", aperçu flouté du dashboard Premium visible | |
| B2 | Badge compteur simulations affiché | test.free | Visible en permanence dans le header du dashboard | |
| B3 | Lancer une simulation EE T1 | test.free (quota < 5) | Interface de production affichée, pas de tips visibles | |
| B4 | Soumettre une production EE | test.free | Rapport affiché : score + NCLC visibles, critères floutés avec cadenas | |
| B5 | Rapport flouté avec mentions correctes | test.free | "Disponible en Standard" + bouton upgrade visible | |
| B6 | Lancer une simulation EO T1 | test.free | Interface d'enregistrement audio, pas d'erreur microphone | |
| B7 | Tenter EO T2 live depuis le sélecteur de tâches | test.free | Cadenas + message "Exclusivité Premium" | |
| B8 | Atteindre la 6e simulation | test.quota | Modal de blocage : "5/5 utilisées" + 2 boutons (Standard/Premium) + "Plus tard" | |
| B9 | Cliquer "Plus tard" dans le modal | test.quota | Modal fermé, dashboard visible, pas de redirection | |
| B10 | Cliquer "Mode Examen" | test.free | Cadenas + message "Exclusivité Premium" | |
| B11 | Tenter accès URL direct `/exam-mode` | test.free | Redirection ou PaywallModal | |
---
## Groupe C — Plan Standard
| # | Test | Compte | Résultat attendu | ✅/❌ |
|---|---|---|---|---|
| C1 | Dashboard Standard après connexion | test.standard | Historique visible, pas de compteur simulations, bouton "Choisir une tâche" actif | |
| C2 | Lancer simulation EE sans limite | test.standard | Accès direct, aucune vérification de quota visible | |
| C3 | Toggle "Suggestions d'idées" activé | test.standard | Suggestions visibles pendant la simulation | |
| C4 | Toggle "Mode focus" activé | test.standard | Tips masqués pendant la simulation | |
| C5 | Rapport complet après soumission EE | test.standard | Score, critères détaillés, erreurs expliquées, modèle, exercices — rien flouté | |
| C6 | Production apparaît dans le dashboard | test.standard | Date, tâche, score affichés dans la liste | |
| C7 | Cliquer une production dans l'historique | test.standard | Rapport complet de cette production réaffiché | |
| C8 | Cliquer "Mode Examen" | test.standard | Message "Réservé au plan Premium" + bouton upgrade | |
| C9 | Cliquer "EO Tâche 2 live" | test.standard | Cadenas + message "Exclusivité Premium" | |
| C10 | Après 5 productions : indice de préparation | test.standard | Section indice visible avec score et message interprétatif | |
| C11 | Upgrade Standard → Premium : prorata affiché | test.standard | Avant confirmation, montant prorata visible (ex : "~10€ aujourd'hui") | |
| # | Test | Compte | Résultat attendu | ✅/❌ |
| --- | -------------------------------------------- | ------------- | --------------------------------------------------------------------------------- | ----- |
| C1 | Dashboard Standard après connexion | test.standard | Historique visible, pas de compteur simulations, bouton "Choisir une tâche" actif | |
| C2 | Lancer simulation EE sans limite | test.standard | Accès direct, aucune vérification de quota visible | |
| C3 | Toggle "Suggestions d'idées" activé | test.standard | Suggestions visibles pendant la simulation | |
| C4 | Toggle "Mode focus" activé | test.standard | Tips masqués pendant la simulation | |
| C5 | Rapport complet après soumission EE | test.standard | Score, critères détaillés, erreurs expliquées, modèle, exercices — rien flouté | |
| C6 | Production apparaît dans le dashboard | test.standard | Date, tâche, score affichés dans la liste | |
| C7 | Cliquer une production dans l'historique | test.standard | Rapport complet de cette production réaffiché | |
| C8 | Cliquer "Mode Examen" | test.standard | Message "Réservé au plan Premium" + bouton upgrade | |
| C9 | Cliquer "EO Tâche 2 live" | test.standard | Cadenas + message "Exclusivité Premium" | |
| C10 | Après 5 productions : indice de préparation | test.standard | Section indice visible avec score et message interprétatif | |
| C11 | Upgrade Standard → Premium : prorata affiché | test.standard | Avant confirmation, montant prorata visible (ex : "~10€ aujourd'hui") | |
---
## Groupe D — Plan Premium
| # | Test | Compte | Résultat attendu | ✅/❌ |
|---|---|---|---|---|
| D1 | Dashboard Premium après connexion | test.premium | Historique, indice, patterns, bouton examen actif, T2 live accessible | |
| D2 | Accéder à EO T2 live | test.premium | Page préparation T2, bouton "Démarrer le dialogue" actif | |
| D3 | Démarrer le dialogue T2 | test.premium | État "Connecting" puis "Listening", l'IA prend la parole en premier | |
| D4 | Répondre en audio à l'IA | test.premium | L'IA réagit après la réponse du candidat, état oscille listening/speaking | |
| D5 | Fin de dialogue T2 | test.premium | Rapport complet affiché, production enregistrée avec tag "T2 Live" | |
| D6 | Déconnexion WebSocket en cours de T2 | test.premium | État "Error" affiché, message utilisateur clair, option de reprise | |
| D7 | Lancer mode Examen EE | test.premium | Page d'avertissement affichée avant démarrage | |
| D8 | Confirmer Examen EE | test.premium | 3 tâches visibles, timer 60:00 démarré, inarrêtable | |
| D9 | Blocage à T=0 (Examen EE) | test.premium | Zone de texte figée, message "Temps écoulé", envoi auto | |
| D10 | Lancer mode Examen EO | test.premium | Timer 12:00, enregistrement actif, tâches enchaînées | |
| D11 | Analyse patterns (5+ productions) | test.premium | Section "Mon profil" avec erreurs récurrentes classées | |
> ⚠️ Certains items décrivent un état cible (sprints futurs), pas l'état implémenté actuel — voir marqueurs par ligne.
| # | Test | Compte | Résultat attendu | ✅/❌ |
| --- | ------------------------------------ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| D1 | Dashboard Premium après connexion | test.premium | Historique, bouton examen actif, T2 live accessible ; indice / patterns / profil ⏳ non implémenté (sprint ultérieur) | |
| D2 | Accéder à EO T2 live | test.premium | Page préparation T2, bouton "Démarrer le dialogue" actif | |
| D3 | Démarrer le dialogue T2 | test.premium | État "Connecting" puis "Listening" ; le candidat prend la parole en premier (le candidat initie l'interaction de service), l'IA répond ensuite | |
| D4 | Répondre en audio à l'IA | test.premium | L'IA réagit après la réponse du candidat, état oscille listening/speaking | |
| D5 | Fin de dialogue T2 | test.premium | Rapport complet affiché, production enregistrée avec tag "T2 Live" | |
| D6 | Déconnexion WebSocket en cours de T2 | test.premium | État "Error" affiché, message utilisateur clair, option de reprise — ⚠️ partiel (cf. note D6) | |
| D7 | Lancer mode Examen EE | test.premium | Page d'avertissement affichée avant démarrage — ⏳ Sprint 8 — non implémenté | |
| D8 | Confirmer Examen EE | test.premium | 3 tâches visibles, timer 60:00 démarré, inarrêtable — ⏳ Sprint 8 — non implémenté | |
| D9 | Blocage à T=0 (Examen EE) | test.premium | Zone de texte figée, message "Temps écoulé", envoi auto — ⏳ Sprint 8 — non implémenté | |
| D10 | Lancer mode Examen EO | test.premium | Timer 12:00, enregistrement actif, tâches enchaînées — ⏳ Sprint 8 — non implémenté | |
| D11 | Analyse patterns (5+ productions) | test.premium | Section "Mon profil" avec erreurs récurrentes classées — ⏳ non implémenté (sprint ultérieur) | |
> **Note D6 (partiel)** : un drop WebSocket subi mène bien à l'état `error` avec un message utilisateur clair (`T2DialoguePage.tsx:165-184`, `useT2LiveSession.ts:336-380`). Mais l'« option de reprise » du critère cible n'est PAS implémentée : l'écran d'erreur n'offre qu'un bouton « Retour aux sujets » (`T2DialoguePage.tsx:176-180`), pas de bouton « Réessayer » / reconnexion. Item à reclasser ✅ une fois la reprise ajoutée.
---
## Groupe E — Paiements Stripe
> ⚠️ Utiliser les cartes de test Stripe :
>
> - Carte valide : `4242 4242 4242 4242` (date future, CVC libre)
> - Carte refusée : `4000 0000 0000 0002`
| # | Test | Compte | Résultat attendu | ✅/❌ |
|---|---|---|---|---|
| E1 | Upgrade Free → Standard (Stripe Checkout) | test.free | Redirection full page vers Stripe, paiement, retour dashboard Standard | |
| E2 | Invalidation du cache plan après paiement | test.free → standard | usePlan() refetch automatiquement, dashboard bascule sans recharger la page | |
| E3 | Upgrade Free → Premium | test.free | Même flux que E1, plan=premium après retour | |
| E4 | Upgrade Standard → Premium avec prorata | test.standard | Montant prorata affiché avant confirmation, accès Premium immédiat | |
| E5 | Paiement refusé (carte 4000 0000 0000 0002) | test.free | Message d'erreur Stripe clair, plan inchangé | |
| E6 | Annuler au milieu du Checkout | test.free | Retour sur `/billing` ou `/pricing`, plan inchangé | |
| # | Test | Compte | Résultat attendu | ✅/❌ |
| --- | ------------------------------------------- | -------------------- | --------------------------------------------------------------------------- | ----- |
| E1 | Upgrade Free → Standard (Stripe Checkout) | test.free | Redirection full page vers Stripe, paiement, retour dashboard Standard | |
| E2 | Invalidation du cache plan après paiement | test.free → standard | usePlan() refetch automatiquement, dashboard bascule sans recharger la page | |
| E3 | Upgrade Free → Premium | test.free | Même flux que E1, plan=premium après retour | |
| E4 | Upgrade Standard → Premium avec prorata | test.standard | Montant prorata affiché avant confirmation, accès Premium immédiat | |
| E5 | Paiement refusé (carte 4000 0000 0000 0002) | test.free | Message d'erreur Stripe clair, plan inchangé | |
| E6 | Annuler au milieu du Checkout | test.free | Retour sur `/billing` ou `/pricing`, plan inchangé | |
---
## Groupe F — Sécurité et permissions
| # | Test | Compte | Résultat attendu | ✅/❌ |
|---|---|---|---|---|
| F1 | URL directe `/t2-live` en Standard | test.standard | Redirection ou PaywallModal, pas d'accès à la page | |
| F2 | Inspecter DevTools → clés privées | — | Aucune clé `SERVICE_ROLE`, `GEMINI`, `STRIPE_SECRET` visible | |
| F3 | Inspecter DevTools → JWT en clair dans localStorage | test.free | JWT Supabase visible (normal, c'est un access token) mais pas de refresh token exposé | |
| F4 | Modifier le plan dans DevTools via Redux/state | test.free | La modification locale n'a aucun effet — le backend reste l'autorité | |
| F5 | Rapport contenant des caractères HTML potentiellement malicieux | test.standard | Rendu comme texte, pas comme HTML (aucune exécution) | |
| F6 | CSP header présent dans la réponse HTTP | — | `Content-Security-Policy` défini dans les headers Cloudflare Pages | |
| F7 | Console navigateur : pas de log de JWT ou données perso | test.free | Aucun `console.log` contenant email, token, payload API | |
| # | Test | Compte | Résultat attendu | ✅/❌ |
| --- | --------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------- | ----- |
| F1 | URL directe `/t2-live` en Standard | test.standard | Redirection ou PaywallModal, pas d'accès à la page | |
| F2 | Inspecter DevTools → clés privées | — | Aucune clé `SERVICE_ROLE`, `GEMINI`, `STRIPE_SECRET` visible | |
| F3 | Inspecter DevTools → JWT en clair dans localStorage | test.free | JWT Supabase visible (normal, c'est un access token) mais pas de refresh token exposé | |
| F4 | Modifier le plan dans DevTools via Redux/state | test.free | La modification locale n'a aucun effet — le backend reste l'autorité | |
| F5 | Rapport contenant des caractères HTML potentiellement malicieux | test.standard | Rendu comme texte, pas comme HTML (aucune exécution) | |
| F6 | CSP header présent dans la réponse HTTP | — | `Content-Security-Policy` défini dans les headers Cloudflare Pages | |
| F7 | Console navigateur : pas de log de JWT ou données perso | test.free | Aucun `console.log` contenant email, token, payload API | |
---
@ -137,15 +143,15 @@
Tests à rejouer sur DevTools mobile emulation (iPhone SE, iPhone 12, Samsung Galaxy) ET sur vrai mobile si possible.
| # | Test | Résultat attendu | ✅/❌ |
|---|---|---|---|
| G1 | Page Home lisible sur écran 375px | Pas de débordement horizontal, CTA accessible | |
| G2 | Formulaire de login sur mobile | Champs bien dimensionnés, clavier virtuel ne cache pas le bouton | |
| G3 | Dashboard Free sur mobile | Compteur visible, aperçu flouté lisible | |
| G4 | Simulation EE sur mobile | Zone de texte utilisable, pas de zoom intempestif | |
| G5 | Enregistrement audio EO sur mobile | Permission microphone demandée, enregistrement fonctionnel | |
| G6 | T2 live sur mobile (Premium) | WebSocket fonctionne, audio bidirectionnel OK | |
| G7 | Modal PaywallModal sur mobile | Scrollable si contenu déborde, bouton fermeture accessible | |
| # | Test | Résultat attendu | ✅/❌ |
| --- | ---------------------------------- | ---------------------------------------------------------------- | ----- |
| G1 | Page Home lisible sur écran 375px | Pas de débordement horizontal, CTA accessible | |
| G2 | Formulaire de login sur mobile | Champs bien dimensionnés, clavier virtuel ne cache pas le bouton | |
| G3 | Dashboard Free sur mobile | Compteur visible, aperçu flouté lisible | |
| G4 | Simulation EE sur mobile | Zone de texte utilisable, pas de zoom intempestif | |
| G5 | Enregistrement audio EO sur mobile | Permission microphone demandée, enregistrement fonctionnel | |
| G6 | T2 live sur mobile (Premium) | WebSocket fonctionne, audio bidirectionnel OK | |
| G7 | Modal PaywallModal sur mobile | Scrollable si contenu déborde, bouton fermeture accessible | |
---
@ -153,18 +159,18 @@ Tests à rejouer sur DevTools mobile emulation (iPhone SE, iPhone 12, Samsung Ga
Les 10 scénarios les plus critiques, à rejouer dans l'ordre avant chaque déploiement production.
| # | Test | Description rapide |
|---|---|---|
| Z1 | Inscription + première simulation Free | Compte créé → simulation → rapport flouté visible |
| Z2 | Blocage quota Free | 6e simulation → modal de blocage |
| Z3 | Simulation Standard complète | Login → simulation → rapport complet → dashboard |
| Z4 | Mode examen bloqué en Standard | Bouton mode examen → message upgrade |
| Z5 | T2 live Premium | Login → T2 live → dialogue → rapport |
| Z6 | Mode examen EE complet | Lancement → timer → T=0 → envoi auto → rapport |
| Z7 | Paiement Free → Standard | Stripe Checkout → retour dashboard Standard sans rechargement |
| Z8 | Prorata Standard → Premium | Montant affiché → confirmation → accès Premium immédiat |
| Z9 | Déconnexion + accès protégé | Logout → accès `/dashboard` → redirection `/login` |
| Z10 | Responsive mobile Home + Login | Affichage correct sur iPhone SE |
| # | Test | Description rapide |
| --- | -------------------------------------- | ------------------------------------------------------------- |
| Z1 | Inscription + première simulation Free | Compte créé → simulation → rapport flouté visible |
| Z2 | Blocage quota Free | 6e simulation → modal de blocage |
| Z3 | Simulation Standard complète | Login → simulation → rapport complet → dashboard |
| Z4 | Mode examen bloqué en Standard | Bouton mode examen → message upgrade |
| Z5 | T2 live Premium | Login → T2 live → dialogue → rapport |
| Z6 | Mode examen EE complet | Lancement → timer → T=0 → envoi auto → rapport |
| Z7 | Paiement Free → Standard | Stripe Checkout → retour dashboard Standard sans rechargement |
| Z8 | Prorata Standard → Premium | Montant affiché → confirmation → accès Premium immédiat |
| Z9 | Déconnexion + accès protégé | Logout → accès `/dashboard` → redirection `/login` |
| Z10 | Responsive mobile Home + Login | Affichage correct sur iPhone SE |
---
@ -190,13 +196,13 @@ Test échoue
> Remplir après chaque session Claude Code frontend.
| Date | Session | Tests rejoués | Résultat | Notes |
|---|---|---|---|---|
| — | — | — | — | — |
| ---- | ------- | ------------- | -------- | ----- |
| — | — | — | — | — |
---
## Historique de ce document
| Version | Date | Changements |
|---|---|---|
| 1.0 | 2026-04-17 | Création initiale, 55 tests frontend |
| Version | Date | Changements |
| ------- | ---------- | ------------------------------------ |
| 1.0 | 2026-04-17 | Création initiale, 55 tests frontend |

View file

@ -299,18 +299,41 @@ Page de préparation :
— Explication du déroulé
(l'IA joue le rôle de l'examinateur)
— Consigne de la tâche affichée
— Zone de notes personnelles (brouillon local du candidat)
— Bouton "Suggestions d'idées"
→ propose des pistes pour nourrir la préparation
→ débloqué seulement quand les notes atteignent ~30 mots
(évite une demande d'idées "à vide")
— Bouton "Démarrer le dialogue"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
SIMULATION LIVE — T2 Expression Orale
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
— L'IA ouvre le dialogue (première prise de parole de l'examinateur)
— Le candidat répond en audio en temps réel
— Le candidat ouvre l'interaction de service (il a besoin d'une
information et initie la conversation — format réel TCF Canada)
— L'examinateur (IA) répond ensuite et relance le dialogue
— Le candidat poursuit en audio en temps réel
— La voix de l'IA est jouée sans blanc ni coupure ;
voix de l'examinateur et voix du candidat partagent la même
horloge audio (dialogue fluide, sans décalage)
— Un indicateur signale qui a la parole
(le candidat parle / il écoute l'examinateur)
— L'IA adapte ses relances selon les réponses du candidat
— Durée libre en mode entraînement (pas de timer sur cette tâche)
— Timer de préparation 2:00 (transition automatique vers le dialogue à 0:00)
— Timer de dialogue 3:30 (210 s)
Pendant le dialogue, le candidat peut :
— "Annuler" → quitte la simulation sans évaluation,
aucun rapport généré, aucun enregistrement conservé
Fin du dialogue (candidat ou IA clôture)
Écran terminal :
— Bouton "Télécharger l'audio" (enregistrement WAV du dialogue complet,
voix candidat + examinateur mixées sur une seule piste)
— Bouton "Voir le rapport" → /rapport/:id
— Bouton "Nouvelle simulation" → relance le parcours T2 Live
Rapport complet généré (même structure que les autres tâches) ✅
Production enregistrée dans le dashboard avec tag "T2 Live"
@ -424,17 +447,18 @@ Webhook Stripe : customer.subscription.deleted
## 5. Matrice des upgrades / downgrades
| Depuis → Vers | Action | Montant facturé | Délai | Données |
|---|---|---|---|---|
| Free → Standard | Stripe Checkout | 19,90€ | Immédiat après webhook | Conservées |
| Free → Premium | Stripe Checkout | 39,90€ | Immédiat après webhook | Conservées |
| Standard → Premium | Prorata Stripe | Différence au prorata | Immédiat après webhook | Conservées |
| Premium → Standard | Résiliation + nouvel abonnement | 19,90€ | Immédiat après webhook | Conservées |
| Premium → Free | Résiliation | — | Immédiat après webhook | Conservées |
| Standard → Free | Résiliation | — | Immédiat après webhook | Conservées |
| Depuis → Vers | Action | Montant facturé | Délai | Données |
| ------------------ | ------------------------------- | --------------------- | ---------------------- | ---------- |
| Free → Standard | Stripe Checkout | 19,90€ | Immédiat après webhook | Conservées |
| Free → Premium | Stripe Checkout | 39,90€ | Immédiat après webhook | Conservées |
| Standard → Premium | Prorata Stripe | Différence au prorata | Immédiat après webhook | Conservées |
| Premium → Standard | Résiliation + nouvel abonnement | 19,90€ | Immédiat après webhook | Conservées |
| Premium → Free | Résiliation | — | Immédiat après webhook | Conservées |
| Standard → Free | Résiliation | — | Immédiat après webhook | Conservées |
> **Règle absolue :** les productions ne sont jamais supprimées, quel que soit le changement de plan.
> L'accès aux features change. Les données restent.
### Détail du prorata Standard → Premium
Stripe crédite automatiquement les jours non consommés du plan Standard et facture les jours restants au tarif Premium. L'utilisateur voit le montant exact avant de confirmer. Aucun calcul manuel requis côté code — comportement natif de Stripe via `subscription.update()` avec `proration_behavior: 'always_invoice'`.

View file

@ -146,30 +146,71 @@
- Tests manuels Groupe E rejoués
- Commit refactor(billing)
## Sprint 6 — T2 Live
## Sprint 6 — T2 Live
18. features/t2-live (ws-client + audio worklet + state machine)
- **6b (frontend)** : capture micro (AudioWorklet 16 kHz uplink) + playback IA + helpers audio purs.
- **6c (frontend)** : state machine T2 (9 états), `useT2LiveSession` (WebSocket + audio + format Gemini natif), pages Sujets / Préparation / Dialogue + routes ; carte EO T2 Live déverrouillée Premium.
- **6e (frontend)** : architecture audio « Voie A » — un seul AudioContext au rate natif partagé (capture + playback + enregistrement), mix temps réel via tap worklet, WAV mono single-track aligné, indicateur de prise de parole (VAD), correction des blancs EO, nettoyage `[BISECT]`. Tests 269/37 ; validation audio à l'oreille.
## Sprint 6.5 — Clean
- Factorisation des fichiers modifiés Sprint 6
- Tests manuels Groupe D rejoués
- Commit refactor(t2-live)
## Sprint 7 — Mode Examen
## Sprint 7 — T1 Live (interruption aléatoire)
19. Timer inarrêtable + readOnly à T=0
- **7a (backend)** : extension du proxy WebSocket Gemini Live (`gemini-3.1-flash-live-preview`, ws brut, pas de SDK) au mode T1 — system prompt « examinateur », décision d'interruption probabiliste, génération de la question de relance sur transcription partielle (DeepSeek). Réutilise l'infra T2 Live. Scoring EO 5 critères × /4. Phonologie live = 0 (TD-08, gelé). Contraintes héritées : pas de `speechConfig`.
- **7b (frontend)** : UI T1 Live réutilisant ws-client + audio worklet + state machine T2 ; phase préparation ; gestion interruption / reprise du flux audio dans la state machine ; gating Premium.
## Sprint 7.5 — Clean
- Factorisation des fichiers modifiés Sprint 7
- Tests manuels Groupe D étendu (T1 Live) rejoués
- Commit refactor(t1-live)
## Sprint 8 — Mode Examen
- Timer inarrêtable + readOnly à T=0
## Sprint 8.5 — Clean
- Factorisation des fichiers modifiés Sprint 8
- Tests manuels Groupe D rejoués
- Commit refactor(exam-mode)
## Sprint 8 — Pré-lancement
## Sprint 9 — Page Admin (outillage opérationnel)
20. MAINTENANCE_MODE implémenté ✅ (2026-04-19)
21. Sentry configuré
22. /ultrareview avant bascule
23. Smoke test Groupe Z complet
24. Procédure DEPLOYMENT.md exécutée
- **9a (backend)** : middleware auth admin (modèle de sécurité à trancher — cf. SECURITY.md) ; endpoint agrégation chiffres clés (inscrits, corrections jour/mois, abonnements actifs, waitlist) ; endpoint waitlist (liste + export CSV).
- **9b (backend)** : CRUD sujets (liste + filtres mode·tâche·statut, create, update, toggle actif, delete) — réutilise le modèle de sujets existant, service role.
- **9c (frontend)** : route admin protégée (hors navigation publique) + Dashboard chiffres clés (compteurs cliquables, refresh périodique).
- **9d (frontend)** : module Gestion des sujets + module Waitlist (tableau + bouton Export CSV).
## Sprint 9.5 — Clean
- Factorisation des fichiers modifiés Sprint 9
- Tests manuels Groupe H (admin) joués
- Commit refactor(admin)
## Sprint 10 — Paiement Orange Money (semi-manuel)
- **10a (backend)** : migration Supabase `commandes_om` (RLS, accès service role) ; endpoint création de commande (code unique + insertion) ; job d'expiration via scheduler Render (pas de cron Vercel).
- **10b (backend)** : endpoint d'activation → écrit le plan via le même chemin que le webhook Stripe (planController / source de vérité unique, ADR 005) — jamais d'écriture SQL directe du plan ; email de confirmation client.
- **10c (frontend)** : page client `/paiement-om` (depuis `/plan`, lien WhatsApp pré-rempli) + ajout de l'option « Payer via Orange Money » sur la page plans.
- **10d (frontend)** : module Commandes OM dans l'admin (onglets en attente / activées / expirées, bouton Activer, countdown, note interne).
## Sprint 10.5 — Clean
- Factorisation des fichiers modifiés Sprint 10
- Tests manuels Groupe H étendu (flux OM complet) joués
- Commit refactor(paiement-om)
## Sprint 11 — Pré-lancement
- MAINTENANCE_MODE implémenté ✅ (2026-04-19)
- Sentry configuré
- /ultrareview avant bascule
- Smoke test Groupe Z complet
- Procédure DEPLOYMENT.md exécutée