Le plan de test gagne un § 0bis : le lot 0 a réécrit tout le chemin d'upload (URL V4 signée puis POST /ingest) et rien n'a jamais été déposé depuis un navigateur, alors que les § 1 à 5 en dépendent tous. Ajout aussi de l'ordre de passage en quatre séances, de la façon de reporter un échec, et des résultats déjà obtenus sur le § 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
20 KiB
Plan de test — Studio lots 6 à 8 (mention IA, calibrage, narration, casting)
Écrit le 2026-09-15, pour une première passe le 16/09. Rien de ce plan n'a encore tourné en réel : tout est couvert par des tests unitaires (357 verts côté service) et l'analyse Flutter / TypeScript, mais aucun écran n'a été affiché et aucun audio n'a été généré. Les résultats viennent de Thomas, pas du code.
Légende : colonne ✓ à remplir (✅ / ❌ + une note). Un ❌ dans § 0 arrête la suite. Référence : v2/studio-plan.md § 9.4 (calibrage) et lot 8.
Comment passer ce plan — ajouté le 2026-09-16
Quatre séances, dans cet ordre. Chacune est arrêtée par la précédente : inutile de chercher un bug de narration si l'upload est cassé en dessous.
| Séance | Sections | Durée | Ce qu'un ❌ arrête |
|---|---|---|---|
| 1. Le socle | § 0, § 0bis | ~45 min | Tout. Rien d'autre ne se teste |
| 2. L'argent et l'image | § 1, § 2, § 2bis | ~1 h | Les § 3 à 5 (pas de génération fiable, pas de narration) |
| 3. La narration | § 3, § 4 | ~1 h 30 | Les § 5 et 5bis côté visiteur |
| 4. Les trois fronts | § 5, § 5bis, § 6 | ~1 h | Rien — c'est la fin |
Comment reporter. Un ✅ tout court suffit quand c'est conforme. Un ❌ demande trois choses et pas une de plus : ce que tu as fait, ce que tu as vu, ce que tu attendais. Une capture vaut mieux qu'une description pour tout ce qui est visuel. N'essaie pas de diagnostiquer — c'est mon travail, et un diagnostic dans la colonne ✓ masque souvent le vrai symptôme.
Ce qui n'est pas un bug : la liste des limites connues en fin de document. À lire avant de commencer, pas après avoir signalé l'une d'elles.
0. Prérequis — une fois
| # | Étape | Attendu | ✓ |
|---|---|---|---|
| 0.1 | winget install Gyan.FFmpeg, rouvrir le terminal, ffmpeg -version |
Une version s'affiche. Sans ffmpeg, la génération audio échoue et les crédits sont remboursés | ✅ 16/09 — 9.0.1-full_build, alias winget dans le Path persistant |
| 0.2 | dotnet user-secrets set "AI:ApiKey" "<clé Gemini>" --project manager-service/ManagerService |
dotnet user-secrets list montre AI:ApiKey (et Studio:Fal:Key) |
✅ 16/09 — « Successfully saved AI:ApiKey » (Thomas) |
| 0.3 | Lancer Postgres puis dotnet ef database update avec MIGRATIONS_CONNECTION (voir la note mémoire EF) |
Migrations appliquées : AddAiWatermarkBurned, StudioCalibrationEngravingStyle, StudioCreditUnitPerReference, AddNarrators, AddGeoPointAudio, AddCasting |
✅ 16/09 — les 6 appliquées en une passe, « Done. » |
| 0.4 | dotnet run (manager-service), flutter run -d chrome (manager-app) |
Les deux démarrent, connexion SuperAdmin OK | |
| 0.5 | Dialogue d'instance — il n'est pas dans un écran : pied du menu de gauche → icône ⇄ « Changer d'instance » (visible seulement en SuperAdmin) → dans la liste, le crayon « Configurer le plan » de la ligne voulue. Activer Assistant IA, puis Studio, puis accorder 100 crédits | Solde 100 dans la jauge du menu | |
| 0.5b | ⚠️ Dans ce même dialogue, décocher Assistant IA | L'interrupteur Studio se désactive tout seul et devient grisé : pas de Studio sans assistant (décision 12). Recocher les deux avant de continuer | |
| 0.6 | Guide IA › Personnages : un personnage Léon avec une voix (Umbriel) et une intonation, un second La sentinelle avec une autre voix (Kore) | Les deux apparaissent dans la liste, sans « (sans voix) » | |
| 0.7 | Donner un portrait à Léon (onglet Visage → générer ou choisir une vue Portrait) | La vue Portrait est enregistrée |
0bis. Le socle d'upload — ajouté le 2026-09-16
Pourquoi cette section n'existait pas, et pourquoi elle doit exister. Le lot 0 a réécrit tout le chemin d'upload de manager-app : plus aucun appel au SDK Firebase, tout passe par une URL V4 signée puis
POST /ingest. C'est codé depuis le 13/09 et couvert par des tests unitaires, mais aucun fichier n'a jamais été déposé depuis un navigateur. Les § 1 à 5 supposent tous que ça marche : la médiathèque reçoit les images générées, les MP3 de narration, les portraits. Tester la narration avant ce socle, c'est chercher une fuite au toit avant d'avoir regardé les fondations.
| # | Étape | Attendu | ✓ |
|---|---|---|---|
| 0b.1 | Médiathèque : déposer une image JPEG de plus de 2560 px | Elle monte (barre de progression), apparaît dans la grille, s'ouvre. Réduite à 2560 px côté long | |
| 0b.2 | Déposer un PNG à transparence | Reste un PNG, l'alpha est préservé (le voir sur fond coloré) | |
| 0b.3 | Déposer un MP3, un PDF | Montent tels quels, lisibles / téléchargeables depuis la fiche | |
| 0b.4 | Remplacer le fichier d'une ressource existante | Le nouveau s'affiche partout où la ressource était utilisée, l'ancien ne traîne pas | |
| 0b.5 | Supprimer une ressource non utilisée | Disparaît de la grille ; recharger la page ne la ramène pas | |
| 0b.6 | ⚠️ Fichier au-dessus du plafond de son type (ex. une image > 30 Mo) | Refusé avec un message traduit, pas une erreur brute ni un échec muet. Tester en FR, EN et NL | |
| 0b.7 | ⚠️ Sélecteur de ressource : champ image d'une section → ajouter un fichier depuis la modale → Enregistrer | Le fichier arrive et le champ de la section le pointe. C'est le piège historique : la modale embarque ResourcesScreen, une régression ici touche les 13 types de section |
|
| 0b.8 | Ouvrir une ressource déposée depuis visitapp-web et depuis mymuseum-visitapp | L'URL s'ouvre des deux côtés (le jeton de téléchargement est bien posé à l'écriture) | |
| 0b.9 | Remplacer l'image d'une visite déjà téléchargée dans mymuseum-visitapp | L'app la re-télécharge, n'affiche pas l'ancienne | |
| 0b.10 | Sélecteur de langues d'une configuration | Le luxembourgeois (LB) est proposé, avec son drapeau (ajouté au lot 0, jamais vu à l'écran) |
1. Mention IA dans le fichier (lot 6)
| # | Étape | Attendu | ✓ |
|---|---|---|---|
| 1.1 | Studio : générer une image et la valider | L'image arrive dans la médiathèque avec la facette IA | |
| 1.2 | Télécharger le fichier validé, l'ouvrir dans un lecteur de métadonnées (ex. exiftool, ou Propriétés › Détails) | XMP DigitalSourceType = trainedAlgorithmicMedia, droits = nom de l'instance |
|
| 1.3 | Dialogue d'instance : cocher Graver la mention IA, générer et valider une nouvelle image | Bandeau « Généré par IA · AI-generated » en bas à droite de l'image. L'ancienne image n'a pas changé | |
| 1.4 | ⚠️ Enregistrer l'instance depuis un autre écran (ex. Abonnement) | La case gravure reste cochée : un enregistrement qui ne l'envoie pas ne la remet pas à faux | |
| 1.5 | Décocher la gravure | Les images suivantes n'ont plus de bandeau |
2. Calibrage et unité de crédit
| # | Étape | Attendu | ✓ |
|---|---|---|---|
| 2.1 | Studio › Identité : ouvrir la grille des styles | 8 tuiles avec un moulin, un style chacune (plus aucune tuile vide) | |
| 2.2 | Guide IA › Personnages › Visage : grille des archétypes | 12 mannequins au trait, un par pose | |
| 2.3 | Générer 3 variantes sans image de référence | Estimation et débit = 6 crédits (2 par image) | |
| 2.4 | Ajouter 3 images de référence à l'identité, relancer 3 variantes | Estimation = 15 crédits (3 × (2 + 3)) | |
| 2.5 | Style Gravure, gabarit « Décor d'énigme », 3 variantes | Images noir et blanc, aucune légende ni signature (c'était ~4/10 avant correction) | |
| 2.6 | Gabarit « Objet d'époque » depuis une photo d'objet portant une date | La date reste gravée sur l'objet | |
| 2.7 | Relire les planches outputs/studio-calibrage-2026-09-15/ |
Ton verdict « du même projet » par série, à reporter dans studio-plan § 9.4 |
2bis. Les crédits, pour de vrai — ajouté le 2026-09-16
Le § 2 vérifie que l'estimation affichée est juste. Cette section-ci vérifie que le solde bouge juste, ce qui n'est pas la même chose : le mécanisme est une réservation puis un débit réel, avec remboursement en cas d'échec, et rien de tout ça n'a tourné en conditions réelles.
| # | Étape | Attendu | ✓ |
|---|---|---|---|
| 2b.1 | Noter le solde, lancer une génération de 3 variantes, regarder la jauge pendant le travail | Le solde baisse dès le lancement (réservation), pas à l'arrivée des images | |
| 2b.2 | À la fin | Le débit définitif égale l'estimation annoncée, ni plus ni moins | |
| 2b.3 | Fermer le panneau sans valider aucune variante | Les crédits restent dépensés (l'appel a coûté), mais rien ne s'ajoute au quota de stockage | |
| 2b.4 | Lancer deux générations coup sur coup sans attendre la première | Les deux réservations se cumulent ; le solde ne descend jamais sous zéro | |
| 2b.5 | Solde insuffisant pour 3 variantes mais suffisant pour 1 | Refus avant l'appel, message clair, aucun crédit pris | |
| 2b.6 | ⚠️ Génération qui échoue (couper le réseau du serveur vers fal, ou fausse clé) | Le solde revient à sa valeur d'avant. C'est le point le plus important de la section | |
| 2b.7 | Abonnement › journal des crédits | Une ligne par mouvement (réservation, débit, remboursement), export CSV lisible | |
| 2b.8 | SuperAdmin : accorder 50 crédits à l'instance | Solde +50, et la date d'expiration repoussée à +12 mois sur tout le solde (pas seulement sur les 50) |
3. Qui raconte quoi (lot 8a et 8c)
Préparer une configuration avec un parcours de 4 étapes (titre + description remplis en FR et NL) et un article avec du texte.
| # | Étape | Attendu | ✓ |
|---|---|---|---|
| 3.1 | Écran Configuration : carte Narration visible | Deux champs : « Guide de cette visite » et « Narrateur par défaut de la visite » | |
| 3.2 | Guide de la visite = Guide de l'instance, narrateur par défaut = Le guide raconte, Enregistrer | Enregistré ; recharger l'écran garde les valeurs | |
| 3.3 | Ouvrir le parcours : dans le rail, une entrée Narrateurs sous « Parcours » | L'onglet s'ouvre, une ligne par étape | |
| 3.4 | Sans rien choisir | Chaque étape affiche en gris « ↳ » + le guide de l'instance, ou « Aucun narrateur » s'il n'y en a pas | |
| 3.5 | Narrateur par défaut du parcours = Léon | Toutes les lignes passent à « ↳ Léon » en gris | |
| 3.6 | Étape 3 : choisir La sentinelle | La ligne 3 affiche La sentinelle en gras ; les autres restent « ↳ Léon » | |
| 3.7 | Fermer et rouvrir le parcours | Le choix de l'étape 3 et le défaut sont conservés | |
| 3.8 | Toutes les étapes suivent le défaut | Confirmation « Retirer le narrateur choisi sur 1 étape(s) ? », puis l'étape 3 repasse à « ↳ Léon » | |
| 3.9 | Remettre La sentinelle sur l'étape 3 (pour la suite) | — | |
| 3.10 | ⚠️ Archiver La sentinelle (Personnages) puis revenir à l'onglet | L'étape 3 retombe sur « ↳ Léon » : un personnage archivé ne parle plus | |
| 3.11 | Désarchiver La sentinelle | L'étape 3 la retrouve | |
| 3.12 | Instance sans Studio (décocher dans le dialogue) | L'entrée Narrateurs disparaît du rail et le bloc Narration de l'article aussi. Réactiver ensuite |
4. Générer l'audio (lot 8b et 8c)
| # | Étape | Attendu | ✓ |
|---|---|---|---|
| 4.1 | Onglet Narrateurs : colonne Audio | « 2 à générer · N crédits » par étape (FR + NL) ; barre du bas « X fichier(s) audio à générer · Y crédits » | |
| 4.2 | Générer l'audio | Confirmation qui répète le total. Rien ne part avant « Oui » | |
| 4.3 | Confirmer | Notification « Génération lancée » ; en moins d'une minute les lignes passent à Audio à jour sans rien toucher (relecture toutes les 5 s) | |
| 4.4 | Jauge de crédits | Débit ≈ 1 crédit par minute d'audio par langue, jamais plus que l'estimation affichée | |
| 4.5 | Médiathèque, filtre Audio | Un MP3 par étape et par langue, libellé « Léon · titre (FR) », facette IA | |
| 4.6 | Écouter un MP3 (étape 3) | Voix de La sentinelle, dans la bonne langue, avec l'intonation du personnage | |
| 4.7 | ⚠️ Piège corrigé : modifier le titre d'une étape juste après la génération, attendre l'enregistrement, rouvrir | L'audio de l'étape est toujours rattaché (avant correction, l'enregistrement le détachait) | |
| 4.8 | Revenir à l'onglet Narrateurs | L'étape modifiée est « 2 à régénérer » : son texte a changé | |
| 4.9 | Changer la voix de Léon (Personnages › Voix) | Toutes les étapes de Léon passent « à régénérer » ; celle de La sentinelle reste à jour | |
| 4.10 | Régénérer | L'ancien MP3 disparaît de la médiathèque, le nouveau le remplace | |
| 4.11 | Étape avec un audio déposé à la main en FR, puis générer FR | L'audio généré prend sa place dans l'étape, le fichier déposé reste dans la médiathèque | |
| 4.12 | Article : bloc Narration audio sous les réglages audio | Narrateur « ↳ Léon » (hérité), état, bouton Générer | |
| 4.12b | Carte : réglages de la section, Narrateur par défaut des points = La sentinelle, enregistrer la section | Chaque fiche de point affiche « ↳ La sentinelle » dans son bloc Narration audio | |
| 4.12c | Carte : ouvrir un point, choisir Léon dans son bloc Narration audio | Le point s'enregistre seul ; moins d'une seconde après, l'état se relit (Léon, « à générer ») | |
| 4.12d | Carte : générer l'audio d'un point, puis modifier son titre | L'audio reste rattaché au point après rechargement ; l'état passe « à régénérer » | |
| 4.12e | web : ouvrir ce point sur la carte | Lecteur audio en tête de la fiche, portrait + nom du narrateur au-dessus (lot 8e) | |
| 4.12f | mymuseum-visitapp : ouvrir ce point | Lecteur audio au-dessus de la description, portrait + nom | |
| 4.12g | tablet-app : ouvrir ce point | Portrait + nom à gauche du bouton de lecture | |
| 4.12h | Les trois : langue sans audio pour ce point | Pas de lecteur sur mobile et tablette (pas de repli de langue) ; le web retombe sur la première langue, comme pour l'article | |
| 4.13 | Article : générer, puis Enregistrer la section après le passage à « Audio à jour » | L'audio reste sur l'article après rechargement | |
| 4.14 | Personnage sans voix en narrateur, Générer | Message « Ce narrateur n'a pas de voix », aucun crédit réservé | |
| 4.15 | Solde à 0, Générer | Message « Crédits Studio insuffisants » | |
| 4.16 | Couper le réseau du serveur vers Gemini (ou mauvaise clé), Générer | Les lignes ne passent jamais à jour, et les crédits réservés sont remboursés (jauge) |
5. Côté visiteur — le portrait du narrateur (lot 8d)
Même configuration, avec l'audio généré en § 4. Léon a un portrait, La sentinelle non.
| # | App | Étape | Attendu | ✓ |
|---|---|---|---|---|
| 5.1 | visitapp-web | Ouvrir l'article | Le lecteur audio joue ; à la place de « GUIDE AUDIO », portrait + « Léon » | |
| 5.2 | visitapp-web | Parcours en liste, étapes 1 et 3 | L'audio joue (avant, un audio rangé par identifiant n'avait pas de lecteur) ; portrait de Léon à l'étape 1, disque neutre + « La sentinelle » à l'étape 3 | |
| 5.3 | visitapp-web | Parcours en carte (showMap) |
Même chose dans la vue carte | |
| 5.4 | visitapp-web | Changer la langue en NL | L'audio NL joue, même narrateur | |
| 5.5 | mymuseum-visitapp (en ligne) | Article | Lecteur replié à droite ; déplié, le portrait ouvre la ligne | |
| 5.6 | mymuseum-visitapp (en ligne) | Parcours, vue contenu et vue carte | L'audio de l'étape joue, portrait + nom au-dessus du lecteur | |
| 5.7 | mymuseum-visitapp (hors ligne) | Article téléchargé, mode avion | L'audio joue ; pas de portrait (limite connue, il n'est pas téléchargé) | |
| 5.8 | tablet-app | Article | Portrait + nom à gauche du bouton de lecture | |
| 5.9 | Les trois | Un audio déposé à la main (pas une narration) | Lecteur habituel, aucun portrait | |
| 5.10 | Les trois | Une ancienne étape dont l'audio est une URL | L'audio joue toujours |
5bis. Casting — « les personnes que vous allez rencontrer » (lot 8bis)
Migration supplémentaire à appliquer : AddCasting. Même configuration qu'en § 3 (Léon et La sentinelle avec portrait,
ajouter La cuisinière sans portrait comme narratrice d'une étape).
| # | App | Étape | Attendu | ✓ |
|---|---|---|---|---|
| 5b.1 | manager | Configuration sans aucun narrateur ni guide | Carte « Personnages de la visite » : « Aucun personnage ne raconte cette visite » | |
| 5b.2 | manager | Seul le guide (Léon) raconte | « Léon raconte toute cette visite (le guide) », ni liste ni interrupteur | |
| 5b.3 | manager | Étape 3 = La sentinelle, étape 4 = La cuisinière | Liste de 3 : Léon en gris « ↳ » (hérité), les deux autres en gras, avec « N étape(s) » ; ordre = ordre d'apparition dans la visite | |
| 5b.4 | manager | Ligne La cuisinière | « Pas de portrait : ne peut pas être montré », case grisée | |
| 5b.5 | manager | Réordonner (glisser La sentinelle en tête), décocher Léon, armer « Présenter les personnages au visiteur », Enregistrer, recharger | Ordre, cases et interrupteur conservés | |
| 5b.6 | manager | Toucher l'ordre puis Annuler | Retour à l'état enregistré | |
| 5b.7 | web | Ouvrir la visite (Léon décoché) | Pas d'écran d'entrée : un seul portrait montré | |
| 5b.8 | manager + web | Recocher Léon, enregistrer, rouvrir la visite (attendre ~1 min, cache) | Écran d'entrée : La sentinelle puis Léon, portraits ronds, bouton « Commencer la visite » | |
| 5b.9 | web | Fermer, revenir à la liste des sections | L'écran ne revient pas dans la même session ; un nouvel onglet le remontre | |
| 5b.10 | web | Passer en NL | Titre « De personen die u zult ontmoeten » | |
| 5b.11 | mymuseum-visitapp | Ouvrir la visite | Même écran, une fois par lancement de l'app | |
| 5b.12 | tablet-app | Charger la configuration | Même écran au chargement | |
| 5b.13 | manager | Désarmer l'interrupteur, enregistrer | Plus aucun écran d'entrée sur les trois apps |
6. Non-régression
| # | Étape | Attendu | ✓ |
|---|---|---|---|
| 6.1 | Guide IA (assistant) : poser une question sur un contenu dont le titre contient du gras ou un & |
Réponse normale, titre cité sans balises ni & (le nettoyage HTML de l'assistant est désormais partagé avec la narration) |
|
| 6.2 | Image générée côté visiteur | Mention « Image générée par IA » toujours présente (lot 6) | |
| 6.3 | Enregistrer un article sans toucher à la narration | Rien ne change dans ses audios | |
| 6.4 | Modifier un point de carte dans l'ancienne fenêtre | Enregistrement OK ; son audio éventuel n'est pas effacé |
Limites connues (pas des bugs)
- Les points de carte n'ont pas encore d'assignation de narrateur ni de bouton Générer dans le manager.
- Hors ligne, mymuseum-visitapp ne montre pas le portrait.
- Le portrait affiché est le portrait actuel du personnage ; le nom et la voix sont ceux de l'audio entendu.
- Les extraits d'écoute des voix (lot 7) ne sont pas encore produits.
Ce que ce plan ne couvre pas — ajouté le 2026-09-16
À savoir avant de conclure « le Studio est testé » :
- L'infra du bucket de prod. Le lot 0 est codé mais son volet infra reste ouvert : CORS des origines
de prod, cycle de vie
incoming/, et surtout fermeture des règles Storage en écriture — qui doit se faire après le déploiement du nouveau manager-app, sinon l'upload de la version encore en ligne casse. Rien de tout ça ne se voit depuis un poste de dev. - Le plafond journalier par utilisateur : il est configurable, champ « Plafond par utilisateur et par jour (crédits, 0 = aucun) » dans le dialogue d'instance, sous l'interrupteur Studio. Il vaut 0 par défaut, donc rien ne se déclenche tant qu'on n'y met pas une valeur — à tester le jour où on veut vérifier le garde-fou « stagiaire », pas dans cette passe.
- Le webhook fal en conditions réelles : en local
Studio:WebhookBaseUrlest vide, la relecture se fait par sondage. La vérification de signature ED25519 ne sera exercée qu'en préprod. - La charge : tout ce plan se passe à un seul utilisateur. Le sémaphore à 2 images simultanées de l'ingestion ne se voit pas à cette échelle.
- Les lots 9 à 11 (avant/après, vidéo, 3D) : pas commencés.