Audit du Creative & Growth OS : 18 septembre 2026
Perimetre : tout ce qui, dans ce depot, produit, range, paie, relit, distribue ou mesure une creative, pour la plateforme elle-meme comme pour un store client. Seize sous-systemes lus en parallele par autant de lecteurs…
Perimetre : tout ce qui, dans ce depot, produit, range, paie, relit, distribue ou mesure une creative, pour la plateforme elle-meme comme pour un store client. Seize sous-systemes lus en parallele par autant de lecteurs independants (le tableau « La carte » ci-dessous les enumere, et c'est lui qui fait foi sur le compte), chaque affirmation
MISSING/REDUNDANT/SAFE_TO_REMOVE/MISPLACEDqui decide du plan contre-verifiee par un sceptique a trois lentilles (autre nom, implementation partielle, preuve fausse), puis une synthese et une critique de completude. La decision qui en sort est l'ADR0024; ce document est la preuve, l'ADR est la regle.
Ce qui a ete trouve, en une phrase
Le depot ne manque pas d'un Studio : il en a deux qui ne se parlent pas (le Studio vendu, sur l'AI Gateway, avec ledger, QC et lineage ; et la production de la plateforme, sur Remotion et ElevenLabs, sans ligne en base ni cout enregistre), et les sept capacites media du premier vivent hors du registre que l'ADR 0023 a fait adopter par le canvas, le MCP et l'API, donc aucune grammaire autre que le chat ne sait generer.
La methode, et ce qu'elle ne couvre pas
- Codebase first. Aucune affirmation de ce document ne vient du
brief ; chacune cite un fichier. Quand un lecteur a dit
MISSING, un sceptique a cherche le concept sous un autre nom, sous forme partielle, et a relu la preuve. Les affirmations qui n'ont pas ete contre-verifiees sont marquees comme telles dans la gap analysis : elles sont des constats d'inventaire, pas des verdicts. - Les captures de reference ne sont pas dans le depot. Le brief s'appuie sur plus de vingt captures d'un studio generatif du marche ; trois sont decrites textuellement (Replace, Motion, Cinematic > Scenes). La matrice ecran ci-dessous est un squelette construit sur ces descriptions et sur les primitives existantes. La parite d'interaction attend les captures reelles, versionnees dans le depot (elles sont un prerequis nomme, pas un detail).
- Les moteurs sont ceux du depot, et la liste est fermee. AI Gateway (Vercel), ElevenLabs, Remotion, Playwright (les captures du produit reel), Postiz, Datafast. Le studio generatif dont viennent les captures est une reference d'ergonomie, jamais un fournisseur : rappele le 2026-09-18, « on reproduit, en aucun cas on va l'utiliser ». Une capacite que nos moteurs ne servent pas est nommee et desactivee, pas sous-traitee.
- Pas de recommandation sur l'argent. Le plan qui inclut la generation video, qui paie la production de la plateforme, le cout de gros reel d'une image, la licence Remotion : chiffres et options sont dans le registre des decisions, jamais tranches ici.
Les deux Studios, mesures
| Le Studio vendu (plane B) | La plateforme se produit elle-meme (plane A) | |
|---|---|---|
| Entree | le chat, mediaMode dans le menu « + » du composer (composer-plus-menu.tsx) ; une regex sur l'objectif arme aussi les outils quand le toggle est « off » (studio-capabilities.ts) | /admin/creative/generate (role ADMIN) et, depuis growth-web/2758, /ops/content (platform.content.operate) |
| Capacites | generateImage, composeProductImage, generateVector, generateVideo, saveStudioAsset, planCreativeVariants, saveCreativeConcept : construites par tour dans handler-tools-build.ts, assemblees dans handler-tools-assembly.ts, hors TOOL_FAMILIES | launchTemplateRender (render-dispatch.ts) : dix formes × six toiles × quatre fichiers (templates.json), synthesizeVoiceover (voiceover.ts) |
| Moteurs | Nano Banana Pro et Veo 3.1 Fast sur l'AI Gateway, nommes une fois dans ai-models.ts ; le vecteur est ecrit par le modele, sans moteur image | Remotion sur GitHub Actions (creative-render.yml), ElevenLabs |
| Asynchrone | non : experimental_generateVideo est synchrone en ai@6, polle 180 s dans un tour de 300 s ; un clip plus long est paye et perdu (ai-platform/0377) | workflow_dispatch sans identifiant de run rendu, etat relu sur l'API GitHub, aucune ligne en base |
| Fichiers | Blob stores/<storeId>/studio/, ou studio/unassigned sans store | Blob creative/library/<template>/<slug>/, creative/voiceover/<slug>/ |
| Enregistrement | StudioAsset (storeID, neuf termes North Star, conceptId, variantOfId + variantAxis, generationCostUsd, QC, dropId), best-effort : la ligne n'existe que si le modele appelle saveStudioAsset | aucune : « le chemin EST l'enregistrement » (generated-assets.ts) |
| Argent | canAffordStudioMedia avant, trackStudioMediaUsage apres, ligne Credit avec providerCost estime (billing.ts) ; generationCostUsd lu d'une Map en memoire qui ne survit pas a une autre instance Vercel, et jamais remplie pour la video | rien : minutes de runner et caracteres ElevenLabs hors ledger (billing/0613) |
| Verite produit | catalogue, kit de marque, concepts valides du store | facts.md de boostecom-content, garde check-plan.mjs |
| QC, lineage, apprentissage | file QC, variantOf, CreativeLearning | aucun |
| Portes | chat seulement ; MCP boostecom:studio.read et REST /studio/[section] en lecture | deux server actions ; aucune porte MCP ni API |
Les trois planes de creative-media-os.md §2
tenaient ces deux pipelines a distance par doctrine (« un render A n'ecrit
jamais un StudioAsset »). L'ADR 0024 garde l'intention (ne jamais
melanger la production de la plateforme et celle d'un client) et change
le mecanisme : le scope, pas le pipeline.
La carte, par sous-systeme
Seize lectures. Les fichiers pivots, et ce que chaque sous-systeme decide :
| Sous-systeme | Ou il vit | Ce qu'il decide | Ce qu'il ignore |
|---|---|---|---|
| Chat, surface de commande | features/ai/chat/runtime/, orchestrator/runtime/handler*.ts | quand une capacite media s'arme (resolveStudioCapabilities : le toggle est un indice, une regex sur l'objectif suffit), quel modele, quel niveau d'autonomie | qu'un autre appelant puisse generer |
| Canvas, surface graphe | features/ai/workflow/v2/, api/workflow/v2/ | quelles capacites du registre un store peut executer (capability-catalogue.ts, derive), l'etat available / planned | la generation : ai.generate-image est planned, l'executeur n'a pas de branche |
| Generation media | orchestrator/runtime/handler-tools-build.ts (2301 lignes), handler-tools-assembly.ts | les sept capacites creatives, leurs limites zod, leur prompt compose, leur cout | le registre : aucune n'est dans TOOL_FAMILIES |
| Registre de capacites | features/ai/tools/index.ts, stages.ts, CLAUDE.md | la liste unique que le canvas, le MCP et l'API derivent (ADR 0023), l'etape de chaque famille, le risque de chaque outil | la generation, et la garde surfaces-import-their-capabilities que l'ADR 0023 annonce est absente du disque |
| Assets et lineage | prisma (StudioAsset, CreativeConcept, StudioDrop, CreativeLearning), features/studio/boards.ts | concept → asset → variante → drop → apprentissage, la QC, le cout par asset | la generation qui l'a produit : ni prompt, ni references, ni parametres, ni run |
| Bibliotheques | services/creative/generated-assets.ts (Blob nu), admin/creative/library, StudioAssetGrid | trois inventaires disjoints, aucun modele commun | la suppression : studioAsset.delete n'a aucun appelant, un store supprime laisse ses blobs |
| Remotion, plane A | creative/ (dix primitives, trois compositions, pipeline), services/creative/render-dispatch.ts, creative-render.yml | le rendu deterministe du produit, dix formes × six toiles × quatre fichiers | son propre suivi : workflow_dispatch ne rend aucun identifiant de run, rien n'est persiste |
| Voix | services/creative/voiceover.ts, creative/pipeline/tts.mjs | la synthese, le quota, les trois refus avant depense | qu'une voix appartienne a quelqu'un : ELEVENLABS_VOICE_ID est une valeur d'environnement pour toute la plateforme |
| Identite visuelle | nulle part | rien | tout : aucun modele, aucun type, aucun outil, aucun ecran |
| Tenancy | lib/security/ (permissions, platform-permissions, access-grants, tenant-auth, collab-access), features/studio/internal-scope.ts | qui est admin plateforme, quelles platform.* un mandat porte, quelle organisation la plateforme opere, que le mandataire n'entre pas cote user | un contexte unique : quatre resolveurs d'entitlement, aucun StudioRuntimeContext |
| Jobs | services/jobs/ (QStash, DLQ, verifier, quota), services/cron/ | l'asynchrone de la plateforme, les retries, la file morte | la generation : aucune ligne ne suit un rendu, et la video est pollee dans le tour |
| Argent | orchestrator/runtime/billing.ts, config/model-pricing.ts, Credit | le pre-check de solde, la ligne d'usage, le cout provider estime | la production de la plateforme (hors ledger), et le cout d'une video sur l'asset (Map en memoire, jamais remplie pour la video) |
| Distribution | services/growth/postiz.ts, features/growth/postiz-draft.ts, ContentRender, ContentReview | le brouillon par canal, l'approbation humaine, la preuve de publication | qu'un store ait ses propres canaux : Postiz est une seule cle, celle de la plateforme |
| Mesure | services/growth/attribution.ts, lib/datafast-api.ts, AttributionEvent, CreativeLearning | l'attribution observee, l'apprentissage par concept et par variante | le maillon generation → post → canal |
| Connaissance | features/ai/skills/, knowledge-tools, memory/, prompts/kernel.ts, .claude/skills/ | les trois niveaux de contexte du chat (modele, ecosysteme, utilisateur) | le niveau 1 seul du noeud aiText du canvas (ai-platform/2777) |
| Surfaces | config/admin-routes.ts (categorie creative, 6 outils), features/studio/ops-routes.ts, ~/studio/*, [storeSlug]/studio | qui ouvre quoi, avec quelle permission | qu'une seule des trois fabriques soit atteignable par un non-admin |
La frontiere Founder / Store, couche par couche
La plateforme n'est nommee par aucun identifiant, et ce n'est pas un
oubli : PLATFORM_ORG_ID et Organization.kind ont ete rejetes trois
fois (acces-delegue-et-frontiere-studio.md, ADR 0013, ADR 0024). Le
profil existe comme un contexte derive a trois couches :
| Couche | Ce qui porte la frontiere | Ou |
|---|---|---|
| identite | User.role === "ADMIN", relu en base, jamais depuis la session | lib/security/platform-admin.ts |
| mandat | cinq platform.* sur un AccessGrant de portee platform, resolues par un membre synthetique delegate a plancher vide | lib/security/platform-permissions.ts |
| organisation | l'organisation interne = adhesion de l'admin union les organisations atteintes par un mandat portant la permission nommee | features/studio/internal-scope.ts |
Cote store, le scope tient a chaque couche, et par des mecanismes
differents (c'est la solidite et le cout) : le where Prisma
d'appartenance (garde par tenant-pages-scope-by-membership.test.ts), la
porte cross-tenant du chat (handler-gates.ts : storeAccess.orgId !== activeOrgId → 403), AtlasToolContext, les chargeurs Studio qui
prennent storeId dans le where, le prefixe Blob
stores/<storeId>/studio, le bearer MCP lie a un seul storeId, le
namespace vectoriel par store, et la frontiere /collab (le mandataire
n'entre pas cote user, garde par
collab-never-reaches-the-user-side.test.ts).
Trois trous, mesures, et ce sont eux que l'ADR 0024 ferme :
studio-references.tsautorise par hote, pas par scope :.public.blob.vercel-storage.comen entier, donc l'URL publique d'un asset d'un autre store passe l'allowlist. Aucun test ne prouve quegenerateImagedu store A ne peut pas referencer le store B.- Le prefixe
studio/unassigned: un rendu sans store est facture a l'organisation, ecrit publiquement, liste nulle part, supprimable par personne. Trois sites d'ecriture, zero lecteur. - La production de la plateforme est hors tenant : pas de
StudioAsset, pas de ligneCredit, pas de QC.loadQcBoard()sansorgIdreste d'ailleurs une signature qui embrasse la plateforme entiere, meme si plus aucun appelant de production ne l'utilise ainsi.
Deux gardes citees par le code n'existent pas sur le disque :
delegate-has-no-floor.test.ts (referencee par deux docblocks de
lib/security/) et surfaces-import-their-capabilities.test.ts
(annoncee par l'ADR 0023, par features/ai/tools/CLAUDE.md et par
ai-platform/2764). Une regle citee et absente est plus couteuse qu'une
regle jamais ecrite : elle se lit comme tenue.
Les capacites, et ou elles manquent
L'ADR 0023 a fait du registre src/features/ai/tools/ la liste unique :
26 familles, une etape par famille, un risque par outil, trois lecteurs
(createAtlasTools, describeAtlasTools, probeToolContext) et un
catalogue servi par store a GET /api/workflow/v2/capabilities.
Les sept capacites creatives n'y sont pas :
| Capacite | Construite dans | Atteignable depuis |
|---|---|---|
generateImage | handler-tools-build.ts:778 | le chat seul |
composeProductImage | :802 (composite serveur, la seule qui produise une preuve de fidelite) | le chat seul |
generateVector | :859 (le modele ecrit le SVG, aucun moteur image) | le chat seul |
generateVideo | :1336 (Veo 3.1 Fast, synchrone, 180 s) | le chat seul |
saveStudioAsset | :1439 | le chat seul |
planCreativeVariants | :1925 | le chat seul |
saveCreativeConcept | :2089 | le chat seul |
Et le rendu Remotion (launchTemplateRender) n'est atteignable que par
deux server actions, l'une requireAdmin, l'autre
platform.content.operate. La matrice cible, capacite produit contre
implementations :
| Capacite produit | Aujourd'hui | A ajouter | Jamais |
|---|---|---|---|
| image depuis texte / references | Nano Banana Pro (Gateway) | — | — |
| produit exact dans une scene | composeProductImage (composite serveur) | — | un modele generatif seul : la fidelite est le produit |
| clip court depuis une image | Veo 3.1 Fast (Gateway), synchrone | asynchrone (job) | — |
| demonstration du produit | Remotion, deterministe (ADR 0017) | Remotion comme capacite du registre | un provider generatif : l'ADR 0017 a mesure pourquoi |
| mouvement d'un personnage | rien | a evaluer sur Veo (le Gateway sert deja l'image d'ouverture) ; sinon planned, nomme et desactive | un fournisseur generatif ajoute pour l'obtenir |
| remplacement d'un personnage dans une video | rien | idem | idem |
| scenes cinematiques enchainees | rien | un enchainement de clips composes par Remotion est le chemin le plus proche de nos moteurs | idem |
| voix off alignee | ElevenLabs, hors ledger, une seule voix | voix par identite, cout compte | — |
| vecteur editable | le modele ecrit le SVG | — | un raster qui ressemble a du vectoriel |
L'identite visuelle : le mot est deja pris
Dans ce depot, avatar designe l'avatar marketing, la cible d'une
creative : CreativeConcept.marketingAvatar (« "women 25-34" is not an
avatar »), StudioAsset.persona, l'axe de variante persona, le
plancher AVATAR_FLOOR = 2 du Go/No-Go, les « Avatar Cards » du skill
creative-ads. Et AgentPersona plus identity-registry.avatar
designent les agents IA. Trois occupants pour un mot.
Le personnage reutilisable du brief (le fondateur, un porte-parole, un personnage de marque, avec ses references, ses versions, ses attributs et sa voix) n'existe nulle part : aucun modele, aucun type, aucun outil, aucun ecran. Ce qui en tient lieu :
- une reference de generation est une position d'attachement ou une
URL sur allowlist, jamais un identifiant ; aucune grammaire
@n'existe cote Studio (resolveMention,parseMentions: zero occurrence) ; - le kit de marque (
StoreContext.modules.brandKit) porte nom, ton, audience, palette, typo, logo, et aucun personnage ; - la voix est une seule valeur d'environnement,
ELEVENLABS_VOICE_ID, pour toute la plateforme ; la doc promet « voix A ≠ voix C » que rien n'implemente ; StudioAssetKind.LIPSYNCn'a aucun producteur ;- le consentement de clonage vocal existe, mais dans un script CLI hors
application (
creative/pipeline/tts.mjs), sur un fichier du depot.
D'ou StudioIdentity (ADR 0024 §6) : un objet distinct, scope comme un
asset, reference par son identifiant, resolu par le meme resolveur, avec
son consentement. Deux questions restent ouvertes, de gouvernance :
le scope (store, comme StudioAsset, ou organisation,
pour qu'une agence reutilise un porte-parole sur plusieurs marques) et le
droit a l'image d'une personne reelle cote client.
Les surfaces : quinze pages, sur quatre racines gardees differemment
Ce n'est pas une constellation de pages inutiles : les chargeurs sont
deja partages (features/studio/boards.ts, board.ts, cockpit.ts
servent les pages d'organisation, les pages /admin, la QC de /ops, la
file personnelle ~/me, les outils du chat et la route REST). Le noyau
commun que la cible demande existe donc deja pour les boards. Ce qui
se duplique est ailleurs :
| Duplication mesuree | Preuve | Sortie |
|---|---|---|
| Deux formulaires pour la meme fabrique Remotion | admin/creative/generate/_components/generator.tsx (313 lignes) et ops/content/_components/creative-workshop.tsx (420 lignes) : les deux importent les memes dix symboles du meme template-catalog.ts : ce qui est duplique n'est donc pas le catalogue, deja unique, mais le formulaire autour (etat local, rendu des champs, validation d'affichage, bouton) | un composant unique dans features/creative/, parametre par l'action ; les deux portes restent differentes |
| Quatre boards internes en double | /admin/creative/{cockpit,pipeline,clients,concepts} sont les memes pages que ~/studio/*, avec canManage / canValidate forces a true au lieu d'etre derives | fusionner vers ~/studio/*, ou assumer /admin/creative comme « fabrique plateforme » (generate + library) et rien d'autre |
| Deux lecteurs de l'etat d'un rendu, par deux modules homonymes | listRenderRuns de services/creative/render-dispatch.ts (lu par admin/creative/generate) et listRenderRuns de services/growth/creative-render.ts (lu par ops/growth) : deux fonctions du meme nom, dans deux modules paralleles, pour un fait qui vit sur l'API GitHub. ToolStateStrip n'en est pas un : il affiche de la configuration et un lien sortant | une table de jobs, et les deux lisent la meme ligne |
| Deux surfaces Postiz | ops/content (file de relecture, ContentReview) et ops/growth (rendus, media desk, ContentRender) ecrivent chez Postiz avec deux modeles | une file de distribution partagee ; ops/growth garde l'amont (unite → rendu) |
| Trois galeries | GeneratedGrid (Blob nu), StudioAssetGrid (Prisma), StudioMediaCard (resultat d'outil) | une carte d'asset, une galerie, sur la forme StudioAsset |
| Deux sources de tokens de marque | creative/src/tokens.ts ecrit ses couleurs, rayons, polices et courbes en dur (45 lignes, derivees de rien) ; src/styles/tokens.css porte les memes valeurs sous forme derivee | creative/src/tokens.ts derive du design system, en tranche 4 : c'est le moment ou la production de la plateforme rentre dans le produit |
Quelle racine devient le Studio de la plateforme ? La question est
tranchee par une contrainte que le depot porte deja : /admin est garde
au bord sur User.role === "ADMIN" (src/proxy.ts), donc une page
qui doit s'ouvrir a quelqu'un qu'on recrute ne peut pas y vivre. C'est
exactement pourquoi la QC creative a demenage vers /ops
(security-identity/0605). La fabrique suit le meme chemin : la porte
de production de la plateforme est /ops, avec une permission
platform.* ; /admin/creative garde ce qui est du ressort du
proprietaire seul. Ce qui est deja vrai a moitie (la QC est sous /ops,
la fabrique sous les deux) devient une regle.
Les moteurs, et pourquoi la liste est fermee
Six moteurs, et aucun autre : AI Gateway de Vercel (Nano Banana Pro
pour l'image, Veo 3.1 Fast pour la video, un seul catalogue dans
config/ai-models.ts), ElevenLabs (voix), Remotion (composition
deterministe, ADR 0017), Playwright (creative/pipeline/capture.mjs,
sur une allowlist : c'est la marche « produit reel » de la hierarchie de
l'ADR 0017, celle qui passe avant toute imitation), Postiz
(distribution), Datafast (mesure).
Les captures qui ont servi de reference viennent d'un studio generatif du marche. Elles sont une reference d'ergonomie : la forme des controles, la facon de poser un slot, la maniere de dire une limite. Pas un fournisseur. Tranche le 2026-09-18, et c'est coherent avec l'ADR 0017 qui avait deja refuse un moteur generatif pour montrer le produit.
Ce que cela change pour l'architecture, et c'est le point utile :
l'abstraction provider ne sert pas a ajouter des fournisseurs. Elle
sert a ce qu'une capacite produit (« anime ce personnage ») soit nommee
une fois, et que l'UI, le canvas, le MCP et l'API ne sachent pas si elle
est servie par une option de Veo, par une composition Remotion, ou par
rien. Les trois capacites decrites par les captures et que nos moteurs ne
servent pas aujourd'hui (mouvement d'un personnage, remplacement d'un
personnage dans une video source, enchainement de scenes) sont donc
planned : visibles, desactivees, avec leur raison. C'est exactement ce
que the-studio-runs-what-it-declares impose deja au canvas, et ce que
le depot refuse de faire autrement (un controle qui semble agir sans
agir).
Les ecarts
Les inventaires de session ne sont pas versionnes, donc leurs totaux ne
sont derives de rien et ne sont pas repris ici : ce qui tient dans ce
document est une preuve fichier:ligne par ligne, verifiable au clone.
Le tableau garde les entrees qui
decident du plan. La colonne « confirme par » compte les lecteurs
independants qui sont arrives a la meme conclusion depuis un angle
different, ce qui vaut mieux qu'un vote unanime d'un seul.
| Ecart | Statut | Preuve | Confirme par | Ce qu'il faut |
|---|---|---|---|---|
| Les sept capacites media sont hors du registre | MISPLACED | handler-tools-build.ts:778, 802, 859, 1336, 1439, 1925, 2089 ; absentes de TOOL_FAMILIES (tools/index.ts:119-231) ; le catalogue du canvas derive de describeAtlasTools et ne les voit pas | chat, generation, MCP, surfaces (4) | une famille studio-media, et le contexte qui leur manque (attachements, facturation) sur AtlasToolContext |
| Aucune ligne ne suit une generation | MISSING | generateVideo polle 180 s dans un tour de 300 s ; dispatch() GitHub rend 204 sans identifiant de run ; aucun modele Prisma de job | generation, jobs, Remotion, MCP (4) | GenerationRequest + les jobs QStash existants |
| Aucune abstraction provider | MISSING | import { experimental_generateVideo, generateImage, generateText } from "ai" appele avec un id code ; render.mjs shelle npx remotion ; voiceover.ts fetch ElevenLabs en direct | generation, Remotion, MCP (3) | capacite produit → routeur → adaptateur ; ai-models.ts reste le seul catalogue |
| La plateforme n'est pas un tenant de son Studio | NEEDS_REFACTOR | launchTemplateRender et listGeneratedAssets n'ont aucune notion de tenant ; Blob creative/library/ sans ligne ; deux definitions de « Founder » cohabitent (l'org de l'admin pour les boards, aucun tenant pour Remotion) | tenancy, surfaces, assets, Remotion (4) | le point 4 de l'ADR 0024 |
| Les references sont autorisees par hote, pas par scope | NEEDS_REFACTOR | studio-references.ts:57 ouvre .public.blob.vercel-storage.com en entier ; aucun storeId dans resolveReferences | generation, tenancy (2) | resoudre par identifiant d'asset dans le scope, et un test croise |
studio/unassigned : rendu paye, sans proprietaire | NEEDS_REFACTOR | trois sites d'ecriture (handler-tools-build.ts:620, 733, 899), zero lecteur, aucune purge | generation, tenancy, assets (3) | refuser la generation sans store |
| Le cout d'un asset se perd | EXISTS_PARTIALLY | recentImageCost est une Map de 32 entrees en memoire, donc perdue sur une autre instance ; jamais remplie pour la video, donc generationCostUsd est toujours nul pour un clip | generation, assets, credits (3) | lire le cout depuis la ligne Credit, pas depuis la memoire du processus |
| La production de la plateforme est hors ledger | MISSING | minutes de runner et caracteres ElevenLabs n'apparaissent nulle part ; trackStudioMediaUsage rend null sans orgId | credits, surfaces, Remotion (3) | billing/0613, decision de gouvernance |
| Aucune identite visuelle reutilisable | MISSING | aucun modele, aucun type, aucun outil, aucun ecran ; « avatar » designe deja trois autres choses | avatar (exhaustif) | StudioIdentity (ADR 0024 §6) |
| Une seule voix pour toute la plateforme | MISSING | ELEVENLABS_VOICE_ID, une valeur d'environnement lue par voiceover.ts et tts.mjs ; la doc promet « voix A ≠ voix C » | voix, avatar (2) | une voix par identite, et son cout |
| Aucune suppression propagee | MISSING | studioAsset.delete n'a aucun appelant ; supprimer un store laisse stores/<id>/studio/ public | assets | une action, une purge, un cron |
| Aucune regeneration partielle, aucun verrou de generation | MISSING | zero occurrence de regenerat* dans features/studio, services/creative et orchestrator/runtime (les 94 occurrences de src/ sont la regeneration d'un message de chat et celle d'un jeton de listing, sans rapport avec un rendu) ; le seul locked est un verrou d'AFFICHAGE d'un noeud de canvas, ignore par l'executeur | assets, chat, Remotion (3) | depend de GenerationRequest : sans parametres persistes, on ne peut meme pas rejouer |
| Aucun versioning de recette | MISSING | templates.json n'a ni version ni hash ; les presets sont des constantes ; Workflow.version est un compteur sans contenu | Remotion, assets, chat (3) | versionner ce qui produit, pas ce qui affiche |
| Trois galeries, deux formulaires, trois lecteurs d'etat de run | REDUNDANT | GeneratedGrid / StudioAssetGrid / StudioMediaCard ; generator.tsx 313 lignes contre creative-workshop.tsx 420 ; RecentRuns / ToolStateStrip / tuile Remotion | UI, surfaces (2) | une carte, une galerie, un formulaire, une table de jobs |
| Aucun test de regression visuelle | MISSING | ni playwright.config.*, ni .storybook, ni toHaveScreenshot nulle part dans le depot (Playwright y est, mais pour le scan et les captures creatives, pas pour tester une interface) | UI | prerequis a la parite d'interaction, avec les captures |
| Deux gardes citees sont absentes du disque | MISSING | surfaces-import-their-capabilities.test.ts (ADR 0023, tools/CLAUDE.md, ai-platform/2764) et delegate-has-no-floor.test.ts (deux docblocks de lib/security/) | chat, tenancy (2) | les livrer, ou retirer la citation |
| La distribution est mono-tenant | MISSING | POSTIZ_API_KEY est une valeur d'environnement unique (src/env/server.ts) : une seule cle, celle de la plateforme. Un store n'a aucun canal a lui, et rien n'empeche structurellement un rendu de store d'atterrir sur les comptes de la plateforme, sinon le fait que le chemin n'existe pas encore | distribution, surfaces (2) | la clé devient la connexion d'un tenant, en tranche 4, avant que la tranche 8 ne fasse partir quoi que ce soit d'un store |
| La QA deterministe n'existe pas | MISSING | rien ne compare le fichier produit a ce qui a ete demande : ni dimensions, ni duree, ni fps, ni zone de securite. Les specs existent pourtant (creative/src/tokens.ts declare width, height, fps, safeZone par toile) et ne sont jamais confrontees a la sortie | Remotion, assets (2) | derivable des la tranche 2 : la ligne porte les parametres demandes ET l'artefact produit, donc la comparaison devient une garde |
| La QA semantique n'existe pas | MISSING | zero occurrence de « semantique » dans les chemins de generation : rien ne verifie que l'image montre le bon produit, le bon prix, la bonne marque. La seule preuve de fidelite est composeProductImage, et elle n'est pas persistee | assets, generation (2) | tranche 5 ou 6, en partant de la preuve de fidelite qui existe deja |
| La generation n'est gardee que par le solde | EXISTS_PARTIALLY | canAffordStudioMedia (billing.ts) est le seul controle avant une generation, appele en trois points ; les quatre resolveurs d'entitlement (plan, credits, drapeaux, permissions) ne la couvrent pas | tenancy, credits (2) | une capacite declare son entitlement en meme temps que son risque, tranche 1 |
Le noeud aiText du canvas n'a qu'un niveau de contexte | PLANNED | generateText({ model, prompt, system }) sans kernel, sans regles, sans memoire ; l'ADR 0023 le nomme deja | chat, connaissance (2) | ai-platform/2777 |
Les entrees non reprises ici (menage local, prose perimee, doublons de lecteurs) restent dans les inventaires de la session ; elles ne changent pas le plan.
Ce qui se renomme, se fusionne ou disparait
| Geste | Pourquoi | Risque, et ce qui le tient |
|---|---|---|
Les sept outils media vers features/ai/tools/studio-media.ts | c'est la cause structurelle de l'absence de parite | le chat doit continuer a fournir attachements et facturation : studio-write-parity.test.ts est le modele du test a ecrire |
studio/unassigned disparait | un rendu paye sans proprietaire | refuser tot ; le chat dit deja « pas de store, pas de video » pour la video, on etend a l'image |
recentImageCost disparait | une Map en memoire sur du serverless | lire Credit ; le cout devient exact au lieu d'etre absent une fois sur deux |
Un composant de fabrique unique dans features/creative/ | deux formulaires pour un catalogue | les portes restent differentes (requireAdmin contre platform.content.operate) |
StudioAssetKind.LIPSYNC retire | aucun producteur, aucune route | l'enum est en base : retrait au schema seulement quand plus rien ne le lit |
commerce-os-registry.ts et commerce-system-registry.ts retires | plus aucun importateur depuis la moitie cliente de 2764 | ai-platform/2773 les cite comme « source de ce qu'une porte doit exiger » : il doit deriver de resolveToolRisk a la place |
GET /api/workflow/v2/platform, POST /context, GET /templates retires | aucun appelant ; publient des registres a la main | c'est l'oracle que l'ADR 0023 refuse |
creative/src/briefs/load-u18.ts retire | aucun appelant | — |
L'import de Root.tsx vers .claude/skills/ coupe | une dependance Remotion vers un dossier de skill d'agent | deplacer le brief dans creative/ |
services/growth/creative-render.ts retire | troisieme chemin de rendu, vocabulaire propre | quand le plan Q4 passe par templates.json |
ledger.json du pipeline CLI retire | jamais rempli en production, double l'AdminAuditLog | — |
Ce qu'on clone, composer par composer
La consigne est explicite : on reproduit ce que ces studios font tous, on n'utilise aucun d'eux. Ce qui se clone est la forme des controles, la facon de poser un slot, la maniere de dire une limite, l'endroit ou vit le cout. Ce qui reste le notre est le moteur, le nom de la capacite et la porte.
La regle qui gouverne toute cette section, prise telle quelle du brief : l'interface est definie par la capacite, jamais par un formulaire universel. Pas d'invite quand la capacite n'en accepte pas ; pas de duree manuelle quand la sortie suit sa source ; pas de selecteur de format quand la sortie suit sa source. Un controle qui ne pilote rien est la faute que ce depot corrige deja ailleurs (la toolbar de la preview rendait ses commandes dans les quatre modes et trois d'entre eux n'agissaient sur rien).
Sept composers, et ce que nos moteurs en servent reellement :
| Composer de reference | Ses controles | Notre chemin | Verdict |
|---|---|---|---|
| Image | onglets de mode, references image optionnelles, invite, selecteur de modele (nom, objet, cout, selection), format, enregistrer, Generer + cout | Nano Banana Pro sur le Gateway ; les references (jusqu'a six) et les six formats sont deja des schemas zod ; le cout par image est deja tarife et affiche dans le menu « + » | clonable tout de suite : tout existe cote serveur, rien cote ecran |
| Video (qualite) | image d'ouverture, « decrivez le mouvement », qualite, duree, enregistrer, Generer + cout | Veo 3.1 Fast ; l'image d'ouverture est deja referenceImage / referenceUrl ; la duree est deja 4, 6 ou 8 ; le cout est deja par seconde | clonable tout de suite |
| Video cinematique + Scenes | image d'ouverture, image de fin (mono-scene), invite, duree, audio, bascule Scenes, puis de deux a cinq plans avec invite et duree propres, somme validee, audio impose | l'image de fin a un chemin : frameImages accepte first_frame et last_frame, et src/test/ai-sdk-video-frames.test.ts l'assere au compilateur ; l'outil ne l'expose simplement pas. Le multi-plan est N clips Veo assembles par Remotion, chacun avec son invite et sa duree : c'est deterministe, c'est notre moteur, et c'est la piece la plus interessante du lot | clonable, et c'est celle qui vaut le plus |
| Multi-entrees (type Seedance) | debut, fin, images, videos, audio, invite, modele, resolution, format, duree, audio, cout ; les limites affichees dans le slot | inputReferences est typee pour des entrees image et video ; l'audio est genere par Veo lui-meme ; la resolution n'est deliberement jamais passee (borne economique documentee dans model-pricing.ts) | partiel : les slots et l'affichage des limites se clonent ; l'audio en ENTREE et la resolution demandent une decision |
| Reference par mention (type Nova) | videos, images, debut, fin, invite avec « tapez @ pour referencer un element sauvegarde », resolution, vitesse, format, duree | la grammaire @ est la piece la plus utile a cloner, et elle est entierement notre : elle depend des elements sauvegardes (ci-dessous), pas d'un fournisseur. La vitesse est un retiming, que Remotion fait de maniere deterministe | clonable : mentions plus retiming Remotion |
| Remplacement de personnage | video source, une a quatre photos de personnage, aucune invite, resolution, vitesse, temps estime | aucun de nos six moteurs ne sert le remplacement d'un personnage dans une video source | planned : nomme, visible, desactive, avec sa raison |
| Transfert de mouvement | image de personnage, video de mouvement de trois a trente secondes, modele, orientation (« suivre le cadrage de la video » ou « conserver le cadrage de l'image ») | aucun moteur | planned, meme traitement. A noter pour le jour ou : l'orientation se presente en mots, jamais en enum technique, et cette exigence-la est deja un principe du depot |
Deux consequences qu'on ne veut pas manquer :
- Cinq composers sur sept sont servables par nos moteurs, et deux d'entre eux ne demandent que d'exposer ce que le code type deja (l'image de fin) ou de composer ce que Remotion sait faire (le multi-plan, le retiming). Le clone n'est donc pas un exercice d'imitation : c'est surtout du cablage.
- Les deux qui restent sont declares, pas caches. Une capacite
plannedqui dit pourquoi vaut mieux qu'une absence : c'est la difference entre « le produit ne sait pas encore » et « le produit ne sait pas ».
Les elements sauvegardes, et la grammaire @
C'est le systeme que les trois composers a mentions supposent, et il n'existe pas : aucune grammaire de mention cote Studio, aucune table d'element sauvegarde. Les types que le brief enumere, confrontes a ce que le depot porte deja :
| Type d'element | Ce qui existe deja | Ce qu'il faut |
|---|---|---|
| produit, image de produit | le catalogue Shopify, autorise comme reference | un identifiant, pas une URL |
| logo, actif de marque | StoreContext.modules.brandKit (logoUrl, faviconUrl) | l'exposer comme element referencable |
| capture, etat d'interface | creative/captures/ via Playwright, sur allowlist | remonter dans la bibliotheque |
| actif genere | StudioAsset | deja la, il lui manque d'etre adressable par mention |
| image de debut, image de fin, reference de style, reference de mouvement | des roles, pas des objets : un meme actif joue l'un ou l'autre selon le slot | le role appartient au slot, l'objet a la bibliotheque |
| personne, porte-parole, fondateur | rien | StudioIdentity (tranche 6) |
| voix, musique, audio | ELEVENLABS_VOICE_ID, une valeur globale | une voix par identite, et un actif audio |
La regle qui en sort : un element sauvegarde est un actif de la bibliotheque plus un role donne par le slot. Pas une seconde taxonomie. C'est ce qui evite d'avoir a inventer un objet « StartFrame » distinct d'une image.
La definition d'une capacite
Le brief propose une forme, et le depot en porte deja les trois quarts,
eparpilles. Ce que la famille studio-media (tranche 1) doit rassembler,
avec pour chaque champ ce qui le tient aujourd'hui :
| Champ | Ou il vit deja | Ce qui manque |
|---|---|---|
| identifiant, nom affiche, description | le nom de l'outil, sa description zod | rien |
| modalite, tache | implicite dans le nom | explicite, pour que l'UI groupe |
| moteur, capacite du moteur | config/ai-models.ts | le lien capacite → moteur |
| slots d'entree | les schemas zod (referenceImages, referenceUrl…) | le vocabulaire de slot, aujourd'hui inexistant |
| controles | les schemas zod (aspectRatio, durationSeconds, placement, scenePreset) | leur forme d'affichage |
| contraintes | MAX_REFERENCE_IMAGES, VIDEO_DURATIONS, MAX_VIDEO_STYLE_REFERENCES, MAX_REFERENCE_BYTES | qu'elles soient lisibles par l'UI plutot que seulement appliquees |
| cout estime | model-pricing.ts, canAffordStudioMedia | rien |
| latence estimee | rien | utile pour le composer de remplacement, qui l'affiche |
| disponibilite | workflow-capabilities.ts (available, planned) et capability-catalogue (needs-connection) | l'etendre a la generation |
| droits, portee d'execution | tool-permission-matrix.ts, platform.*, studio.* | rien |
| validation, adaptateur | zod ; l'appel direct au SDK | l'adaptateur (tranche 5) |
L'interface du Studio se derive de ces definitions. C'est exactement ce
que le canvas fait deja pour les 87 autres capacites : son formulaire de
noeud est genere depuis l'inputSchema. La generation rejoint ce
mecanisme au lieu d'en inventer un.
Les slots, comme primitive
Huit slots nommes par le brief, zero composant dans le depot. Chacun doit connaitre ses types acceptes, son nombre maximum, sa taille, sa duree maximum, sa portee (le scope, c'est la tranche 3), son etat de televersement, son apercu, son retrait, son remplacement, et le selecteur d'actif deja sauvegarde.
C'est la primitive qui manque le plus, parce que les sept composers ne different presque que par elle : image d'ouverture, image de fin, references image, references video, references audio, personnage, video source, video de mouvement.
Le vocabulaire de mouvement
Le brief enumere dix-sept primitives de motion design (mise au point,
deplacement de curseur, clic, revelation, decalage, modale, panneau,
surlignage, bulle, frappe de texte, revelation de graphique, compteur,
element partage, transition de scene, sous-titre, cadre de navigateur,
cadre d'appareil). Le depot en porte dix, et aucune de ces
dix-sept par son nom : SafeZone, HookText, KineticText,
ProductWindow, BrandBackground, Glow, Notification, Chart,
CTA, ChapterCard.
Le recouvrement est reel mais partiel : KineticText couvre la frappe de
texte, Chart la revelation de graphique, ProductWindow s'approche du
cadre de navigateur. Manquent surtout celles qui montrent un PRODUIT en
action : mise au point, curseur, clic, element partage entre deux plans.
C'est ce qui separe une video « jolie » d'une demonstration, et c'est
exactement la marche que l'ADR 0017 place en haut de sa hierarchie.
La matrice ecrans : un squelette, et ce qui lui manque
Les captures de reference ne sont pas dans le depot. Cette matrice
est construite sur les composers decrits textuellement par le
proprietaire et sur les primitives qui existent. Elle dit ce qu'il faudra
verifier, pas ce qui est decide. Les versionner (sous
docs/references/creative-composers/) est un prerequis nomme de la
parite d'interaction, pas une formalite.
Etat des primitives, mesure :
| Primitive cible | Ce qui existe | Verdict |
|---|---|---|
StudioShell | rien ; voisin : ~/studio/layout.tsx + studio-nav.tsx | manquant |
StudioToolbar | PromptInputToolbar (chat), PanelToolbar (role="toolbar") | a composer |
StudioComposer | le composer du chat, deja eclate en cinq fichiers composer-*.tsx (environ 1 955 lignes) sur la primitive prompt-input.tsx, monte par ai-chat.tsx (2 116 lignes) | a composer, pas a casser : les morceaux existent |
CapabilityTabs | MediaToggleRow + SegmentedControl, quatre etats, tarif en HoverCard | partiel, et l'etat vit en localStorage |
InputSlot (start, end, refs image, refs video, audio, personnage, video source, video de mouvement) | aucun composant ; cote serveur seulement, en positions et URLs | manquant, c'est la piece centrale |
ParameterChip / ParameterPopover | Chip, StatusBadge, ui/popover, HoverCard | a composer |
GenerateButton | PromptInputSubmit, ComposerRightActions (dont blockedByCredits) | a generaliser |
GenerationStatus | toolPart.state, MessageToolRun, Spinner ; ToolProgress reserve aux scans | partiel, et sans etat reel a afficher tant qu'aucune ligne ne suit un rendu |
AssetCard / StudioGallery | StudioMediaCard, GeneratedGrid, StudioAssetGrid : trois | a unifier |
AssetPicker | rien : aucun selecteur d'asset existant a reutiliser | manquant |
RunCard | MessageToolRun, les elements Tool* d'ai-elements | a composer |
InspectorDrawer | ui/sheet, StoreSettingsSheet comme modele | disponible |
Les etats a couvrir, pour chaque composer : survol, selection, desactive, televersement en cours, au-dela de la limite, solde insuffisant, en generation, echoue, termine. Aucun n'est teste visuellement aujourd'hui.
Les trois composers decrits, et ce que la codebase peut en tenir :
| Composer | Controles decrits | Ce qui existe deja | Ce qui manque |
|---|---|---|---|
| Replace (video source + une a quatre references personnage, sans invite) | deux slots, resolution, vitesse, temps estime | rien | les deux slots, la capacite, un provider, l'estimation |
| Motion (image personnage + video de mouvement de trois a trente secondes, orientation) | deux slots, modele, selecteur d'orientation avec sa description en clair | rien | idem, plus le vocabulaire d'orientation en mots, pas en enum |
| Cinematic > Scenes | de deux a cinq scenes, invite et duree par scene, somme egale a la duree globale, audio impose, pas d'image de fin | rien | un vrai mini-compositeur de plans, pas un interrupteur |
La regle qui en sort : l'UI est derivee de la capacite, jamais un formulaire universel. Un controle que la capacite n'accepte pas ne s'affiche pas ; une limite s'affiche dans le slot (« jusqu'a 4 », « 30 s max ») ; et rien n'affiche un pourcentage que personne ne connait.
Les tranches
Chaque tranche est verticale (elle produit un resultat observable), elle reutilise le maximum, et elle nomme sa garde. L'ordre est celui des dependances, pas celui de l'envie.
Tranche 1 : la generation entre dans le registre
Resultat observable : depuis le chat, rien ne change ; depuis le
canvas, un noeud « Generer une image » s'execute reellement ; depuis le
catalogue servi par store, generateImage apparait avec son etat.
| Pilier | ai-platform |
| Fichiers | features/ai/tools/studio-media.ts (nouveau, recoit les fabriques derriere une interface d'adaptateur unique), tools/index.ts (une famille), tools/types.ts (AtlasToolContext gagne attachements et facturation), orchestrator/runtime/handler-tools-assembly.ts (devient un appelant), workflow/v2/lib/workflow-capabilities.ts |
| Reutilise | tout : les fabriques ne sont pas reecrites, elles demenagent ; wrapToolsWithAutonomy, resolveToolRisk, capability-catalogue fonctionnent par derivation |
| L'abstraction provider commence ici | c'est la tranche qui la livre, et rien d'autre ne la livrerait : les sept fabriques passent derriere UNE interface (une capacite produit, un adaptateur), et renderComposition (tranche 4) puis la voix (tranche 6) deviennent le deuxieme et le troisieme adaptateur de la meme interface. Sans cela, l'ADR 0024 point 3 resterait une phrase |
| Entitlement | une capacite studio-media declare son droit de plan en meme temps que son risque : aujourd'hui la generation n'est gardee que par le solde, jamais par un entitlement |
| Gardes | surfaces-import-their-capabilities.test.ts (celle que l'ADR 0023 annonce et qui n'existe pas), the-studio-runs-what-it-declares etendu, un test de parite chat contre canvas sur le modele de studio-write-parity, et check-model-catalogue.mjs etendu aux adaptateurs |
| Taille | L |
Tranche 2 : une generation est une ligne, et un job
Resultat observable : un clip de huit secondes qui met quatre minutes arrive ; l'operateur voit « en cours » puis le fichier ; rien n'est facture si rien n'est livre.
| Piliers | data-platform (le modele), ai-platform (les appelants) |
| Fichiers | prisma/schema.prisma (GenerationRequest, additive, aucune colonne requise sans defaut sur un modele existant), services/jobs/types.ts + un handler, handler-tools-build.ts (ecrit la ligne avant l'appel), billing.ts (le cout se lit sur la ligne, recentImageCost disparait) |
| Reutilise | QStash, la DLQ, le verifier, deduplicationId pour l'idempotence |
| Ce que la tranche doit refermer en passant | le plafond de depense. Sortir la generation du tour invalide le seul plafond dynamique existant (le hard cap mi-stream, lie au tour) : le solde doit etre reverifie au depilement, pas seulement a l'enfilement, sinon N requetes en file depassent le solde ensemble. La QA deterministe devient derivable ici : la ligne porte les parametres demandes ET l'artefact produit, donc comparer dimensions, duree, fps et zone de securite est une garde et non un voeu. La suppression propagee aussi : c'est le premier endroit ou le chemin Blob d'un artefact est connu en base, donc la purge se derive au lieu d'etre un balayage de prefixe |
| Gardes | pas d'appel provider sans ligne prealable ; pas de ligne Credit de canal studio sans requete ; N requetes en file ne peuvent pas depasser le solde ; le fichier produit correspond aux parametres demandes ; le plafond not-null-column-adds monte de ses colonnes neuves, chacune nommee |
| Taille | L |
Tranche 3 : le scope, ferme
Resultat observable : le store A ne peut plus referencer un asset du store B ; un rendu sans store est refuse avant la depense.
| Piliers | ai-platform, security-identity |
| Fichiers | studio-references.ts (resolution par identifiant dans le scope), handler-tools-build.ts (studio/unassigned retire) |
| Gardes | tests de scope croise, dans src/test/ : store A contre store B sur la reference, la sauvegarde et la lecture ; un mandat de store n'ouvre aucune capacite du profil plateforme |
| Taille | M |
Tranche 4 : la plateforme devient un tenant de son Studio
Resultat observable : un rendu Remotion de la plateforme apparait dans une bibliotheque avec son cout, son statut de relecture et son concept, comme celui d'un client.
| Piliers | growth-web (creative/, la doctrine), app-shell (les surfaces), billing (le compte) |
| Fichiers | services/creative/render-dispatch.ts (devient une capacite renderComposition), generated-assets.ts (transition), creative-media-os.md §2 (la doctrine passe de trois planes a un moteur et des scopes), le composant de fabrique unique |
| Bloque par | billing/0613 : comment distinguer la production interne du revenu consomme est une decision de gouvernance |
| Gardes | aucun if (isPlatformAdmin) dans une capacite studio-media ; le store de la plateforme se resout par internal-scope.ts, jamais par une constante |
| Taille | L |
Tranche 5 : les capacites que nos moteurs ne servent pas, nommees
Resultat observable : « anime ce personnage avec ce mouvement » repond « pas encore, voici pourquoi » au lieu de ne rien dire ; et ce que Veo ou Remotion savent reellement faire est branche.
| Pilier | ai-platform |
| Prerequis | les tranches 1 et 2 |
| Travail | evaluer ce que Veo sert au-dela de l'image d'ouverture (references de style, image de fin : le SDK les type, le tool ne les expose pas) ; evaluer un enchainement de clips composes par Remotion ; declarer planned ce qui reste, avec sa raison |
| Fichiers | tools/studio-media.ts, workflow-capabilities.ts, ai-models.ts |
| Gardes | une capacite planned ne s'execute pas et le dit ; un champ invalide est refuse avant tout appel ; la description derive du catalogue |
| Ce que la tranche ne fait pas | ajouter un fournisseur. La liste est fermee, et l'ouvrir est une decision de gouvernance, pas une tranche |
| Taille | M |
Tranche 6 : l'identite visuelle
Resultat observable : « genere avec @<identite> » produit le meme personnage deux fois ; sa voix le suit.
| Piliers | data-platform, ai-platform |
| Fichiers | StudioIdentity et ses references, studio-references.ts (resolution par identifiant), voiceover.ts (une voix par identite) |
| Bloque par | le scope (store ou organisation) et le consentement : deux decisions de gouvernance |
| Taille | L |
Tranche 7 : la surface
Resultat observable : les composers decrits existent, un seul formulaire, une seule galerie, une carte d'asset.
| Piliers | design-system, app-shell |
| Prerequis | les captures versionnees ; les tranches 1 et 2 (sans etat reel, GenerationStatus n'a rien a montrer) |
| Fichiers | features/creative/ (composer, slots, galerie), les trois galeries fusionnees |
| Gardes | regression visuelle (a installer), etats couverts |
| Taille | L |
Tranche 9 : rejouer, verrouiller, versionner
Resultat observable : « refais exactement ca pour la feature B » fonctionne ; « ne change plus cette image » tient ; une recette qui bouge ne reecrit pas le passe.
Six exigences du brief tiennent ensemble parce qu'elles ont toutes le meme prerequis, et c'est pour cela qu'elles sont une tranche et non six : sans les parametres d'une generation en base (tranche 2), on ne peut ni rejouer, ni verrouiller ce qui a produit, ni dire avec quelle version d'une recette un artefact a ete fait.
| Piliers | data-platform, ai-platform |
| Prerequis | tranche 2, strictement |
| Couvre | regeneration partielle (rejouer une branche sans rejouer le reste), verrous (un actif approuve ne se fait pas ecraser par une resauvegarde sur la meme URL), versioning des recettes (templates.json et les presets n'ont ni version ni empreinte), presets (aujourd'hui des constantes de code, non editables, non scopees), comparaison de variantes (le lien existe en base, aucune vue ne le montre), historique et rejeu |
| Taille | L |
Tranche 8 : la boucle
Resultat observable : une hypothese produit des creatives, elles partent par canal, ce qui revient se rattache a l'hypothese.
| Piliers | growth-web, intelligence |
| Prerequis | les tranches 2 et 4 (sans ligne de generation, le maillon manque) |
| Taille | L |
Le master prompt, section par section : ou chaque exigence vit
Le brief compte 105 sections. Un audit qui n'en couvre que les plus visibles laisse croire que le reste est prevu. Ce tableau dit, groupe par groupe, ou l'exigence est portee (une section de ce document, un point de l'ADR 0024, un item), et ce qui n'est porte nulle part encore, avec l'endroit ou ca ira. Une lecture prealable vaut pour tout le tableau : la reference d'ergonomie du brief (le studio generatif tiers) est une reference, jamais un moteur. Ses sections 26 et 93 (lire sa documentation, en deriver une matrice provider) sont remplacees par la meme matrice derivee de NOS six moteurs, sur consigne du 2026-09-18 : ses modeles sont ceux que l'AI Gateway sert deja.
| Sections | Exigence | Ou elle vit | Etat |
|---|---|---|---|
| 0-2 | codebase first, cartographie EXISTS / PARTIAL / MISSING | ce document entier, 16 sous-systemes, chaque ligne avec preuve | couvert |
| 3-4, 92 | matrice ecrans, parite fonctionnelle, statut par capture | « La matrice ecrans » ; design-system/2785 | squelette : les captures ne sont pas dans le depot, la matrice se remplit quand elles y sont |
| 5-8 | un noyau, deux profils, Founder = jamais un super store | ADR 0024 points 4 et 5 ; « La frontiere Founder / Store » ; growth-web/2783, ai-platform/2782 | couvert |
| 9-11 | un moteur d'identite, deux surfaces, identite reutilisable | ADR 0024 point 6 ; « L'identite visuelle » ; data-platform/2784, decisions 2789 et 2790 | couvert, bloque par deux decisions |
| 12 | elements sauvegardes, grammaire @ | « Les elements sauvegardes » ; tranche 6 | couvert dans le plan, rien de livre |
| 13-14, 71 | le chat est le cockpit, manuel et agent sur le meme pipeline | ADR 0024 point 1 ; ai-platform/2780 | couvert : c'est LA tranche 1 |
| 15-22 | les sept composers a cloner | « Ce qu'on clone, composer par composer » ; 2785 | couvert, avec un verdict par composer |
| 23-24 | CapabilityDefinition, slots comme primitives | « La definition d'une capacite », « Les slots » ; 2780 porte la definition, 2785 les slots | couvert |
| 25, 28, 49 | UI → registre → service → routeur → adaptateur ; capacite produit ≠ capacite provider | ADR 0024 point 3 ; tranche 1 (« L'abstraction provider commence ici ») | couvert, sur une liste de moteurs fermee |
| 26-27, 93 | doc provider, ne pas inventer une API, matrice provider | remplacees : matrice de NOS moteurs, « Les moteurs » ; check-model-catalogue.mjs etendu aux adaptateurs | couvert, autrement que le brief le demandait |
| 29-30 | generation asynchrone persistee, pas de faux pourcentage | ADR 0024 point 2 ; data-platform/2781 ; contrainte « un etat reel, ou rien » dans 2785 | couvert |
| 31 | concurrence : limites provider, quotas par tenant, file intelligente | 2781, contrainte « plafond de concurrence par organisation » | partiel : QStash porte retries, delai et deduplication (services/jobs/client.ts), aucune notion de concurrence par organisation ni de limite provider. 2781 l'exige, rien ne le dessine encore |
| 32-33 | ledger de cout (brut / interne / credits / prix public), garde-fous de budget | « Argent » dans la carte ; Credit.metadata.providerCost existe ; 2781 (solde reverifie au depilement) ; decisions 2786, 2788 | couvert pour le ledger, partiel pour les plafonds par run et par campagne : un plafond par cycle est dans 2788, un plafond par run n'est ecrit nulle part |
| 34 | bibliotheque d'assets integree | « Trois galeries » dans les surfaces ; 2785 (une galerie sur StudioAsset) | couvert |
| 35-36 | lignee, graphe d'artefacts, regeneration partielle | le lien parent existe en base (StudioAsset.variantOfId) (« le lien existe en base, aucune vue ne le montre ») ; graphe multi-niveaux et regeneration par branche dans app-shell/2792 | couvert dans le plan ; le graphe est la tranche 9 |
| 37-39 | verrous, versioning des recettes, rejeu et duplication | app-shell/2792 | couvert dans le plan |
| 40 | etat de session structure (scope, objectif, assets, verrous, capacite active) | Conversation.metadata est un Json? non type, rien n'y range un etat Studio | pas encore : la tranche 2 pose la ligne de generation, l'etat de session qui la reference se dessine avec 2785 |
| 41 | carte de run (plan, etapes, cout, annuler, relancer) | agent-plan-card.tsx (parse une liste en prose), task.tsx, tool.tsx, queue.tsx existent dans patterns/ai-elements/chat/ | partiel : les briques existent, aucune ne lit une ligne de generation ; la carte de run du brief est leur composition sur GenerationRequest, tranche 7 |
| 42 | langage naturel vers capacites | @Atlas + le routeur de skills (features/ai/skills/router.ts) | couvert des que la famille est dans le registre (tranche 1) : le routage existe, la cible manque |
| 43-44 | verite produit plateforme, verite produit store | ADR 0024 point 4 (facts.md = connaissance du tenant plateforme) ; le Store lit le catalogue par les outils existants | couvert |
| 45-47 | Remotion moteur deterministe, produit reel d'abord, vocabulaire de mouvement | ADR 0017 ; « Le vocabulaire de mouvement » ; tranche 4 ; decision 2787 | couvert |
| 48 | ElevenLabs comme timing maitre | voiceover.ts, creative/pipeline/tts.mjs ; la voix comme horloge des captions est dans la carte, pas dans une tranche | partiel : la synthese existe, le pipeline script → timestamps → captions → coupes n'est dessine nulle part. Il revient a la tranche 4 quand Remotion est une capacite |
| 50, 54-56 | boucle strategie → hypothese → recette → generation → QA → distribution → mesure → apprentissage ; spec et recettes | tranche 8 ; GrowthUnit, ContentRender, planCreativeVariants (les huit termes North Star) existent | couvert dans le plan, la tranche 8 n'a pas d'item de travail avant 2781 et 2783 |
| 51-53 | registre de connaissance (source, auteur, confiance, licence, version), skills curatees | features/ai/skills/ existe (registre, routeur, acces) ; aucun champ de source, de licence ni de confiance | pas encore, et hors des neuf tranches : c'est un item a ouvrir apres la tranche 8, quand la boucle produit de quoi apprendre. Le dire vaut mieux que l'oublier |
| 57-58 | canaux par profil, Postiz, adaptation par canal | « la distribution est mono-tenant » (ligne d'ecart) ; ADR 0024 sur la regle 2 ; tranche 8 | partiel : un store n'a pas de canal, et c'est nomme |
| 59-62 | tracking, attribution (mesuree / derivee / estimee), apprentissage isole par store | AttributionEvent (observe seulement), cron outcome-attribution (mesure l'impact des AgentAction, pas d'une creative), GrowthUnit | partiel : l'attribution d'une creative a un resultat n'existe que par les huit termes que saveStudioAsset accepte ; l'isolation d'apprentissage par store est un principe de l'ADR, pas un mecanisme. Tranche 8 |
| 63-65 | QA deterministe, visuelle, semantique ; regression visuelle ; etats exacts | lignes d'ecart QA ; 2781 (QA deterministe derivable de la ligne) ; platform-ops/2793 ; 2785 (les etats) | couvert dans le plan, la QA semantique n'a pas d'item : elle est une capacite de plus dans le registre, apres la tranche 1 |
| 66-68 | acces Founder cote serveur, entitlements store, public-ready sans constante | ADR 0024 points 4 et 5 (internal-scope.ts, jamais un id) ; tranche 1 (entitlement) ; decision 2788 | couvert |
| 69 | retention et suppression propagee | data-platform/2791 | couvert dans le plan |
| 70 | piste d'audit des operations sensibles | AdminAuditLog sur les verdicts QC (features/studio/actions.ts), AuditLog sur l'autorisation (guard.ts) ; rien sur une generation couteuse, une publication ou une suppression | partiel : la tranche 2 ecrit une ligne par etat de generation, c'est la piste ; publication et suppression suivent avec 2791 et la tranche 8 |
| 72-75 | comparer, presets, historique, recherche | 2792 (comparer, presets, historique) ; la recherche globale a un registre de providers (lib/search/) sans provider Studio | partiel : un provider de recherche sur StudioAsset et GenerationRequest est une ligne dans 2785, ajoutee ci-dessous |
| 76-79 | secrets serveur seulement, observabilite structuree, idempotence, une seule file | serverEnv, le logger structure, deduplicationId, QStash seul broker ; 2781 (« ne pas creer un second broker ») | couvert |
| 80-82 | primitives partagees, pas une constellation de pages, un centre de commande sobre | « Les surfaces » (quinze pages, quatre racines) ; 2785 ; /ops/content existe deja comme cockpit | couvert |
| 83-86 | ne pas sur-agentiser, le code pour les regles, controle humain, pas de regeneration gaspillee | ADR 0023 (un orchestrateur + outils) ; 2792 (verrous, rejeu) ; 2781 (rien n'est refait parce que le chat a recharge) | couvert |
| 87-88 | renommer sans casser, migrations non destructives | « Ce qui se renomme, se fusionne ou disparait » ; le schema guard additif, not-null-column-adds | couvert |
| 89 | tranches verticales, la premiere reutilise le plus | « Les tranches », neuf, la premiere sans bloqueur | couvert |
| 90-91, 96-97 | quatre surfaces validees separement, tests croises, relecture apres chaque changement | ADR 0024 point 5 ; chaque item porte ses tests croises en DoD | couvert dans le plan |
| 94-95 | l'existant plus avance prime, l'invisible compte | ce tableau est la reponse a 95 ; 94 est la regle de chaque verdict de composer | couvert |
| 98 | la documentation attendue | ce document (inventaire, ecarts, carte, frontiere, capacites, identite) + ADR 0024 ; le reste nait avec sa tranche | partiel : PROVIDER_CONTRACTS, ARTIFACT_GRAPH, KNOWLEDGE_ENGINE n'existent pas encore, chacun a une tranche |
| 99, 104-105 | ne pas s'arreter au plan, demarrer par la plus petite tranche | la tranche 1 est ouverte et sans bloqueur : c'est le prochain geste apres le merge | couvert |
Ce que ce tableau laisse honnetement dehors : le registre de connaissance (51-53), la QA semantique (63) et le pipeline voix-comme- horloge (48). Aucun des trois n'est oublie, aucun n'a d'item, et les trois dependent d'une tranche qui n'est pas livree.
Risques
- La tranche 1 touche le chemin le plus chaud du produit. Deplacer sept outils, c'est risquer de casser le chat. Ce qui l'attenue : les fabriques ne sont pas reecrites, et le test de parite est ecrit avant le deplacement, pas apres.
AtlasToolContextest un type partage par 26 familles. Lui ajouter deux champs oblige chaque construction de contexte a repondre. C'est voulu (le depot le dit deja pourisPlatformAdmin), mais c'est une PR large.- La tranche 4 rend visible une depense qui ne l'etait pas. Le cockpit d'economie affichera une marge differente le jour ou la production interne y entre. C'est une correction, pas une regression, et il faut le dire avant.
- Un provider de plus est une surface d'attaque et une facture. Cle serveur seulement, scope non accordable par defaut, plafond ecrit.
- Le squelette d'UI sans les captures est une invitation a deviner. Il est marque comme tel a trois endroits pour que personne ne le lise comme une specification.
Ce qui attend une decision de gouvernance
Aucune de ces questions n'est tranchable par un agent : chacune engage un
prix, un contrat, une depense ou un risque juridique. Une question sans
item type: "decision" n'atteint jamais le registre, donc le
proprietaire ne la voit pas : les cinq qui n'en avaient pas en ont un,
et la section 8 de
docs/ops/operator-decisions.md les
porte avec leur recommandation.
| Question | Item | Ce qui est deja mesure | Bloque |
|---|---|---|---|
| Comment distinguer au ledger la production interne du revenu consomme ? | billing/0613 | billing/0613 : trackStudioMediaUsage rend null sans orgId, donc gratuit et invisible ; l'equipe se debite elle-meme depuis son organisation | tranche 4 |
| Le cout de gros reel d'une image, et ouvre-t-on le controle de resolution ? | billing/2786 | l'ecart est epingle : markup: 1.5 avec perImage 0.21 impliquerait 0.14, la constante dit 0.039 ; studio-media-units-have-a-cost.test.ts le nomme sans le trancher | la verite du cockpit de marge |
Le mouvement et le remplacement d'un personnage restent-ils planned, ou vaut-il la peine d'ouvrir la liste fermee des moteurs pour les obtenir ? | aucun : la liste des moteurs est fermee par l'ADR 0024, la rouvrir se demande en une ligne | aucun de nos six moteurs ne les sert ; la reference d'ergonomie qui les montre n'est pas un fournisseur que nous utiliserons | tranche 5 |
| Une identite est-elle scopee au store ou a l'organisation ? | data-platform/2789 | StudioAsset n'a que storeID ; une agence qui reutilise un porte-parole sur plusieurs marques n'a aucun mecanisme | tranche 6 |
| Le droit a l'image et a la voix d'une personne reelle cote client | security-identity/2790 | le seul controle existant est assertVoiceConsent, dans un script CLI hors application | tranche 6 |
| Quel plan inclut la generation video ? | billing/2788 | canAffordStudioMedia est la seule porte : seul le solde decide, et un clip de 8 s vaut $1.20 contre $1 de bonus quotidien Pro | tranche 1, la couche d'entitlement n'a pas de contenu |
| La licence Remotion | billing/2787 | ADR 0017 la nomme (« Automators si automation et organisation de trois personnes ou plus ») | tranche 4, des qu'on recrute |
Ce que le backlog en fait
Huit items de travail, un par tranche executable plus les deux que la
tranche 2 tire derriere elle, cinq items type: "decision" pour les
questions qui n'appartiennent a aucun agent, et six items existants dont
la relation a la cible est desormais explicite plutot que devinee.
Les items ouverts par cet audit
| Item | Tranche | Bloque par |
|---|---|---|
ai-platform/2780 | 1, la generation entre dans le registre | rien |
data-platform/2781 | 2, une generation est une ligne et un job | 2780 |
ai-platform/2782 | 3, le scope ferme | rien (meilleur apres 2780) |
growth-web/2783 | 4, la plateforme devient un tenant | billing/0613, decision |
data-platform/2784 | 6, l'identite visuelle | 2780, plus deux decisions |
design-system/2785 | 7, la surface | 2780, 2781, et les captures |
data-platform/2791 | 2, sa moitie retention et suppression propagee | 2781 |
app-shell/2792 | 9, rejouer, verrouiller, versionner | 2781 |
platform-ops/2793 | 7, l'outillage de regression visuelle (hors du pilier de la surface) | rien |
integrations/2794 | hors tranche : Higgsfield est un secours, jamais un moteur, et le depot doit le presenter ainsi | rien (consigne) |
La tranche 5 (nommer ce que nos moteurs ne servent pas) et la tranche 8
(la boucle) n'ont pas d'item de travail : la premiere est une evaluation qui tient
dans 2780 une fois la famille en place, la seconde attend 2781 et
2783 pour avoir un maillon a rattacher.
Les items existants, replaces
| Item | Etat | Relation a la cible |
|---|---|---|
ai-platform/0377 (un rendu de plus de 180 s n'a aucun chemin) | blocked par platform-ops/0329 | La moitie « rien ne suit un rendu » passe dans 2781, qui ne depend d'aucune version du SDK. Ce qui reste dans 0377 est la moitie SDK : l'asynchrone natif de experimental_generateVideo arrive en ai@7. |
ai-platform/2764 (le Studio ignore les 87 capacites) | claimed, moitie serveur livree | Prolonge par 2780 : 2764 a branche le canvas sur le registre, 2780 met la generation DANS le registre. La garde que 2764 annonce, surfaces-import-their-capabilities, est livree par 2780 puisqu'elle n'existe pas. |
ai-platform/2773 (porte d'approbation des ecritures du canvas) | ready, bloque par 2764 | Prerequis pour qu'une capacite d'ecriture passe available sur le canvas. A corriger : il cite commerce-os-registry comme « source de ce qu'une porte doit exiger », or ce fichier n'a plus aucun importateur. Il doit deriver de resolveToolRisk. |
ai-platform/2777 (le noeud aiText appelle le modele nu) | ready, P1 | Independant et toujours vrai : tant que le noeud de raisonnement du canvas n'a qu'un niveau de contexte, « meme pipeline » est faux pour lui. |
ai-platform/0347 (voix, vecteur, reranker) | ready | Recoupe 2784 sur la voix : 0347 demande de cabler ce qui manque, 2784 demande a qui la voix appartient. 0347 reste, et attend 2784 pour la partie identite. |
app-shell/0154 (la bibliotheque d'un store n'a pas de porte) | ready | Devient une consequence de 2785 : une galerie unique sur StudioAsset est ce que 0154 demandait. |
billing/0613 (qui paie la production Studio interne) | ready, type decision | Bloque 2783. Rendre la depense visible est du travail ; decider comment elle se distingue du revenu consomme appartient au proprietaire. |
integrations/2767 (livrer une creative depuis le MCP) | ready, bloque par 2765 | A reecrire apres 2780. Il n'ouvrait que le chemin Remotion de la plateforme, et demandait « suivre un rendu » sans substrat. Une fois la famille dans le registre et GenerationRequest en base, il devient une porte de scope, pas une implementation. |
growth-web/2682 (campagne multi-format) | ready | Reste, et porte l'AMONT de la tranche 8 (unite, recette, multi-format) quand 2781 et 2783 sont la. Il ne porte pas la moitie distribution : son perimetre ne mentionne ni canal par store ni compte Postiz de tenant, et c'est la limite nommee dans la ligne d'ecart « la distribution est mono-tenant ». |