diff --git a/v2/studio-plan.md b/v2/studio-plan.md index 915ea5e..3d25dbb 100644 --- a/v2/studio-plan.md +++ b/v2/studio-plan.md @@ -156,6 +156,7 @@ Tout sur `Instance`, dupliqué depuis `SubscriptionPlan` : `StorageQuotaBytes`, | 19 | Contenu éditorial — *13/09* | Un **catalogue v0** (styles, règle d'assemblage, 3 gabarits) est écrit au §9 comme seed ; il est **calibré à la fin du lot 3** par des générations réelles, avant d'ouvrir le lot 4 | | 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) | --- @@ -207,7 +208,7 @@ est donc : | **Studio › Identité visuelle** | L'écran structurant | neuf | | **Studio › Personnages** | Registre des personnages : guide et figures de parcours, avec leurs facettes visage et voix — voir §3.8 | neuf | | **Studio › À valider** | Les **brouillons** — ils ne sont pas encore des `Resource`, donc ils n'ont pas leur place dans Ressources. Liste courte : jobs abandonnés, et générations d'un `ContentEditor` sans la permission de validation | neuf | -| **Jauge du pied de menu → popover** | Les chiffres de crédits : mois, réservés, disponibles, votre jour, plafond dur. Accessible à tous | enrichi | +| **Jauge du pied de menu → popover** | Les chiffres de crédits : disponibles, solde, réservés, expiration, votre jour. Accessible à tous | enrichi | | **Abonnement** | Le détail et le **journal d'usage exportable**. Pas de sous-onglet Studio dédié | enrichi | > **Décidé le 2026-09-01 : pas de sous-onglet « Usage & crédits ».** Il dupliquerait une surface qui @@ -556,9 +557,9 @@ onglet fermé, navigateur planté. Précédents en place : `AuditLogPurgeService ### 3.5 Crédits et garde-fous ``` -SubscriptionPlan += HasStudio, StudioCreditsGranted -- dotation à la souscription Instance += StudioEnabled, StudioCreditsBalance, StudioCreditsExpireAt, - StudioCreditsHardCap, StudioPerUserDailyCap, StudioProviderRegion + StudioPerUserDailyCap + -- plafond dur, dotation de plan et région : retirés (décision 22) CreditLedger (append-only, exportable CSV) Id, InstanceId, UserId, GenerationJobId?, Kind, Amount, BalanceAfter, @@ -603,10 +604,10 @@ CreditLedger (append-only, exportable CSV) **Le plafond dur n'est pas le bon outil contre le stagiaire.** Le scénario « vider un budget en une après-midi » est un problème **par utilisateur**, pas par organisation : le solde est -justement là pour être dépensé. Deux garde-fous distincts : +justement là pour être dépensé. Le garde-fou est donc : -- `StudioCreditsHardCap` — plafond organisation que même un dépassement facturé ne franchit pas. -- `StudioPerUserDailyCap` — le vrai rempart : N générations par utilisateur et par jour. +- `StudioPerUserDailyCap` — N crédits engagés par utilisateur et par jour, réservations en cours + comprises. Le plafond organisation a disparu avec les crédits prépayés (décision 22). **Réservation en deux temps.** `CheckQuota` avant / incrément après suffit à un appel LLM bloquant de 2 s. Il ne suffit pas à dix jobs Hangfire de 40 s lancés en parallèle : les dix contrôles passent avant @@ -1195,7 +1196,7 @@ POST /api/Studio/personas/{id}/archive ``` GET /api/Studio/credits?instanceId= → { balance, reserved, available, - expiresAt, hardCap, + expiresAt, studioEnabled, perUserDailyCap, usedToday } GET /api/Studio/credits/ledger?from=&to=&format=csv POST /api/Studio/credits/grant [Policy = SuperAdmin] @@ -1273,9 +1274,8 @@ Resource += Origin int not null default 0 -- Uploaded=0 | Generated SourceResourceId? -- lignage parent (lot 0) AiProvenance jsonb? -- détail (lot 3) Instance += StudioEnabled, StudioCreditsBalance, StudioCreditsExpireAt, - StudioCreditsHardCap, StudioPerUserDailyCap, StudioProviderRegion, - IsAiWatermarkBurned -SubscriptionPlan += HasStudio, StudioCreditsGranted + StudioPerUserDailyCap, -- lot 1, livré + IsAiWatermarkBurned -- lot 6 User += CanValidateAssets VisitorQuestion += PersonaId? -- sans lui, les stats restent agrégées sur un -- assistant imaginaire : impossible de voir qu'un @@ -1575,8 +1575,8 @@ la première migration du Studio, pas en arrivant au lot 11. 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 : - `StudioEnabled` → plafond utilisateur/jour → plafond dur → solde disponible. Refus en codes - (`studio_disabled`, `daily_cap`, `hard_cap`, `insufficient_credits`). + `StudioEnabled` → plafond utilisateur/jour → solde disponible. Refus en codes + (`studio_disabled`, `daily_cap`, `insufficient_credits`). 3. `Grant` : solde augmenté, `StudioCreditsExpireAt = now + 12 mois` (décision 17). 4. Job Hangfire quotidien `StudioCreditExpiryService` : solde échu → ligne `Expire` du montant perdu, solde à 0 ; instance à réservations ouvertes sautée, reprise au passage suivant.