DOCS/test-plan-studio-narration.md
Thomas Fransolet a588c53549 Studio : ordre de passage du plan de test et socle d'upload
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>
2026-09-16 15:18:40 +02:00

20 KiB
Raw Blame History

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 &amp; (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:WebhookBaseUrl est 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.