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 22e76f5..a286d8a 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 · consolidé 13/09, lots 0-4 prêts à coder -src: v2/studio-plan.md — §8 (exécutable lots 0-4), §9 (catalogue v0) +flag: good | V2 · lots 0-4 codés le 13/09, gate à passer +src: v2/studio-plan.md — §8 (exécutable lots 0-4), §9 (catalogue v0), décisions 22-23 --- +

🛠️ 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.

Prérequis dur, carte séparée : le backend ne sait pas écrire dans le bucket. Sans le lot 0, rien du Studio ne tourne.

diff --git a/kanban/cards/5-planifie/300-socle-de-stockage-serveur-le-backend-ne-sait.md b/kanban/cards/5-planifie/300-socle-de-stockage-serveur-le-backend-ne-sait.md index 44a5089..e64cc1d 100644 --- a/kanban/cards/5-planifie/300-socle-de-stockage-serveur-le-backend-ne-sait.md +++ b/kanban/cards/5-planifie/300-socle-de-stockage-serveur-le-backend-ne-sait.md @@ -3,9 +3,10 @@ title: Socle de stockage — URL d'envoi signée et ingestion serveur area: backend manager horizon: v2 tags: manager-service, manager-app, Studio IA, sécurité -flag: critical | Prérequis dur +flag: warn | Code fait le 13/09, infra à faire src: v2/studio-plan.md — §8 lot 0, décisions 14, 18, 20 --- +

🛠️ Code livré le 13/09 (branche studio) : URL signée, ResourceIngestionService, ImageSharp, upload avec progression dans manager-app, SDK Firebase retiré. Reste l'infra ci-dessous, puis la checklist du §8.

IResourceBlobService n'expose que DeleteAsync : tout l'upload est fait par le navigateur en direct, à deux endroits de resources_screen.dart. Or le Studio et le TTS pré-généré produiront côté serveur.

⚠️ Trou de sécurité actuel, indépendant du Studio. manager-app n'a pas de Firebase Auth (firebase_storage sans firebase_auth) et écrit pourtant dans le bucket : les règles Storage sont très probablement ouvertes en écriture. Aucun storage.rules dans le repo — à relever dans la console.

Tranché le 13/09 : URL signée + ingestion. L'API délivre une URL V4 signée (15 min, un objet, taille bornée), le navigateur envoie directement chez Google dans incoming/, puis POST /ingest : un ResourceIngestionService unique — le même pour le Studio et le TTS — mesure la taille réelle, applique quota et plafond, post-traite les images (2 à la fois au plus) et range sous pictures/. Les vidéos, 360 et GLB sont copiés côté Google, jamais lus par le VPS. Les écritures clientes sont ensuite fermées dans les règles.

diff --git a/v2/studio-plan.md b/v2/studio-plan.md index 3d25dbb..98dd82e 100644 --- a/v2/studio-plan.md +++ b/v2/studio-plan.md @@ -1,6 +1,8 @@ # Module Studio — Génération IA d'images, vidéos et 3D -> **Statut** : conception arrêtée le 2026-09-02, **consolidée le 2026-09-13**. Rien de codé. +> **Statut** : conception arrêtée le 2026-09-02, **consolidée le 2026-09-13**. **Lots 0 à 4 codés le +> 2026-09-13** (branche `studio`), écarts en décision 23. Restent avant le gate : infra du bucket, +> clé fal.ai, **calibrage et prix (§9.4)**, puis le test du conservateur. > > **Pour coder, lire d'abord le [§8](#8-exécutable--lots-0-à-4)** : il rassemble, pour les lots 0 à 4, > les prérequis, les étapes, ce qui les vérifie et la checklist. Les §0-§7 restent la trace de la @@ -157,6 +159,7 @@ 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 | --- @@ -1505,6 +1508,9 @@ la première migration du Studio, pas en arrivant au lot 11. ### Lot 0 — ingestion serveur +> ✅ **Codé le 13/09** (backend et manager-app). **Reste l'infra** : bucket de dev, CORS, cycle de vie +> `incoming/`, fermeture des règles après déploiement. + **Backend** 1. `IResourceBlobService` : `CreateUploadUrl(storagePath, contentType, maxBytes)` → URL V4 signée en @@ -1572,6 +1578,8 @@ la première migration du Studio, pas en arrivant au lot 11. ### Lot 1 — crédits +> ✅ **Codé le 13/09.** Écarts : décision 22. + 1. Migration `AddStudioCredits` : colonnes `Instance` / `SubscriptionPlan` (§5), table `CreditLedgerEntries`. 2. `StudioCreditService` : `Reserve`, `Charge`, `Refund`, `Grant`, `Expire`. **`Reserve` sous verrou de ligne sur `Instance`**, dans la transaction qui écrit le ledger (§3.5). Contrôles dans l'ordre : @@ -1594,6 +1602,8 @@ la première migration du Studio, pas en arrivant au lot 11. ### Lot 2 — identité visuelle +> ✅ **Codé le 13/09** — backend et écran **Studio** du menu. Écarts : décision 23. + 1. Migration `AddVisualIdentities` + seed du catalogue de styles v0 (§9.1). 2. Service : identité de base créée vide au premier accès, non supprimable ; `Resolve(instanceId, configurationId)` ; `duplicate` = copie complète + `CopiedFromIdentityId/Version` ; `Version++` à @@ -1611,6 +1621,8 @@ la première migration du Studio, pas en arrivant au lot 11. ### Lot 3 — fournisseur, génération, calibrage +> ✅ **Codé le 13/09, sauf l'étape 7** : le calibrage et les prix attendent la clé fal.ai. Écarts : décision 23. + 1. Migration `AddGeneration` : `GenerationModels`, `GenerationTemplates`, `GenerationJobs`, `GeneratedAssets`, `Resource.AiProvenance`, `User.CanValidateAssets` ; seed `image-default` (§3.6) et gabarit `puzzle-decor` (§9.3). @@ -1638,6 +1650,8 @@ la première migration du Studio, pas en arrivant au lot 11. ### Lot 4 — « Générer » dans le sélecteur, et le gate +> ✅ **Codé le 13/09** (étapes 1 à 4, migration `AddGuidedStepImageResource`). **Gate non passé.** Écarts : décision 23. + 1. Onglet « Générer » dans `ResourceTab` / `showNewResource` : un seul branchement (§3.0), qui apparaît dans les 13 types de section, les POI, les étapes et la Médiathèque. Absent si `StudioEnabled == false`. 2. Panneau : gabarit → 2-3 champs → point de départ (texte | image, décision 15) → coût estimé →