diff --git a/STATUS.md b/STATUS.md index f256b3f..078b075 100644 --- a/STATUS.md +++ b/STATUS.md @@ -156,7 +156,7 @@ Dix-neuf chantiers de canaux et d'IA, chacun avec son plan détaillé. État au - **V1** — **visiteur web** : `visitapp-web` build ✅, les 13 types de section rendus, assistant IA inclus ; **il ne manque que le Dockerfile et l'entrée `app.myinfomate.be` au compose**. Bloquant, le plan Essentiel vendu par l'onboarding est web-only. Plus l'**écran Guide IA**. La **Médiathèque** (basculée en V1 le 2026-09-02) est **codée de bout en bout le même jour** — index inverse des usages, panneau de détail, facettes, suppression protégée, les deux bugs corrigés ; `dotnet test` 236/236 et `flutter build web` verts, **mais la checklist de 15 points reste à passer dans un navigateur** (colonne « À tester » du kanban). Voir [v1-mediatheque-plan.md](v1-mediatheque-plan.md). - **Livrés** — export PDF des stats à la demande, historique 13 mois pour tous les plans, refonte de l'écran Statistiques (⚠️ jamais ouvert dans un navigateur). -- **V2** — module Studio (génération IA d'images), Personnages (une entité `Persona` unifiant trois plans), TTS pré-généré, kiosk web, VR Meta Quest, import IA + RAG. ⛔ **Prérequis dur commun au Studio et au TTS** : le backend ne sait pas écrire dans le bucket (`IResourceBlobService` n'a que `DeleteAsync`). +- **V2** — module Studio (génération IA d'images), Personnages (une entité `Persona` unifiant trois plans), TTS pré-généré, kiosk web, VR Meta Quest, import IA + RAG. 🛠️ **Studio : lots 0 à 6 codés et commités (branche `studio`, pas encore poussée), calibrage §9.4 fait le 2026-09-15** (≈ 3 $ réels, unité de crédit arrêtée : 1 crédit = 0,015 $ de coût fal, prix de vente avec marge). Lot 7 en cours : échantillons de style et figures d'archétypes embarqués ; reste l'écoute des voix (clé Gemini absente en local). Le prérequis « le backend ne sait pas écrire dans le bucket » est levé depuis le lot 0. - 📄 **La frontière entre le Studio IA et le canal XR est tranchée depuis le 2026-09-11** : [v2/immersif-frontiere-plan.md](v2/immersif-frontiere-plan.md). Les deux plans se renvoyaient la balle (le Studio mettait la 3D en « dépend du chantier VR », le plan VR décrivait les POI sur GLB sans dire d'où venait le GLB). Trois couches : ressources immersives (360/GLB, **se vendent sans casque**), consommation, et le Studio comme *source d'approvisionnement* de la première. **Cinq décisions de rétro-compat touchent des lots antérieurs à la 3D** — à lire avant la première migration du Studio, pas en arrivant au lot 11. - ✅ **Le backend du canal XR est fait le 2026-09-11** (lot `XR-1` de [v2/vr-quest-unity-plan.md](v2/vr-quest-unity-plan.md)) : `Device.AppType` + migration, `DeviceController` résout l'`ApplicationInstance` sur le canal au lieu de `Tablet` en dur, `Get` filtre par canal, `ApiKeyAppType.VrApp`, `appType` dans `manager_api_new`. 224 tests au vert. Et **`XR-3` était déjà fait** — l'export est ouvert aux apps depuis `9cc45c5`, le plan était en retard sur le code. - ✅ **`XR-2` fait le 2026-09-12 — le canal VR est administrable.** L'écran XR est une coquille à deux sous-onglets : « Configuration » réutilise `AppConfigurationLinkScreen` tel quel (déjà générique sur l'`appType`, zéro code neuf), « Casques » est la grille avec pincode d'appairage, état connecté, batterie, version et dernier vu. i18n FR/EN/NL. ⚠️ Deux affirmations du plan étaient fausses : la grille kiosk liste des `AppConfigurationLink`, pas des `Device` (le filtre par canal était déjà implicite, et le paramètre `appType` de `/api/device` ne sert pas à cet écran), et batterie/version/dernier vu ne sont que dans `DeviceDetailDTO` — donc un appel de détail par casque, assumé sur une flotte qui se compte en unités. Ne restent que deux puces documentaires de `XR-3` (contrat d'export versionné, décision multilingue). diff --git a/kanban.html b/kanban.html index af24b64..e2f8851 100644 --- a/kanban.html +++ b/kanban.html @@ -984,8 +984,9 @@
-
Studio IAV2 · lots 0-4 codés le 13/09, gate à passer
+
Studio IAV2 · lots 0-6 codés, calibrage fait le 15/09, gate conservateur à passer

Module Studio — génération d'images sous identité visuelle

+

15/09 : lots 5 et 6 codés, calibrage §9.4 fait. Mention IA dans le fichier (XMP IPTC) + gravure en option. Calibrage sur 61 générations (Fourneau en gravure, Fort en aquarelle) : trois défauts corrigés avant tout client — négatif en double négation, titre de fiche recopié en légende, date d'un objet effacée. Coût réel fal : 0,03 $ l'image + 0,015 $ par référence ; unité arrêtée : 1 crédit = 0,015 $ de coût, vendu avec marge (≈ 0,05 €). Planches dans outputs/studio-calibrage-2026-09-15/. Reste : jugement visuel de Thomas, test du conservateur (gate lot 4), push de la branche.

🛠️ Lots 0 à 4 codés le 13/09 sur la branche studio (manager-service, manager-app, mymuseum-visitapp) : ingestion serveur, crédits, identité visuelle (menu Studio), génération fal.ai derrière IGenerationProvider (fournisseur interchangeable), onglet Générer dans le sélecteur de ressource, image d'étape rattachée à une ressource. Reste avant le gate : bucket de dev + CORS, clé fal.ai (Studio__Fal__Key), migrations appliquées, calibrage et prix (§9.4), puis le test du conservateur.

Plan consolidé le 13/09. Le §8 donne, pour les lots 0 à 4, les prérequis, les étapes, les vérifications et le gate. Le §9 contient un catalogue v0 (8 styles, règle de préambule, 3 gabarits) à calibrer en fin de lot 3. Six décisions ont été prises : upload par URL signée + ingestion serveur (bucket fermé aux clients, gros fichiers hors VPS), MVP limité à « depuis le texte » et « depuis une image » (objets et lieux), prix différés avec Grant manuel, une recharge qui prolonge tout le solde, plafonds d'upload prudents, catalogue calibré au lot 3. Le webhook fal.ai est signé en ED25519, pas en HMAC.

Le produit n'est pas l'accès aux modèles, c'est la cohérence. N'importe qui peut générer une image ailleurs. La valeur : qu'un conservateur qui ne sait pas prompter obtienne, en trois champs, une image raccord avec les quarante autres du même parcours. Une VisualIdentity par instance (style verrouillé, époque, palette, exclusions, 1-5 images de référence) + surcharge optionnelle par configuration — le parcours Halloween a sa propre identité, exactement comme Configuration porte déjà ses PrimaryColor/Languages. Injectée côté serveur dans chaque prompt, jamais retapée. Boutons « Générer » dans l'éditeur de contenu, pas dans un playground ; gabarits métier, prompt libre en mode avancé seulement.

@@ -1101,6 +1102,7 @@
3 reposStudio IAV2 · remplace le talking head

Personnages — un objet, quatre facettes

+

🛠️ Lot 7 en cours (15/09). Entité Persona, écran Personnages, TtsVoice en base, migration des colonnes Instance.Guide* faits. Figures des 12 archétypes générées et embarquées. Reste : l'écoute des voix (échantillons audio à produire, pas de clé Gemini en local), puis le lot 8 (narration attribuée).

Trois plans décrivaient le même objet sans se croiser (relevé le 01-02/09) : le canon visuel (studio-plan.md), les 3 frames de lipsync (talking-head-plan.md) et PersonaConfig { WakewordId, GuideName, PersonaPrompt, VoiceName } (tts-pregenerated-plan.md). Une entité Persona les remplace : identité, visage (canon en jeu de vues), voix + intonation, parole.

Le talking head est abandonné, sa dépendance était déjà cassée. talking-head-plan.md:11 anime ses frames « selon les timestamps retournés par Google Cloud TTS » (enable_time_pointing) — or le code livré tourne sur Gemini TTS (GeminiTtsEngine, gemini-2.5-flash-preview-tts) : aucune source de timestamps. Remplacé par le portrait canon statique à côté du lecteur audio, puis une boucle vidéo 6-8 s si du mouvement est voulu. En-tête d'abandon posé sur le fichier.

⚠️ « Le visiteur choisit entre Viva et Marco » était un choix de voix déguisé en choix de guide. Le besoin réel est celui du Bastogne War Museum : 3-4 personnages qui narrent chacun leurs stations, avec leur voix et leur intonation. Le conservateur assigne (NarratorPersonaId sur GuidedStep/GeoPoint/SectionArticle), le visiteur ne choisit rien. Bonne nouvelle : ça divise le stockage TTS par deux au lieu de le multiplier — chaque contenu n'existe plus qu'en une voix.

diff --git a/kanban/cards/5-planifie/280-module-studio-generation-d-images-sous-identi.md b/kanban/cards/5-planifie/280-module-studio-generation-d-images-sous-identi.md index a286d8a..20480ef 100644 --- a/kanban/cards/5-planifie/280-module-studio-generation-d-images-sous-identi.md +++ b/kanban/cards/5-planifie/280-module-studio-generation-d-images-sous-identi.md @@ -3,9 +3,10 @@ title: Module Studio — génération d'images sous identité visuelle area: backend manager horizon: v2 tags: Studio IA -flag: good | V2 · lots 0-4 codés le 13/09, gate à passer +flag: good | V2 · lots 0-6 codés, calibrage fait le 15/09, gate conservateur à passer src: v2/studio-plan.md — §8 (exécutable lots 0-4), §9 (catalogue v0), décisions 22-23 --- +

15/09 : lots 5 et 6 codés, calibrage §9.4 fait. Mention IA dans le fichier (XMP IPTC) + gravure en option. Calibrage sur 61 générations (Fourneau en gravure, Fort en aquarelle) : trois défauts corrigés avant tout client — négatif en double négation, titre de fiche recopié en légende, date d'un objet effacée. Coût réel fal : 0,03 $ l'image + 0,015 $ par référence ; unité arrêtée : 1 crédit = 0,015 $ de coût, vendu avec marge (≈ 0,05 €). Planches dans outputs/studio-calibrage-2026-09-15/. Reste : jugement visuel de Thomas, test du conservateur (gate lot 4), push de la branche.

🛠️ Lots 0 à 4 codés le 13/09 sur la branche studio (manager-service, manager-app, mymuseum-visitapp) : ingestion serveur, crédits, identité visuelle (menu Studio), génération fal.ai derrière IGenerationProvider (fournisseur interchangeable), onglet Générer dans le sélecteur de ressource, image d'étape rattachée à une ressource. Reste avant le gate : bucket de dev + CORS, clé fal.ai (Studio__Fal__Key), migrations appliquées, calibrage et prix (§9.4), puis le test du conservateur.

Plan consolidé le 13/09. Le §8 donne, pour les lots 0 à 4, les prérequis, les étapes, les vérifications et le gate. Le §9 contient un catalogue v0 (8 styles, règle de préambule, 3 gabarits) à calibrer en fin de lot 3. Six décisions ont été prises : upload par URL signée + ingestion serveur (bucket fermé aux clients, gros fichiers hors VPS), MVP limité à « depuis le texte » et « depuis une image » (objets et lieux), prix différés avec Grant manuel, une recharge qui prolonge tout le solde, plafonds d'upload prudents, catalogue calibré au lot 3. Le webhook fal.ai est signé en ED25519, pas en HMAC.

Le produit n'est pas l'accès aux modèles, c'est la cohérence. N'importe qui peut générer une image ailleurs. La valeur : qu'un conservateur qui ne sait pas prompter obtienne, en trois champs, une image raccord avec les quarante autres du même parcours. Une VisualIdentity par instance (style verrouillé, époque, palette, exclusions, 1-5 images de référence) + surcharge optionnelle par configuration — le parcours Halloween a sa propre identité, exactement comme Configuration porte déjà ses PrimaryColor/Languages. Injectée côté serveur dans chaque prompt, jamais retapée. Boutons « Générer » dans l'éditeur de contenu, pas dans un playground ; gabarits métier, prompt libre en mode avancé seulement.

diff --git a/kanban/cards/5-planifie/310-personnages-un-guide-adressable-n-narrateurs.md b/kanban/cards/5-planifie/310-personnages-un-guide-adressable-n-narrateurs.md index 7518d3a..3f8e853 100644 --- a/kanban/cards/5-planifie/310-personnages-un-guide-adressable-n-narrateurs.md +++ b/kanban/cards/5-planifie/310-personnages-un-guide-adressable-n-narrateurs.md @@ -6,6 +6,7 @@ tags: 3 repos, Studio IA flag: warn | V2 · remplace le talking head src: v2/studio-plan.md §3.8 — lots 7 et 8 --- +

🛠️ Lot 7 en cours (15/09). Entité Persona, écran Personnages, TtsVoice en base, migration des colonnes Instance.Guide* faits. Figures des 12 archétypes générées et embarquées. Reste : l'écoute des voix (échantillons audio à produire, pas de clé Gemini en local), puis le lot 8 (narration attribuée).

Trois plans décrivaient le même objet sans se croiser (relevé le 01-02/09) : le canon visuel (studio-plan.md), les 3 frames de lipsync (talking-head-plan.md) et PersonaConfig { WakewordId, GuideName, PersonaPrompt, VoiceName } (tts-pregenerated-plan.md). Une entité Persona les remplace : identité, visage (canon en jeu de vues), voix + intonation, parole.

Le talking head est abandonné, sa dépendance était déjà cassée. talking-head-plan.md:11 anime ses frames « selon les timestamps retournés par Google Cloud TTS » (enable_time_pointing) — or le code livré tourne sur Gemini TTS (GeminiTtsEngine, gemini-2.5-flash-preview-tts) : aucune source de timestamps. Remplacé par le portrait canon statique à côté du lecteur audio, puis une boucle vidéo 6-8 s si du mouvement est voulu. En-tête d'abandon posé sur le fichier.

⚠️ « Le visiteur choisit entre Viva et Marco » était un choix de voix déguisé en choix de guide. Le besoin réel est celui du Bastogne War Museum : 3-4 personnages qui narrent chacun leurs stations, avec leur voix et leur intonation. Le conservateur assigne (NarratorPersonaId sur GuidedStep/GeoPoint/SectionArticle), le visiteur ne choisit rien. Bonne nouvelle : ça divise le stockage TTS par deux au lieu de le multiplier — chaque contenu n'existe plus qu'en une voix.

diff --git a/outputs/studio-calibrage-2026-09-15/A-coherence.jpg b/outputs/studio-calibrage-2026-09-15/A-coherence.jpg new file mode 100644 index 0000000..0deb39f Binary files /dev/null and b/outputs/studio-calibrage-2026-09-15/A-coherence.jpg differ diff --git a/outputs/studio-calibrage-2026-09-15/A-refs-0-3-5.jpg b/outputs/studio-calibrage-2026-09-15/A-refs-0-3-5.jpg new file mode 100644 index 0000000..ba429bd Binary files /dev/null and b/outputs/studio-calibrage-2026-09-15/A-refs-0-3-5.jpg differ diff --git a/outputs/studio-calibrage-2026-09-15/B-coherence.jpg b/outputs/studio-calibrage-2026-09-15/B-coherence.jpg new file mode 100644 index 0000000..d3924ae Binary files /dev/null and b/outputs/studio-calibrage-2026-09-15/B-coherence.jpg differ diff --git a/outputs/studio-calibrage-2026-09-15/C-coherence.jpg b/outputs/studio-calibrage-2026-09-15/C-coherence.jpg new file mode 100644 index 0000000..b810ea2 Binary files /dev/null and b/outputs/studio-calibrage-2026-09-15/C-coherence.jpg differ diff --git a/outputs/studio-calibrage-2026-09-15/archetypes.jpg b/outputs/studio-calibrage-2026-09-15/archetypes.jpg new file mode 100644 index 0000000..626a26a Binary files /dev/null and b/outputs/studio-calibrage-2026-09-15/archetypes.jpg differ diff --git a/outputs/studio-calibrage-2026-09-15/samples.jpg b/outputs/studio-calibrage-2026-09-15/samples.jpg new file mode 100644 index 0000000..96e79fc Binary files /dev/null and b/outputs/studio-calibrage-2026-09-15/samples.jpg differ diff --git a/outputs/studio-calibrage-2026-09-15/source.jpg b/outputs/studio-calibrage-2026-09-15/source.jpg new file mode 100644 index 0000000..aac632f Binary files /dev/null and b/outputs/studio-calibrage-2026-09-15/source.jpg differ diff --git a/outputs/studio-calibrage-2026-09-15/v2-A-negatif.jpg b/outputs/studio-calibrage-2026-09-15/v2-A-negatif.jpg new file mode 100644 index 0000000..b7d5cce Binary files /dev/null and b/outputs/studio-calibrage-2026-09-15/v2-A-negatif.jpg differ diff --git a/outputs/studio-calibrage-2026-09-15/v2-refs3-source.jpg b/outputs/studio-calibrage-2026-09-15/v2-refs3-source.jpg new file mode 100644 index 0000000..9fb5ea2 Binary files /dev/null and b/outputs/studio-calibrage-2026-09-15/v2-refs3-source.jpg differ diff --git a/outputs/studio-calibrage-2026-09-15/v3-contexte.jpg b/outputs/studio-calibrage-2026-09-15/v3-contexte.jpg new file mode 100644 index 0000000..523733d Binary files /dev/null and b/outputs/studio-calibrage-2026-09-15/v3-contexte.jpg differ diff --git a/status/roadmap-canaux.md b/status/roadmap-canaux.md index d4014ac..ce3b73a 100644 --- a/status/roadmap-canaux.md +++ b/status/roadmap-canaux.md @@ -19,8 +19,8 @@ Plans détaillés, pas encore démarrés en dev (sauf mention contraire) : | TTS pré-généré (audioguide multilingue) | [v2/tts-pregenerated-plan.md](../v2/tts-pregenerated-plan.md) | Priorité 3. ⚠️ **Trois corrections posées le 2026-09-02** (en-tête du fichier) : le moteur est **Gemini TTS**, pas Cloud TTS — le document disait les deux, le code tranche ; « le visiteur choisit entre Viva et Marco » est remplacé par **un guide adressable + N narrateurs** ; et le stockage s'en trouve **divisé par deux**, pas multiplié. Source de vérité sur les personnages : [v2/studio-plan.md §3.8](../v2/studio-plan.md) | | ~~Talking head (avatar animé)~~ | [v2/talking-head-plan.md](../v2/talking-head-plan.md) | ⛔ **ABANDONNÉ le 2026-09-02.** Son lipsync repose sur `enable_time_pointing` de **Google Cloud TTS**, or le code livré tourne sur **Gemini TTS** (`GeminiTtsEngine`, `gemini-2.5-flash-preview-tts`) : aucune source de timestamps. Le plan était sans fondation, pas « à faire ». Remplacé par le portrait canon statique d'un `Persona`, puis une boucle vidéo 6-8 s. En-tête d'abandon posé sur le fichier | | **Médiathèque — refonte de l'onglet Ressources** | ⭐ **[v1-mediatheque-plan.md](../v1-mediatheque-plan.md)** — spec autonome et exécutable, écrite le 2026-09-02 | ⭐ **V1 — prêt à implémenter, sans rien lire du plan Studio.** Rien à voir avec l'IA : renommage en Médiathèque, rail de facettes cumulables (type/usage/origine/configuration), filtre **« jamais utilisée »**, tri, groupement, sélection multiple, et le détail en **panneau latéral** avec la liste « Utilisée dans » — presque gratuite, `GetReferencedResourceIds` existe déjà sur les 13 sous-types. 🐛 Corrige deux bugs : le téléchargement forcé en `.json` quel que soit le type (`show_resource_popup.dart:91`) et `Resource.FileName` absent de `ToDTO()`. **Aucune clé API, aucun coût variable — meilleur ratio valeur/risque du chantier** | -| **Module Studio — génération IA d'images/vidéos/3D** | [v2/studio-plan.md](../v2/studio-plan.md) | 📦 **V2 — conception arrêtée le 2026-09-02, rien de codé.** Le produit n'est pas l'accès aux modèles, c'est la **cohérence visuelle à l'échelle d'un projet** : une `VisualIdentity` par instance + surcharge par configuration, injectée côté serveur dans chaque prompt. La génération se greffe sur le **sélecteur de ressource existant** (`showSelectResourceModal` embarque déjà `ResourcesScreen`) — un seul branchement couvre les 13 types de section, les POI et les étapes. MVP = escape game, images seulement. ⛔ **Prérequis dur : le backend ne sait pas écrire dans le bucket** (`IResourceBlobService` n'a que `DeleteAsync`) — lot 0 partagé avec le TTS pré-généré. ⚠️ Trois pièges tranchés : URL Firebase à jeton **publique** (préfixe `studio-drafts/` + proxy authentifié), `CheckQuota` avant/après **ne tient pas** sur des jobs Hangfire parallèles (réservation en deux temps sur `CreditLedger`), et `GuidedStep.ImageUrl` est une URL — les images de l'escape game **ne partiraient pas offline**. 🚫 Pas de watermark gravé : provenance + badge UI | -| **Personnages — un guide adressable + N narrateurs** | [v2/studio-plan.md §3.8](../v2/studio-plan.md) | 📦 **V2 — arrêté le 2026-09-02.** **Trois plans décrivaient le même objet sans se croiser** : le canon visuel (studio-plan), les frames de lipsync (talking-head), et `PersonaConfig { WakewordId, GuideName, PersonaPrompt, VoiceName }` (tts-pregenerated). Une entité `Persona` les remplace, et les 4 colonnes `Instance.Guide*` deviennent une ligne. ⚠️ « Le visiteur choisit entre Viva et Marco » était **un choix de voix déguisé en choix de guide** : le besoin réel (modèle Bastogne War Museum) est N narrateurs assignés par le conservateur via `NarratorPersonaId` — ce qui **divise le stockage TTS par deux** au lieu de le multiplier. Le **wakeword devient une exigence du canal mains-libres**, pas une propriété du personnage : ça corrige le bug latent où le visiteur dirait « Marco » à quelqu'un qui se présente comme « Léon ». À ajouter : `Persona.VoicePrompt` (l'intonation, aujourd'hui la **constante de build** `kGeminiTtsPrompt`) et une table `TtsVoice` — deux constantes ne suffisent pas à quatre narrateurs | +| **Module Studio — génération IA d'images/vidéos/3D** | [v2/studio-plan.md](../v2/studio-plan.md) | 🛠️ **V2 — lots 0 à 6 codés, calibrage fait le 2026-09-15** (voir studio-plan §9.4 : négatif, contexte, coûts réels, unité de crédit). Conception du 2026-09-02 : Le produit n'est pas l'accès aux modèles, c'est la **cohérence visuelle à l'échelle d'un projet** : une `VisualIdentity` par instance + surcharge par configuration, injectée côté serveur dans chaque prompt. La génération se greffe sur le **sélecteur de ressource existant** (`showSelectResourceModal` embarque déjà `ResourcesScreen`) — un seul branchement couvre les 13 types de section, les POI et les étapes. MVP = escape game, images seulement. ✅ Prérequis bucket levé (lot 0 : URL signée + ingestion serveur). ⚠️ Trois pièges tranchés : URL Firebase à jeton **publique** (préfixe `studio-drafts/` + proxy authentifié), `CheckQuota` avant/après **ne tient pas** sur des jobs Hangfire parallèles (réservation en deux temps sur `CreditLedger`), et `GuidedStep.ImageUrl` est une URL — les images de l'escape game **ne partiraient pas offline**. 🚫 Pas de watermark gravé par défaut : provenance + badge UI + XMP IPTC dans le fichier, gravure en option par instance (lot 6) | +| **Personnages — un guide adressable + N narrateurs** | [v2/studio-plan.md §3.8](../v2/studio-plan.md) | 🛠️ **V2 — lot 7 en cours** (entité `Persona`, écran Personnages, `TtsVoice` en base, figures d'archétypes). Arrêté le 2026-09-02 : **Trois plans décrivaient le même objet sans se croiser** : le canon visuel (studio-plan), les frames de lipsync (talking-head), et `PersonaConfig { WakewordId, GuideName, PersonaPrompt, VoiceName }` (tts-pregenerated). Une entité `Persona` les remplace, et les 4 colonnes `Instance.Guide*` deviennent une ligne. ⚠️ « Le visiteur choisit entre Viva et Marco » était **un choix de voix déguisé en choix de guide** : le besoin réel (modèle Bastogne War Museum) est N narrateurs assignés par le conservateur via `NarratorPersonaId` — ce qui **divise le stockage TTS par deux** au lieu de le multiplier. Le **wakeword devient une exigence du canal mains-libres**, pas une propriété du personnage : ça corrige le bug latent où le visiteur dirait « Marco » à quelqu'un qui se présente comme « Léon ». À ajouter : `Persona.VoicePrompt` (l'intonation, aujourd'hui la **constante de build** `kGeminiTtsPrompt`) et une table `TtsVoice` — deux constantes ne suffisent pas à quatre narrateurs | | AI Persona par venue (guide IA personnalisable) | [v2/myinfomate-ai-persona-analysis.md](../v2/myinfomate-ai-persona-analysis.md) | Architecture de référence pour les chantiers IA ci-dessus | | Lunettes Ray-Ban Meta | [roadmap.md](../roadmap.md) (section XR), [rayban-meta-integration.md](../rayban-meta-integration.md) | 🔨 **POC fonctionnel — bien plus avancé que ce que cette ligne disait jusqu'au 2026-08-09.** Voir §5bis | | VR Meta Quest (borne office tourisme) | [v2/vr-quest-unity-plan.md](../v2/vr-quest-unity-plan.md) · [roadmap.md](../roadmap.md) (section XR) | 📦 **V2** — ⚠️ **« zéro ligne de code » était faux** (corrigé le 2026-08-31, relevé dans le code). Le canal VR est déjà dans le modèle : `AppType.VR`, `Instance.IsVR` en base depuis juillet 2025, sous-menu « VR » branché dans `main_screen.dart:65`, filtre stats + i18n `statsChannelVR` prêts. **Ce qui manque** : l'écran de flotte (`main_screen.dart:704` = `Text("TODO vr")`), un `Device.AppType` (le `DeviceController` est hardcodé sur `Tablet`), et l'app Unity. Back-office XR : **8-12 j**. App Unity : **3-4 mois**. La landing annonce « Meta Quest — Bientôt » en 4 langues (`translations.ts`, clé `mode4Desc`) | diff --git a/v2/studio-plan.md b/v2/studio-plan.md index 98dd82e..2ba1d17 100644 --- a/v2/studio-plan.md +++ b/v2/studio-plan.md @@ -159,7 +159,9 @@ Tout sur `Instance`, dupliqué depuis `SubscriptionPlan` : `StorageQuotaBytes`, | 20 | Lignage | **`Resource.Origin`** (`Uploaded` \| `Generated` \| `Derived`) et **`Resource.SourceResourceId`** en colonnes typées ; `AiProvenance` jsonb garde le détail. Reporte la décision n°4 d'`immersif-frontiere-plan` §5. Posé au lot 0, puisque l'ingestion l'écrit | | 21 | Contrat fournisseur | **`ProviderResult` multi-fichier**, délais et relances **par `GenerationKind`**. Le job composite (parent + N enfants) est réservé à la 3D. Reporte la décision n°6 d'`immersif-frontiere-plan` §5 | | 22 | Plafond dur et dotation de plan — *13/09, lot 1* | **Retirés.** Avec des crédits prépayés (décision 17), le solde *est* le plafond : un « plafond dur organisation » n'a plus rien à borner. Seul reste `StudioPerUserDailyCap`, en crédits. `SubscriptionPlan.HasStudio` / `StudioCreditsGranted` et `Instance.StudioProviderRegion` ne sont pas créés tant que rien ne les lit (prix différés, décision 16 ; région déclarative) | -| 23 | Écarts des lots 2 à 4 — *13/09* | **Fournisseur interchangeable** : `IGenerationProvider` (soumission, sondage, lecture du webhook) + `GenerationModel.ProviderKey` en base ; changer de fournisseur = une classe et une ligne de catalogue. **Une requête par variante** (FLUX.2 pro n'a pas de `num_images`). **Référence source avant celles de l'identité** dans l'ordre d'envoi. Migration unique `AddStudioGeneration` pour les lots 2 et 3. **Non livrés** : `preview`, `GET /jobs`, `cancel`, `reject`, `regenerate`, l'interrupteur UI de `CanValidateAssets`. **Pas de `target` serveur** : l'onglet « Générer » vit dans le sélecteur (`showSelectResourceModal`, mode sélection) et la ressource validée revient au champ qui l'a ouvert — donc absent du bouton « Ajouter » de la Médiathèque, qui relève du lot 5. Surcharges d'identité en puces, pas en onglets ; `ModelKey` non exposé (un seul modèle). **`GuidedStep.ImageUrl` conservée** et remplie par le client avec `ImageResourceId` (motif `GeoPoint`) : visitapp-web n'a rien à changer, mymuseum-visitapp lit l'id pour l'hors-ligne, tablet-app n'a pas d'étapes. `CreditCost = 10` provisoire jusqu'au calibrage | +| 23 | Écarts des lots 2 à 4 — *13/09* | **Fournisseur interchangeable** : `IGenerationProvider` (soumission, sondage, lecture du webhook) + `GenerationModel.ProviderKey` en base ; changer de fournisseur = une classe et une ligne de catalogue. **Une requête par variante** (FLUX.2 pro n'a pas de `num_images`). **Référence source avant celles de l'identité** dans l'ordre d'envoi. Migration unique `AddStudioGeneration` pour les lots 2 et 3. **Non livrés** : `preview`, `GET /jobs`, `cancel`, `reject`, `regenerate`, l'interrupteur UI de `CanValidateAssets`. **Pas de `target` serveur** : l'onglet « Générer » vit dans le sélecteur (`showSelectResourceModal`, mode sélection) et la ressource validée revient au champ qui l'a ouvert — donc absent du bouton « Ajouter » de la Médiathèque, qui relève du lot 5. Surcharges d'identité en puces, pas en onglets ; `ModelKey` non exposé (un seul modèle). **`GuidedStep.ImageUrl` conservée** et remplie par le client avec `ImageResourceId` (motif `GeoPoint`) : visitapp-web n'a rien à changer, mymuseum-visitapp lit l'id pour l'hors-ligne, tablet-app n'a pas d'étapes. `CreditCost = 10` provisoire jusqu'au calibrage || 24 | Modèles 3D et multi-vues — *15/09* | **Tout reste sur fal** : Meshy (v5, v6-preview, v7), Hi3D, Tripo3D, Hunyuan3D et Trellis y sont exposés — aucun second fournisseur à intégrer, aucune seconde clé. **Deux lignes de catalogue, pas une** : `3d-default` mono-image (~0,02–0,05 $) et `3d-multiview` = `meshy/v7/multi-image-to-3d` (~1,20 $). Le facteur ~25 sur le coût impose que le basculement de l'un à l'autre soit **annoncé avant la génération, jamais silencieux**. Hi3D n'expose pas de multiview : excellent en fidélité structurelle, il ne couvre pas le cas d'usage central. Ids et prix à revérifier au calibrage (§9.4) | +| 25 | Casting visiteur — *15/09* | **Retenu** — lève le « hors périmètre » de §3.8. `ConfigurationPersona { ConfigurationId, PersonaId, Order, IsFeatured }` ne porte **que** l'ordre et la mise en avant : l'appartenance reste **calculée** depuis les narrateurs assignés. Un personnage ne s'assigne qu'à **un seul endroit — le contenu** ; l'écran casting est une *vue*, jamais une seconde saisie. Rien ne s'affiche côté visiteur sans `Configuration.IsCastingShownToVisitor` (défaut off). Lot 8bis | + --- @@ -673,12 +675,13 @@ Cibles au moment de la rédaction, **à ne jamais écrire dans le code** : | Rôle | Modèle | Note | |---|---|---| -| Image par défaut | FLUX.2 [pro] | jusqu'à 10 images de référence — **chiffre non retrouvé sur la page API fal le 13/09, à confirmer au calibrage** (§9.4) ; bonne préservation de style | +| Image par défaut | FLUX.2 [pro] | jusqu'à 10 images de référence — **mesuré le 15/09 : 422 « At most 10 image URLs are allowed » au-delà** ; bonne préservation de style | | Personnages et scènes narratives | Nano Banana Pro / Gemini 3 Pro Image | jusqu'à 14 entrées de référence | | Texte lisible / vectoriel | Recraft V3 | | | Vidéo par défaut | Kling 3.0 | clips 6-8 s, pensés pour la boucle | | Vidéo avec audio natif | Veo 3.1 | | -| 3D | Meshy 6 ou Tripo | objets isolés uniquement, export GLB | +| 3D — 1 image | Hi3D (`hitem3d/hi3d/image-to-3d`) ou Tripo3D | objets isolés, export GLB, ~0,02–0,05 $ | +| 3D — plusieurs vues | **Meshy v7** (`meshy/v7/multi-image-to-3d`) | 2 à 4 photos du **même** objet, ~1,20 $ (1,40 $ en ultra) — voir décision 24 et lot 11 | `StudioProviderRegion` sur l'instance est déclaratif aujourd'hui ; il devient un critère de sélection de provider le jour où une région alternative existe. @@ -934,11 +937,48 @@ donc **deux canons qui divergent**, exactement ce que `Persona` existe pour emp dans l'escape game » reste **calculé**, jamais déclaré : un champ déclaré peut mentir, un compte d'usages non. -**Un seul cas justifierait un vrai lien configuration ↔ personnage** : présenter le **casting** au -visiteur (« les 4 témoins que vous allez rencontrer »), qui demande un ordre et un sous-ensemble -éditorial qu'un ensemble calculé ne porte pas. Table optionnelle -`ConfigurationPersona { ConfigurationId, PersonaId, Order, IsFeatured }`, remplie seulement si cet -écran existe. **Hors périmètre tant que la décision produit n'est pas prise.** +#### Le casting — décidé le 2026-09-15 (décision 25) + +Un seul cas justifiait un vrai lien configuration ↔ personnage : présenter le **casting** au visiteur +(« les 4 témoins que vous allez rencontrer »), qui demande un ordre et un sous-ensemble éditorial +qu'un ensemble calculé ne porte pas. **Il est retenu** — c'est aussi le premier usage visiteur réel +du canon visuel, qui ne servait jusqu'ici qu'à la cohérence des images générées. + +> **La règle qui rend l'écran simple :** *on assigne un personnage à un seul endroit — le contenu.* +> Le casting n'est pas une seconde saisie, c'est une **vue** de ce qui est déjà calculé, plus deux +> champs éditoriaux : l'ordre et la mise en avant. Deux endroits où déclarer la même chose, ce sont +> deux vérités qui divergent — le même piège que les deux canons évités plus haut. + +**Écran `Configuration › Personnages`.** Une ligne par personnage réellement utilisé : portrait canon, +nom, rôle, **d'où vient l'usage** (« 6 étapes du parcours principal », cliquable). Deux contrôles, +pas trois : une case « montré au visiteur » et une poignée de réordonnancement. **Aucun sélecteur de +personnage sur cet écran** : pour en ajouter un, on lui assigne un contenu ; pour en retirer un, on +change le narrateur de ses contenus — et le lien pour y aller est sur la ligne. + +**Les trois états — c'est là que se joue la clarté**, parce que la majorité des clients n'auront +jamais qu'un seul personnage et ne doivent pas voir un écran de casting vide : + +| Situation | Ce que montre l'écran | Ce que le client a à faire | +|---|---|---| +| Aucun narrateur assigné | Bandeau : « **Léon** raconte toute cette visite » + *(guide de l'instance)* + lien vers sa fiche | **rien** — cas nominal | +| Un seul personnage | Le même bandeau, pas de liste | rien ; l'interrupteur casting est masqué, pas grisé | +| Deux personnages ou plus | La liste + l'interrupteur **« Présenter les personnages au visiteur »** (`IsCastingShownToVisitor`, **défaut off**) | cocher, ordonner | + +Quatre règles, dans la ligne des quatre exigences de l'onglet « Narrateurs » : + +1. **Hérité et choisi se distinguent à l'œil**, ici aussi : `↳ Le gouverneur` en gris quand il n'est + là que comme repli du parcours, **La sentinelle** en gras quand un contenu le désigne. +2. **Rien n'apparaît côté visiteur sans un geste explicite.** Un client qui assigne un deuxième + narrateur ne doit pas découvrir un nouvel écran dans son app sans l'avoir demandé. +3. **Un personnage sans portrait canon** s'affiche avec un gabarit neutre et un avertissement, et ne + peut pas être « mis en avant » : un casting à trous est pire que pas de casting. +4. **L'ordre par défaut est celui d'apparition dans la visite**, pas l'ordre de création : c'est ce + que le visiteur va vivre, et ça évite au client d'avoir à réordonner quoi que ce soit dans 90 % + des cas. + +Table `ConfigurationPersona { ConfigurationId, PersonaId, Order, IsFeatured }` — **Order et +IsFeatured seulement.** L'appartenance reste calculée : un champ déclaré peut mentir, un compte +d'usages non. **Règle du wakeword — c'est une exigence du canal, pas une propriété du personnage.** @@ -1193,6 +1233,9 @@ POST /api/Studio/personas/{id}/canon { assetId } → fige le port POST /api/Studio/personas/{id}/views { kind } → job (profil, plan large, dos) DELETE /api/Studio/personas/{id}/views/{viewId} POST /api/Studio/personas/{id}/archive + +GET /api/Studio/configurations/{id}/casting → liste calculée + Order/IsFeatured +PUT /api/Studio/configurations/{id}/casting { isShownToVisitor, entries[{personaId, order, isFeatured}] } ``` ### Crédits @@ -1267,6 +1310,7 @@ Migrations additives, **une par lot** (le §8 dit laquelle), aucune donnée exis | `Personas` | `InstanceId`, index `(InstanceId, IsArchived)` — **pas de `Kind`** (décision 7) ; remplace les colonnes `Guide*` de `Instance` | | `PersonaViews` | `PersonaId` FK cascade, `ResourceId` FK | | `TtsVoices` | `Key` unique — catalogue, plus deux constantes | +| `ConfigurationPersonas` | `ConfigurationId` FK, `PersonaId` FK, index unique `(ConfigurationId, PersonaId)` — **ordre et mise en avant seulement**, l'appartenance reste calculée (décision 25, lot 8bis) | | `BeforeAfterPairs` | `AfterResourceId`, `BeforeResourceId` | | `CreditLedgerEntries` | `InstanceId`, `UserId`, `GenerationJobId?`, index `(InstanceId, CreatedAt)` | @@ -1276,6 +1320,7 @@ Migrations additives, **une par lot** (le §8 dit laquelle), aucune donnée exis Resource += Origin int not null default 0 -- Uploaded=0 | Generated=1 | Derived=2 (lot 0) SourceResourceId? -- lignage parent (lot 0) AiProvenance jsonb? -- détail (lot 3) +Configuration += IsCastingShownToVisitor bool not null default false -- lot 8bis Instance += StudioEnabled, StudioCreditsBalance, StudioCreditsExpireAt, StudioPerUserDailyCap, -- lot 1, livré IsAiWatermarkBurned -- lot 6 @@ -1395,11 +1440,18 @@ des `Resource` et n'ont donc rien à faire dans la bibliothèque. En usage norma `AiProvenance`, descente jusqu'aux 3 clients visiteurs, badge traduit, `rightsHolder`, option de gravure. +> **Fait le 2026-09-15.** À la validation, `AiImageMarker` écrit un XMP IPTC `DigitalSourceType = trainedAlgorithmicMedia` +> + `dc:rights` = `rightsHolder` (survit au ré-encodage de l'ingestion, testé). Gravure : `Instance.IsAiWatermarkBurned` +> (interrupteur SuperAdmin dans le dialogue d'instance), texte fixe « Généré par IA · AI-generated » en bas à droite, +> police DejaVu Sans embarquée. ⚠️ `IsImageWatermark` (filigrane du lieu) reste mort — carte 305, rien partagé. + ### Lot 7 — Personnages (fusion guide + figures) Registre de personnages, grille d'archétypes neutres (une douzaine à pré-produire), paramètres, canon en **jeu de vues**, jauge de budget de références, réinjection en référence n°1, archivage. +> **État au 2026-09-15.** Fait : `Persona`, écran Personnages, `TtsVoice` en base, migration `AddPersonas`. Fichiers pré-produits le 15/09 et **embarqués dans manager-app** (`assets/images/studio/`) plutôt que déposés au bucket : une URL de bucket différerait entre dev et prod, alors qu'un asset marche partout — `VisualStyle.SampleImageUrl` prime s'il est rempli. 8 échantillons de style (un même moulin dans les 8 styles) et 12 figures d'archétypes (mannequin neutre au trait), ≈ 0,60 $. **Reste** : l'écoute des voix — 6 extraits audio `TtsVoice` à générer en Gemini TTS, bloqué sur la clé (absente en local). + **Migration structurante** : les 4 colonnes `Instance.Guide*` deviennent une ligne `Persona` à facette parole remplie (pas de `Kind`, décision 7), et `Instance` porte un `GuidePersonaId`. Le catalogue `TtsVoice` passe @@ -1416,6 +1468,18 @@ Le lot « Bastogne » : plusieurs personnages qui racontent chacun leurs station coût de régénération par ligne, rien de régénéré avant validation. - Côté visiteur : portrait canon statique à côté du lecteur audio. +### Lot 8bis — Casting visiteur *(petit, juste après le lot 8)* + +N'a de sens qu'une fois plusieurs narrateurs assignables, donc après le lot 8 — et il est court. + +- `ConfigurationPersona { ConfigurationId, PersonaId, Order, IsFeatured }` et + `Configuration.IsCastingShownToVisitor` (défaut `false`). +- Écran `Configuration › Personnages` : liste **calculée** depuis les narrateurs assignés, trois + états (0 / 1 / n personnages), aucun sélecteur de personnage — voir §3.8. +- Côté visiteur : écran d'entrée « les personnes que vous allez rencontrer », portrait canon + nom + + rôle. Trois fronts. **Ne s'affiche que si l'interrupteur est armé et si ≥ 2 personnages ont un + portrait.** + ### Lot 9 — Avant / après Gabarit image-to-image + composant slider ×3 clients. @@ -1426,10 +1490,54 @@ Kling 3.0 / Veo 3.1, clips 6-8 s en boucle, canon d'un personnage comme image so stockage sérieux : à cadrer côté quota avant d'ouvrir. C'est ici que revient l'idée d'un portrait animé, sous une forme qui tient en 2026. +> **Non engagé — arbitré le 2026-09-15.** Faire *parler* un personnage reste souhaitable, mais le +> blocage est le **stockage**, pas la techno (le lipsync, lui, est mort pour une autre raison : +> décision 9). Tant que l'enveloppe de stockage n'est pas chiffrée, ce lot reste après tout le +> reste — V2 tardive, voire V3. Le portrait canon statique du lot 8 est la réponse en attendant. + ### Lot 11 — 3D -Meshy 6 / Tripo, objets isolés, export GLB, `ResourceType.Model3D` (déjà dans l'enum, valeur 13). Suppose un viewer GLB côté -visiteur — dépend du chantier VR/XR (`vr-quest-unity-plan.md`). +Objets isolés, export GLB, `ResourceType.Model3D` (déjà dans l'enum, valeur 13). Suppose un viewer +GLB côté visiteur — dépend du chantier VR/XR (`vr-quest-unity-plan.md`). + +**Deux modèles au catalogue, pas un** (décision 24) : `3d-default` mono-image et `3d-multiview` +(`meshy/v7/multi-image-to-3d`). Tous deux sur fal, donc zéro intégration supplémentaire. + +#### Cas d'usage nommé — « l'objet de la vitrine, en 3D » + +C'est le cœur du lot, et il ne se résume pas à un appel d'API : **la qualité du résultat dépend +davantage des photos fournies que du prompt.** C'est le seul endroit du Studio où c'est vrai, et +l'écran doit être construit autour de ça. + +1. Le conservateur ouvre le sélecteur de ressource d'une `SectionModel3D`, onglet **Générer**, + gabarit « **Illustration 3D d'un objet** » — libellé volontairement distinct de « objet + numérisé » (photogrammétrie, prestation hors Studio) : les vendre sous le même mot est un + risque réputationnel chez une institution scientifique. +2. **Quatre emplacements de dépôt nommés et illustrés** — pas une zone « glissez vos images ici » : + + | | Face | Profil | Dos | 3/4 | + |---|---|---|---|---| + | | obligatoire | recommandé | recommandé | recommandé | + + Chaque case porte une silhouette d'exemple. Sous le bloc, **une seule consigne**, courte : + *même objet, même lumière, fond neutre, objet entier dans le cadre.* C'est la recommandation du + modèle, et elle pèse plus lourd que n'importe quel réglage. +3. **Le coût se met à jour en direct, et le changement de modèle est annoncé** : 1 photo → modèle + mono-image ; dès la 2ᵉ → Meshy v7 multi, **~25× plus cher**. Une ligne explicite au moment où + la bascule se produit, jamais une facture découverte après coup (même principe que le coût de + régénération par ligne de l'onglet Narrateurs). +4. À l'inverse, **ne pas culpabiliser le mono-image** : une seule photo reste un usage légitime et + bon marché pour une illustration. La formulation est « plus de vues = plus fidèle », pas + « vous avez mal fait ». +5. Génération asynchrone en **job composite** (parent + enfants, décision 21), puis **normalisation + canonique du GLB** à l'ingestion — échelle, pivot, orientation (décision n°2 d' + `immersif-frontiere-plan` §5) : sans elle les POI posés sur un modèle sautent à la + régénération suivante. +6. **Provenance visible obligatoire et non désactivable** sur le `Model3D` généré (décision n°11 + du même plan) — contrairement aux images, où la gravure est optionnelle. Un visiteur qui fait + tourner « le casque de Léon » croit voir l'objet. +7. La ressource validée revient au champ qui a ouvert le sélecteur, et les **POI se posent dans + l'éditeur three.js en iframe** (décision n°9) — le vrai morceau non trivial du chantier. ⚠️ **Ce lot faisait quatre lignes et se contentait de renvoyer au plan VR, qui lui-même renvoyait ici.** La frontière est tranchée depuis le 2026-09-11 dans @@ -1685,7 +1793,7 @@ la première migration du Studio, pas en arrivant au lot 11. |---|---|---| | `illustration-painterly` | Illustration peinte | `painterly digital illustration, visible brush strokes, soft edges, rich but controlled colors` | | `ligne-claire` | Ligne claire | `ligne claire illustration, clean uniform outlines, flat colors, no gradients` | -| `engraving` | Gravure ancienne | `antique copperplate engraving, fine cross-hatching, monochrome ink on aged paper` | +| `engraving` | Gravure ancienne | `antique copperplate engraving, fine cross-hatching, black ink only on aged off-white paper, no color` | | `watercolor` | Aquarelle | `loose watercolor painting, transparent washes, visible paper texture, soft bleeding edges` | | `gouache-poster` | Affiche gouache | `mid-century gouache poster, bold simplified shapes, limited palette, subtle grain` | | `diorama` | Maquette / diorama | `handcrafted miniature diorama, scale model, tilt-shift, warm studio lighting` | @@ -1710,7 +1818,7 @@ PromptPreamble = + "Keep a consistent visual style with the reference images." -- si ReferenceResourceIds ≠ ∅ NegativeFragment = - "no text, no letters, no watermark, no signature, no logo" -- toujours + "text, letters, captions, signatures, watermarks, logos, stamps, plate numbers" -- toujours, sans « no » (§9.4) + ", {Exclusions, joined ', '}" ``` @@ -1718,14 +1826,14 @@ Puces d'exclusion prédéfinies : `anachronisms`, `modern-objects`, `gore`, `nud `real-people` (« aucune personne réelle identifiable »), et `text-in-image`, affichée cochée et verrouillée puisqu'elle est déjà dans le négatif de base. -⚠️ Toutes les variantes de FLUX.2 n'ont pas de `negative_prompt` séparé : le négatif est alors ajouté au -prompt sous la forme `Avoid: …`. À établir modèle par modèle au calibrage, et à consigner dans -`GenerationModel.Params`. +✅ Vérifié le 15/09 sur le schéma OpenAPI fal : ni `flux-2-pro` ni `flux-2-pro/edit` n'ont de `negative_prompt`. +Le négatif part dans le prompt sous la forme `Avoid: …` — d'où un négatif rédigé **sans « no »**. ### 9.3 Gabarits du MVP Contexte de fiche (§3.3, point 4), ajouté après le corps de chaque gabarit : -`"Context: {titre de la fiche}. {description en texte brut, 300 caractères max}"`, dans la langue par +`"Background knowledge, never written in the image: {titre de la fiche}. {description, 300 caractères max}"` +(« Context: » seul faisait écrire le titre en légende, §9.4), dans la langue par défaut de la configuration. **`puzzle-decor` — Décor d'énigme** · `AcceptsPersona = false` · `landscape_16_9` · 4 variantes · @@ -1793,3 +1901,29 @@ Une identité de test par style visé pour les premiers clients — au minimum ` | Coût | coût réel par image, selon le nombre de références | base de `CreditCost` (décision 16) | Sortie : les textes des §9.1-9.3 corrigés **dans ce document**, puis re-seedés. + +#### Résultats du 2026-09-15 — 61 générations, ≈ 3,13 $ estimés + +Identités de test : A gravure / Fourneau Saint-Michel (refs = planches « Forges » de l'Encyclopédie, domaine public), +B aquarelle / Fort de Saint-Héribert, C illustration peinte / Fourneau. Objets : photos Wikimedia libres. Le jugement +« à l'aveugle » est celui de Claude sur planches contact : **à refaire par Thomas** avant d'ouvrir à un client. + +| Série | Résultat | Suite | +|---|---|---| +| Cohérence | A 10/10, B 5/5, C 5/5 lus « du même projet » | ✅ | +| Références 0/3/5 | Écart net : avec refs, vraie planche monochrome ; sans, gravure *colorisée* (lueur rose, sépia). 3 → 5 refs : gain faible pour +50 % de coût. Les refs apportent aussi leurs numéros de planche et légendes | budget §3.3 tient ; 3 refs suffisent pour un style | +| Image source | 5/5 objets reconnaissables. Mais le négatif « no text » **effaçait la date 1703** d'une taque | instruction sujet complétée : garder les inscriptions de l'objet — date revenue ✅ | +| Négatif | v1 « Avoid: no text… » : ~4/10 avec légende/signature. v2 sans « no » + « captions, signatures, plate numbers » : 3/10. v3 + contexte « never written in the image » : **0/10** visible | ✅ les trois corrections sont dans le code et le seed | +| Modèle | plafond 10 refs mesuré ; pas de `negative_prompt` ; `/edit` accepte `landscape_16_9` | consigné | +| Coût | **0,03 $** l'image (16:9 comme carré : fal compte 1 MP = 1024²) **+ 0,015 $ par référence** : 0,045 $ avec 1 source · 0,075 $ 3 refs · 0,105 $ 5 refs · 0,18 $ 10 refs | voir ci-dessous | + +Coûts **recoupés sur l'export d'usage fal** du 15/09 : 44 MP `flux-2-pro` (1,32 $) + 57,5 MP `/edit` (1,725 $) = 3,05 $, +dont ≈ 0,30 $ de portraits faits à la main — le calibrage lui-même a coûté ≈ 2,75 $ (estimé 3,13 $ avec l'ancienne grille). + +**Unité arrêtée le 2026-09-15** : 1 crédit = 0,015 $ de coût fal. `CreditCost = 2` par image, +`CreditCostPerReference = 1` par référence (identité, canon, source). Ex. 3 variantes × 3 refs = 15 crédits ≈ 0,23 $. +**Prix de vente = coût × marge**, jamais le coût nu : proposition ≈ 0,05 €/crédit (×3,3), packs dégressifs +200 = 12 € · 600 = 30 € · 2 000 = 80 € (pire marge ×2,7). La marge couvre aperçus, régénérations, change USD, +Stripe, stockage et support. Prix en € à confirmer avec la recharge Stripe (après le gate du lot 4). + +Anachronismes vus malgré l'exclusion : poêle à bois moderne (A-03 ×2), vélo (refs3 v2), plafonniers (B). À surveiller.