Studio : lots 0 à 4 codés, décision 23, cartes 280 et 300 à jour
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1fe76470ab
commit
59939ddb87
@ -3,9 +3,10 @@ title: Module Studio — génération d'images sous identité visuelle
|
|||||||
area: backend manager
|
area: backend manager
|
||||||
horizon: v2
|
horizon: v2
|
||||||
tags: Studio IA
|
tags: Studio IA
|
||||||
flag: good | V2 · consolidé 13/09, lots 0-4 prêts à coder
|
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)
|
src: v2/studio-plan.md — §8 (exécutable lots 0-4), §9 (catalogue v0), décisions 22-23
|
||||||
---
|
---
|
||||||
|
<p>🛠️ <strong>Lots 0 à 4 codés le 13/09</strong> sur la branche <code>studio</code> (manager-service, manager-app, mymuseum-visitapp) : ingestion serveur, crédits, identité visuelle (menu <strong>Studio</strong>), génération fal.ai derrière <code>IGenerationProvider</code> (fournisseur interchangeable), onglet <strong>Générer</strong> dans le sélecteur de ressource, image d'étape rattachée à une ressource. <strong>Reste avant le gate</strong> : bucket de dev + CORS, clé fal.ai (<code>Studio__Fal__Key</code>), migrations appliquées, calibrage et prix (§9.4), puis le test du conservateur.</p>
|
||||||
<p>✅ <strong>Plan consolidé le 13/09.</strong> 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 : <strong>upload par URL signée + ingestion serveur</strong> (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 <code>Grant</code> manuel, une recharge qui prolonge tout le solde, plafonds d'upload prudents, catalogue calibré au lot 3. Le webhook fal.ai est signé en <strong>ED25519, pas en HMAC</strong>.</p>
|
<p>✅ <strong>Plan consolidé le 13/09.</strong> 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 : <strong>upload par URL signée + ingestion serveur</strong> (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 <code>Grant</code> manuel, une recharge qui prolonge tout le solde, plafonds d'upload prudents, catalogue calibré au lot 3. Le webhook fal.ai est signé en <strong>ED25519, pas en HMAC</strong>.</p>
|
||||||
<p><strong>Le produit n'est pas l'accès aux modèles, c'est la cohérence.</strong> 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 <code>VisualIdentity</code> par instance (style verrouillé, époque, palette, exclusions, 1-5 images de référence) + <strong>surcharge optionnelle par configuration</strong> — le parcours Halloween a sa propre identité, exactement comme <code>Configuration</code> porte déjà ses <code>PrimaryColor</code>/<code>Languages</code>. Injectée côté serveur dans chaque prompt, <strong>jamais retapée</strong>. Boutons « Générer » dans l'éditeur de contenu, pas dans un playground ; gabarits métier, prompt libre en mode avancé seulement.</p>
|
<p><strong>Le produit n'est pas l'accès aux modèles, c'est la cohérence.</strong> 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 <code>VisualIdentity</code> par instance (style verrouillé, époque, palette, exclusions, 1-5 images de référence) + <strong>surcharge optionnelle par configuration</strong> — le parcours Halloween a sa propre identité, exactement comme <code>Configuration</code> porte déjà ses <code>PrimaryColor</code>/<code>Languages</code>. Injectée côté serveur dans chaque prompt, <strong>jamais retapée</strong>. Boutons « Générer » dans l'éditeur de contenu, pas dans un playground ; gabarits métier, prompt libre en mode avancé seulement.</p>
|
||||||
<p>⛔ <strong>Prérequis dur, carte séparée</strong> : le backend ne sait pas écrire dans le bucket. Sans le lot 0, rien du Studio ne tourne.</p>
|
<p>⛔ <strong>Prérequis dur, carte séparée</strong> : le backend ne sait pas écrire dans le bucket. Sans le lot 0, rien du Studio ne tourne.</p>
|
||||||
|
|||||||
@ -3,9 +3,10 @@ title: Socle de stockage — URL d'envoi signée et ingestion serveur
|
|||||||
area: backend manager
|
area: backend manager
|
||||||
horizon: v2
|
horizon: v2
|
||||||
tags: manager-service, manager-app, Studio IA, sécurité
|
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
|
src: v2/studio-plan.md — §8 lot 0, décisions 14, 18, 20
|
||||||
---
|
---
|
||||||
|
<p>🛠️ <strong>Code livré le 13/09</strong> (branche <code>studio</code>) : URL signée, <code>ResourceIngestionService</code>, ImageSharp, upload avec progression dans manager-app, SDK Firebase retiré. <strong>Reste l'infra</strong> ci-dessous, puis la checklist du §8.</p>
|
||||||
<p><code>IResourceBlobService</code> n'expose que <code>DeleteAsync</code> : <strong>tout l'upload est fait par le navigateur en direct</strong>, à deux endroits de <code>resources_screen.dart</code>. Or le Studio et le TTS pré-généré produiront côté serveur.</p>
|
<p><code>IResourceBlobService</code> n'expose que <code>DeleteAsync</code> : <strong>tout l'upload est fait par le navigateur en direct</strong>, à deux endroits de <code>resources_screen.dart</code>. Or le Studio et le TTS pré-généré produiront côté serveur.</p>
|
||||||
<p>⚠️ <strong>Trou de sécurité actuel, indépendant du Studio.</strong> manager-app n'a pas de Firebase Auth (<code>firebase_storage</code> sans <code>firebase_auth</code>) et écrit pourtant dans le bucket : les règles Storage sont très probablement <strong>ouvertes en écriture</strong>. Aucun <code>storage.rules</code> dans le repo — à relever dans la console.</p>
|
<p>⚠️ <strong>Trou de sécurité actuel, indépendant du Studio.</strong> manager-app n'a pas de Firebase Auth (<code>firebase_storage</code> sans <code>firebase_auth</code>) et écrit pourtant dans le bucket : les règles Storage sont très probablement <strong>ouvertes en écriture</strong>. Aucun <code>storage.rules</code> dans le repo — à relever dans la console.</p>
|
||||||
<p>✅ <strong>Tranché le 13/09 : URL signée + ingestion.</strong> L'API délivre une URL V4 signée (15 min, un objet, taille bornée), le navigateur envoie directement chez Google dans <code>incoming/</code>, puis <code>POST /ingest</code> : un <code>ResourceIngestionService</code> unique — le même pour le Studio et le TTS — mesure la <strong>taille réelle</strong>, applique quota et plafond, post-traite les images (2 à la fois au plus) et range sous <code>pictures/</code>. 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.</p>
|
<p>✅ <strong>Tranché le 13/09 : URL signée + ingestion.</strong> L'API délivre une URL V4 signée (15 min, un objet, taille bornée), le navigateur envoie directement chez Google dans <code>incoming/</code>, puis <code>POST /ingest</code> : un <code>ResourceIngestionService</code> unique — le même pour le Studio et le TTS — mesure la <strong>taille réelle</strong>, applique quota et plafond, post-traite les images (2 à la fois au plus) et range sous <code>pictures/</code>. 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.</p>
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
# Module Studio — Génération IA d'images, vidéos et 3D
|
# 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,
|
> **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
|
> 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 |
|
| 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 |
|
| 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) |
|
| 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
|
### 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**
|
**Backend**
|
||||||
|
|
||||||
1. `IResourceBlobService` : `CreateUploadUrl(storagePath, contentType, maxBytes)` → URL V4 signée en
|
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
|
### Lot 1 — crédits
|
||||||
|
|
||||||
|
> ✅ **Codé le 13/09.** Écarts : décision 22.
|
||||||
|
|
||||||
1. Migration `AddStudioCredits` : colonnes `Instance` / `SubscriptionPlan` (§5), table `CreditLedgerEntries`.
|
1. Migration `AddStudioCredits` : colonnes `Instance` / `SubscriptionPlan` (§5), table `CreditLedgerEntries`.
|
||||||
2. `StudioCreditService` : `Reserve`, `Charge`, `Refund`, `Grant`, `Expire`. **`Reserve` sous verrou
|
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 :
|
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
|
### 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).
|
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,
|
2. Service : identité de base créée vide au premier accès, non supprimable ; `Resolve(instanceId,
|
||||||
configurationId)` ; `duplicate` = copie complète + `CopiedFromIdentityId/Version` ; `Version++` à
|
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
|
### 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`,
|
1. Migration `AddGeneration` : `GenerationModels`, `GenerationTemplates`, `GenerationJobs`,
|
||||||
`GeneratedAssets`, `Resource.AiProvenance`, `User.CanValidateAssets` ; seed `image-default` (§3.6)
|
`GeneratedAssets`, `Resource.AiProvenance`, `User.CanValidateAssets` ; seed `image-default` (§3.6)
|
||||||
et gabarit `puzzle-decor` (§9.3).
|
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
|
### 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
|
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`.
|
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é →
|
2. Panneau : gabarit → 2-3 champs → point de départ (texte | image, décision 15) → coût estimé →
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user