Plan Studio : lot 1 livre, decision 22

Plafond dur organisation retire : avec des credits prepayes, le solde est deja
le plafond. SubscriptionPlan.HasStudio / StudioCreditsGranted et
Instance.StudioProviderRegion ne sont pas crees tant que rien ne les lit. API
credits, schema et etapes du lot 1 alignes sur le code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thomas Fransolet 2026-09-13 19:51:21 +02:00
parent d70d851b6f
commit 1fe76470ab

View File

@ -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.