ADR-0024 — Un moteur creatif, deux profils : la plateforme est un tenant de son propre Studio
Prolonge l'ADR 0023 (une capacite, un contexte, N grammaires) et l'ADR 0017 (Remotion est le moteur video des planes A et C). Ne revoque ni l'ADR 0003 (Creative est un process vertical, pas un second produit), ni l'ADR…
Statut
Accepté · 2026-09-18
Piliers : ai-platform, growth-web, app-shell, data-platform, design-system, security-identity, integrations, billing
Prolonge l'ADR 0023 (une capacite, un contexte, N grammaires) et l'ADR 0017 (Remotion est le moteur video des planes A et C). Ne revoque ni l'ADR 0003 (Creative est un process vertical, pas un second produit), ni l'ADR 0004 (le Studio agence est un tenant), ni l'ADR 0013 (deux namespaces de permissions). Chacun est cite la ou il contraint.
Contexte
Fixe la cible le 2026-09-18, apres avoir relu les captures de reference d'un studio generatif du marche :
On ne doit pas construire quatre produits differents. Il faut construire un noyau Studio commun, puis deux contextes d'execution strictement separes : Founder Studio pour la plateforme elle-meme et Store Studio pour chaque utilisateur / store. Meme logique pour les avatars : un moteur commun, deux surfaces / contextes / permissions, pas deux implementations divergentes.
Et la contrainte de methode : la codebase est la source de verite, pas le brief ; on cherche avant de conclure, on fusionne avant de creer.
L'audit du meme jour
(docs/audits/2026-09-18-creative-growth-os.md)
mesure ce que le depot porte deja, et le resultat n'est pas « il manque
un Studio ». Il y en a deux, et ils ne se parlent pas :
| Plane B, le Studio vendu | Plane A, la plateforme se produit elle-meme | |
|---|---|---|
| Entree | le chat, toggle mediaMode du menu « + » (composer-plus-menu.tsx) | /admin/creative/generate, formulaire derive de templates.json |
| Moteur | Nano Banana Pro et Veo 3.1 sur l'AI Gateway, appeles dans le tour (handler-tools-build.ts) | Remotion sur GitHub Actions (render-dispatch.ts), ElevenLabs (voiceover.ts) |
| Ou vont les fichiers | Blob stores/<storeId>/studio/ | Blob creative/library/<template>/<slug>/ |
| Enregistrement | StudioAsset : concept, angle, hook, variante, cout, QC, drop | aucune ligne en base : le chemin Blob EST l'inventaire (generated-assets.ts) |
| Argent | canAffordStudioMedia avant, trackStudioMediaUsage apres, ligne Credit avec providerCost (billing.ts) | rien : minutes de runner et caracteres ElevenLabs hors ledger |
| Qui | un membre d'une organisation, sur un store en contexte | le role ADMIN (requireAdmin), depuis growth-web/2758 aussi platform.content.operate |
| Verite produit | catalogue, kit de marque, concepts valides du store | facts.md de boostecom-content, garde check-plan.mjs |
Deux defauts structurels rendent la cible impossible sans decision :
-
Les capacites media ne sont pas dans le registre. L'ADR 0023 a fait du registre
src/features/ai/tools/la liste unique de ce que la plateforme sait faire, et le canvas Workflow, le serveur MCP et l'API en derivent leur catalogue. MaisgenerateImage,composeProductImage,generateVector,generateVideo,saveStudioAsset,planCreativeVariantsetsaveCreativeConceptsont construits par tour de chat dansorchestrator/runtime/handler-tools-build.tset assembles danshandler-tools-assembly.ts, hors deTOOL_FAMILIES. Consequence mesurable : le noeudai.generate-imagedu canvas estplanned(workflow-capabilities.ts), le catalogue servi parGET /api/workflow/v2/capabilitiesne contient aucune generation, et le serveur MCP porte un seul outil Studio, le scopeboostecom:studio.read, en lecture (un scope, pas un outil : l'ADR 0023 interdit de confondre les deux, et il vit danslib/security/mcp-scopes.ts). Un agent peut parler d'une creative ; il ne peut en produire une que par le chat. « Manual et agent sur le meme pipeline » est donc vrai dans le chat et faux partout ailleurs, pour une raison de rangement. -
La plateforme n'a pas de profil. Quand elle se produit elle-meme, elle ne passe ni par
StudioAsset, ni par le ledger, ni par la QC, ni par le lineage concept → variante que le Studio vendu porte deja. Elle a un second OS, avec ses propres refus, sa propre bibliotheque et aucun cout enregistre.billing/0613en est le symptome comptable : quand l'equipe produit depuis son organisation d'agence, la depense apparait comme de la consommation client.
Trois faits deja tranches bornent la solution, et ce sont eux qui rendent la decision courte :
- La plateforme n'est PAS nommee par un identifiant.
acces-delegue-et-frontiere-studio.mda rejetePLATFORM_ORG_IDetOrganization.kind;features/studio/internal-scope.tsresout l'organisation interne par l'adhesion de l'admin, plus les organisations atteintes par un mandat org-scope. Le profil Founder existe donc deja comme un contexte : l'organisation que la plateforme opere, et rien d'autre. - Le Studio vendu et le Studio agence partagent deja leurs chargeurs
(
features/studio/boards.ts) et leurs composants (features/studio/components/), sous deux namespaces de permissions (ADR 0013). Le modele « un core, deux profils » est deja la forme du Studio ; il ne couvre simplement pas la generation. - Remotion est le moteur deterministe des planes A et C (ADR 0017), et la hierarchie « vraie UI > composants reels > captures > imitation generative » y est ecrite. Un moteur generatif ne remplace pas Remotion pour montrer le produit.
- La liste des moteurs est fermee. AI Gateway (Vercel), ElevenLabs, Remotion, Playwright, Postiz, Datafast. Rappele le 2026-09-18, au moment ou les captures d'un studio generatif du marche servaient de reference : « on reproduit, en aucun cas on va l'utiliser ». Ce qu'on copie est la forme des controles ; le moteur derriere reste le notre.
Décision
Il y a un seul moteur creatif. Ses capacites vivent dans le registre, son contexte est le tenant, et « Founder » comme « Store » sont deux valeurs du contexte, jamais deux implementations. Concretement :
-
Les capacites media entrent dans le registre. Une famille
studio-media(etapestudio,stages.ts) portegenerateImage,composeProductImage,generateVector,generateVideo,saveStudioAsset,planCreativeVariantsetsaveCreativeConcept, avec le meme nom qu'aujourd'hui. Ce qu'elles exigent en plus du contexte commun (les images attachees au tour, l'enveloppe de facturation) devient un champ deAtlasToolContextrempli par chaque grammaire, et jamais un argument que seule la grammaire chat sait donner. Le chat ne perd rien ; le canvas, le MCP, l'API et le cockpit/opsgagnent la generation par derivation, sans qu'aucune liste ne soit recopiee. -
Toute generation est une requete, et une requete est une ligne. Un objet
GenerationRequest(capacite, parametres valides, references resolues par leur identifiant, scope, demandeur, cout estime) est ce que le chat, un noeud du canvas, un appel MCP et le formulaire/opsconstruisent ; un enregistrement persiste avant l'appel provider et porte l'etat (queued,submitted,processing,completed,failed,cancelled), le cout observe et l'artefact produit. Les generations longues passent par les jobs QStash existants (services/jobs/), pas par le tour de chat : le clip qui depasse 180 s cesse d'etre la seule depense du systeme qui peut disparaitre entre le debit et la livraison (ai-platform/0377). L'appel synchrone reste possible pour ce qui tient dans le tour ; il ecrit la meme ligne. -
Un provider est un adaptateur derriere une capacite produit, jamais une capacite, et la liste des providers est fermee. Les moteurs de cette plateforme sont l'AI Gateway de Vercel (images et videos), ElevenLabs (voix), Remotion (composition deterministe), Playwright (captures du produit reel, la marche du haut de la hierarchie de l'ADR 0017), Postiz (distribution) et Datafast (mesure). Le proprietaire l'a redit le 2026-09-18 : un studio generatif tiers est une reference d'ergonomie, jamais un moteur de ce produit. Aucun nouveau fournisseur de generation n'entre sans une decision ecrite.
Ce que l'abstraction achete n'est donc pas d'ajouter des fournisseurs : c'est que
character-motion-transfersoit une capacite produit dont l'implementation (une option de Veo, une composition Remotion, ou rien) est un detail que l'UI, le canvas, le MCP et l'API ne connaissent pas. Une capacite que nos moteurs ne servent pas est declareeplanned, visible et desactivee, avec la raison.config/ai-models.tsreste le seul catalogue de modeles. -
Le profil Founder est un tenant. La plateforme se produit elle-meme depuis l'organisation qu'elle opere, resolue par adhesion et mandat comme
internal-scope.tsle fait deja, sur unStorequi porte sa marque. Ses rendus sont desStudioAssetde ce store, ses depenses sont des lignes du ledger de ce store, sa QC est la QC du Studio, et sa verite produit estfacts.mdmonte comme source de connaissance de ce store, avec sa garde. Le generateur Remotion devient une capacite du registre (renderComposition) que ce tenant exerce comme les autres ;creative/library/reste lisible le temps de la migration et cesse d'etre un second inventaire. Ce que ce profil a en plus est du contexte, pas du code : les mandatsplatform.*, la lecture du depot comme verite produit, les canaux Postiz de la plateforme. Ce qu'il n'a pas : un identifiant code en dur, une brancheif (isFounder)dans une capacite, un stockage a part. -
Un store ne voit que son scope, a chaque couche, et un test le prouve. Le prefixe Blob, la ligne
Credit, les lectureswhere: { id, storeID }et les portes destudio-authorization-doorstiennent deja pour les ecritures. Deux trous sont fermes par cette decision : la resolution des references (studio-references.ts) accepte aujourd'hui toute URL Blob publique, donc l'asset d'un autre store passe l'allowlist ; et le prefixestudio/unassignedfacture une organisation sans donner de proprietaire au fichier. Une reference se resout par identifiant d'asset dans le scope, et un rendu sans store est refuse avant l'appel provider. Les tests de scope croise (store A ne lit ni ne reference ni ne sauve vers store B ; un mandat store n'ouvre aucune capacite du profil Founder) sont la garde de ce point, pas la relecture. -
Une identite visuelle est un objet, et elle ne s'appelle pas « avatar ». Dans ce depot, avatar designe deja l'avatar marketing, la cible d'une creative (
CreativeConcept.marketingAvatar, la matrice Avatar × Angle du skillcreative-ads,StudioAsset.persona). Le personnage reutilisable de la cible, le fondateur, un porte-parole, un personnage de marque, avec ses references, ses versions, ses attributs et sa voix, est un objet distinct,StudioIdentity, scope comme un asset (storeID), reference par@nomdans une requete, et resolu par le meme resolveur de references. Le mot « avatar » reste libre pour l'ecran, jamais pour un identifiant du moteur.
Ce que cette decision change dans la doctrine figee de
creative-media-os.md §2, et il
faut le dire plutot que le laisser deriver : la regle dure 1 (« un
render A n'ecrit jamais un StudioAsset marchand ») et la regle 3 (« le
chat B ne lit jamais facts.md comme verite pub ») etaient ecrites pour
empecher un melange entre la production de la plateforme et celle
d'un client. Le point 4 garde exactement cette intention et change son
mecanisme : un render de la plateforme ecrit un StudioAsset de son
propre store, jamais d'un store client ; et facts.md est la verite
produit de ce store-la, montee comme source de connaissance de ce
seul tenant, donc invisible a tout autre chat par construction. La
regle 4 (ADR 0013) est inchangee.
La regle 2 (« un render C n'ecrit jamais le ledger Postiz Growth »)
demande une reponse plus honnete que « inchangee », parce que ce qui la
tenait etait la separation des planes, et que cette decision la remplace.
Ce qui la tient desormais est une limite connue, pas un mecanisme :
POSTIZ_API_KEY est une seule valeur d'environnement, celle de la
plateforme, donc aucun profil Store n'a de file Postiz a atteindre. La
distribution est mono-tenant, c'est mesure dans l'audit du meme jour, et
la tranche 8 est l'endroit ou elle cesse de l'etre : le jour ou un store
a son propre canal, la regle 2 devra etre portee par une capacite de
distribution refusee hors du tenant qui possede le canal. Le dire
maintenant coute une phrase ; le decouvrir apres coute un post publie
sous le mauvais compte.
Le plane C (l'agency desk) n'est pas supprime et n'est pas un
troisieme profil : c'est le profil Store, applique a un Store de
l'organisation agence. C'est deja ce que l'ADR 0004 a decide, et rien ici
ne le change.
La doctrine passe de trois planes a
un moteur et des scopes, et creative-media-os.md le note en tete
de son §2 dans la premiere PR de la tranche 4.
Ce que cette decision change dans l'ADR 0017, et qu'il faut nommer
plutot que couvrir d'un « prolonge » : sa deuxieme puce de decision (« les
contenus A vivent sous creative/ + skills + Postiz ») cesse de valoir
pour l'enregistrement. Un rendu de la plateforme devient un
StudioAsset du store de la plateforme, et creative/library/ est un
chemin de transition. creative/ reste la source des compositions
Remotion, et Remotion reste le moteur video : le reste de l'ADR 0017 est
inchange. Le frontmatter garde supersedes: "" parce qu'il n'y a pas
supersession totale, mais la clause amendee est nommee ici.
Un septieme chemin de generation existe dans le depot et il faut le
nommer pour qu'il ne soit pas lu comme une exception a la liste fermee.
src/config/native-mcps.ts declare le serveur MCP natif higgsfields
(generate_image, generate_video, reframe), offert dans le menu
« + » du composer. Qualifie le 2026-09-19 : c'est
un wrapper des modeles que notre AI Gateway sert deja, donc il ne
peut exister que comme possibilite de secours (leur MCP ou leur API,
en cas de panne de notre chemin), et en aucun cas comme notre systeme.
La liste des moteurs reste a six. Ce que le depot doit corriger, c'est
la presentation : la page legale publique le declare sous « Creation
d'actifs : Studio » et le composer l'offre au meme rang que nos
capacites, pendant que pricing-copy-engines.test.ts le range,
correctement, dans les moteurs que nous n'appelons pas.
backlog/integrations/2794
aligne les trois endroits sur cette regle.
Ce que cette decision ne fait pas : elle ne renomme pas le canvas.
Le depot a tranche en septembre 2026 que le canvas s'appelle
Workflow et que Studio est le hub creatif
(workflow/v2/README.md, ai-platform/2703) ; le brief appelait
« Studio » le canvas, et c'est le brief qui s'aligne. La « creative
surface » de la cible est le Studio : ses boards, sa bibliotheque et le
composer de generation que la tranche 1 lui donne. Le canvas reste la
grammaire « graphe » des memes capacites.
Alternatives écartées
| Option | Pourquoi non |
|---|---|
| Un Founder Studio a part, page admin dediee avec ses propres outils | C'est l'etat actuel (plane A), et c'est ce qui coute : deux inventaires, deux refus, aucun ledger, une QC absente. L'ADR 0003 l'avait deja refuse pour le Studio vendu (« aucun deuxieme Studio »). |
Laisser les outils media dans l'orchestrateur et faire lire handler-tools-build.ts par le canvas et le MCP | Une seconde porte vers les memes outils, sans TOOL_FAMILIES, sans etape, sans risque declare : exactement la liste locale que l'ADR 0023 interdit, et que the-studio-runs-what-it-declares ne pourrait pas deriver. |
Nommer l'organisation de la plateforme (PLATFORM_ORG_ID, Organization.kind) | Rejete par acces-delegue-et-frontiere-studio.md : un "" fail-soft change le comportement en preview, un flag se mint. L'adhesion et le mandat existent, sont revocables et audites. |
| Adopter un studio generatif tiers comme moteur (celui dont les captures servent de reference d'ergonomie, ou un autre) | Refuse le 2026-09-18 : « on reproduit, en aucun cas on va l'utiliser ». Et l'ADR 0017 avait deja mesure pourquoi la demonstration du produit ne peut pas etre generative (fidelite de marque et d'interface). Les captures restent une reference d'ERGONOMIE : ce qu'on copie est la forme des controles, jamais le fournisseur. |
| Une capacite que nos moteurs ne servent pas, rendue quand meme par un fournisseur ajoute a la volee | Un fournisseur est une facture, une cle, une surface d'attaque et une dependance de disponibilite. Une capacite sans moteur est planned, visible et desactivee, avec sa raison : c'est la regle que the-studio-runs-what-it-declares applique deja au canvas, et elle vaut ici. |
| Les captures de reference comme spec exhaustive de l'UI, reproduites une par une | Les captures ne sont pas dans le depot au jour de cette decision. Un squelette de matrice existe dans l'audit ; la parite d'interaction attend les captures reelles. Decider l'UI sans elles serait deviner. |
| Appeler « Studio » le canvas, comme le brief | Renomme un produit deja nomme, contre ai-platform/2703 et le README du canvas. Deux mots pour une chose est le defaut que l'ADR 0023 refuse (« si trois surfaces nomment differemment la meme chose, il y a trois produits »). |
| Reutiliser « avatar » pour l'identite visuelle | Collision avec l'avatar marketing dans le schema, le skill et les boards. Un nom qui designe deux choses casse la regle « une capacite porte le meme nom partout ». |
| Attendre AI SDK 7 pour l'asynchrone | L'ADR 0019 tient la 6 jusqu'a une eval verte, et il nomme lui-meme le signal qui rouvre la question : « une fonctionnalite de la 7 dont un item a besoin ». Le contrat start / status de la 7 en est une, pour Veo. Mais une ligne GenerationRequest et un job QStash ne dependent d'aucune version du SDK : l'asynchrone se gagne cote plateforme avant de se gagner cote SDK. La migration reste un item ; elle n'est plus un prealable. |
Conséquences
Ce que ca coute, et on l'accepte :
- Le registre grossit de sept outils et d'un champ de contexte.
AtlasToolContextgagne les attachements du tour et l'enveloppe de facturation ; chaque grammaire doit les remplir, et le canvas doit apprendre a attacher une image a un noeud. C'est la tranche 1, et elle toucheai-platformen profondeur. - Une table de plus (
GenerationRequest), additive, aucune colonne requise sans defaut sur un modele existant ; le plafond denot-null-column-adds.test.tsmonte de ses colonnes neuves, chacune nommee. - La plateforme paie ses propres credits, visiblement. Le point 4
fait entrer la production interne dans le ledger. Comment la
distinguer du revenu consomme (une ligne interne, un compte separe, un
taux) est
billing/0613, une decision de gouvernance, et cet ADR ne la prend pas : il rend seulement la depense visible la ou elle etait invisible. creative/library/et/admin/creative/generatedeviennent des chemins de transition. Rien n'est supprime avant que la meme capacite soit atteignable par le registre et que la bibliotheque du store de la plateforme liste les memes fichiers.- Des capacites resteront
plannedlongtemps. Le mouvement d'un personnage, le remplacement d'un personnage dans une video source et l'enchainement de scenes cinematiques ne sont servis par aucun de nos moteurs aujourd'hui. L'abstraction les rend nommables et desactivees plutot qu'absentes ; elle ne les rend pas disponibles, et aucun fournisseur n'est ajoute pour les obtenir sans une decision ecrite.
Ce que ca achete :
- une capacite creative existe une fois et s'exerce depuis cinq grammaires ; en ajouter une (Motion d'un personnage) en ajoute une partout ;
- la production de la plateforme est mesuree, relue et lignee comme
celle d'un client, donc les apprentissages (
CreativeLearning) la couvrent aussi ; - un store client obtient, le jour ou son plan l'inclut, exactement le Studio avec lequel la plateforme a fait sa propre croissance, sans qu'une seconde copie ait ete ecrite.
Le signal qui dirait de revisiter : une capacite creative que le registre ne sait pas exprimer parce qu'elle est un flux long avec un humain dans la boucle (un montage a plusieurs allers-retours). Ce jour-la on etend le registre a des capacites a etats, comme l'ADR 0023 le prevoit, on ne recree pas un editeur a cote.
Comment c'est appliqué
Les trois premieres tranches sont livrees, et c'est ce qui fait passer
cet ADR de proposed a accepted : chaque point est desormais tenu
par une garde qui echoue, pas par une relecture. Les trois dernieres
lignes du tableau restent des gardes ATTENDUES, et elles sont ecrites au
futur pour cette raison.
| Point | Garde qui le tiendra |
|---|---|
| 1, capacites media dans le registre | Livre. src/test/the-studio-runs-what-it-declares.test.ts derive executeur et catalogue, ET son dernier bloc lit les sept capacites media dans le registre, verifie que six sont planned derriere la porte d'approbation du canvas et que planCreativeVariants, seule lecture, est available — depuis leur RISQUE, pas depuis une liste. src/test/surfaces-import-their-capabilities.test.ts, annoncee par l'ADR 0023 et absente du disque au jour de cette decision, existe. Reste une moitie que ce point ne peut pas gagner seul : le scope MCP d'ECRITURE media, qui depend d'une decision de gouvernance (integrations/0714) et pas d'une garde |
| 2, une requete = une ligne | Livre : src/test/a-generation-is-a-row.test.ts |
| 3, provider = adaptateur | Livre : scripts/check-model-catalogue.mjs + src/test/model-catalogue.test.ts — config/ai-models.ts reste le seul endroit ou un identifiant de modele est ecrit |
| 4, Founder = tenant | analytics-planes-style : aucun if (isPlatformAdmin) dans une capacite studio-media ; le store de la plateforme est resolu par internal-scope.ts, jamais par une constante |
| 5, scope a chaque couche | les tests de scope croise nommes ci-dessus, dans src/test/ |
6, StudioIdentity | docs-corpus et le schema : un modele, un nom, reference @ resolue par studio-references.ts |
Tant qu'une garde n'existe pas, la ligne qu'elle tient est une regle tenue a la main, donc une regle qui sera violee. Les lignes 4 a 6 sont dans cet etat ; les lignes 1 a 3 n'y sont plus.