Les trous du Studio, de l'Avatar, du MCP et de l'API
Lecture seule. Aucun fichier applicatif n'a ete touche. Ce document et les items de backlog qu'il ouvre sont le seul livrable. Date : 2026-09-19. Base : 23be2e2f (main). Perimetre : le Studio (marchand, agence, siege)…
Lecture seule. Aucun fichier applicatif n'a ete touche. Ce document et les items de backlog qu'il ouvre sont le seul livrable.
Date : 2026-09-19. Base :
23be2e2f(main). Perimetre : le Studio (marchand, agence, siege), la section Avatar, la porte MCP et la porte REST. Hors perimetre : le chat lui-meme, le marketplace, l'intelligence, la facturation.
Périmètre
Ce qui a ete lu, et c'est la liste complete : src/lib/security/mcp-scopes.ts,
src/app/api/mcp/[storeId]/register-tools.ts,
src/lib/security/studio-sections.ts, src/lib/security/permissions.ts,
src/lib/security/platform-permissions.ts,
src/features/studio/ops-routes.ts,
src/features/studio/ops-boards/**, src/features/studio/creative-os*.ts,
src/components/studio/**, src/services/creative/**,
src/features/ai/tools/studio*.ts,
src/app/api/organizations/[orgId]/studio/[section]/route.ts,
content/docs/en/, content/runbooks/en/, content/handbook/fr/,
src/lib/content/docs-surface.ts, prisma/schema.prisma,
.claude/fleet/ownership.json, et la sortie de pnpm deadcode (knip).
Ce qui n'a PAS ete lu, et qui peut donc porter des trous que ce document
ne voit pas : le canvas (features/ai/workflow/v2), le drop public
(/drop/[token]) au-dela de sa garde, les six locales autres que en, et
tout le pipeline Remotion hors du depot (creative/ a la racine n'a ete lu
que par son catalogue).
Un avertissement qui change la lecture de la section 2
Au moment de l'audit, l'arbre de travail porte dix fichiers modifies et
non commites, dont mcp-scopes.ts, register-tools.ts, studio-guard.ts
et les six fichiers de messages/. Ce travail en vol ouvre exactement le
trou que le proprietaire avait mesure : il fait passer
boostecom:studio.read de un outil a quatre. Le HEAD a par ailleurs
avance pendant l'audit (840045c2 puis 23be2e2f).
Tous les chiffres ci-dessous sont donc donnes au commit 23be2e2f, et
le delta non commite est nomme la ou il s'applique. Un chiffre mesure sur
l'arbre de travail aurait decrit un etat que personne d'autre ne voit.
1. Declare mais pas implemente
1.1 [P1] Une table de permissions marchande que personne ne lit, et sa copie vivante
STORE_STUDIO_SECTION_PERMISSION et le type StoreStudioSection
(src/lib/security/studio-sections.ts:74) portent trois entrees
(concepts, inFlight, deliveries) et zero lecteur : la seule autre
occurrence dans le depot est le commentaire du fichier lui-meme, l. 24.
Ce que la surface marchande lit reellement est PRODUCTION_PERMISSIONS
(src/features/studio/creative-os.ts:533), cinq entrees, dont trois
portent les MEMES cles (concept, inFlight, deliveries,
creative-os.ts:559-565).
Le cout n'est pas le code mort, il est dans l'en-tete du fichier de
securite : « le mode d'echec que ce fichier existe pour empecher est
l'evident : trois copies de la meme table, qui derivent, jusqu'a ce qu'une
porte accorde ce qu'une autre refuse ». Il y a bien deux tables, et la
morte est celle qui documente. Un agent qui boote sur studio-sections.ts
apprend une porte qui ne s'ouvre nulle part.
→ item security-identity/2900.
1.2 [P1] Deux etats de generation sur six ne sont jamais ecrits
GenerationStatus declare six valeurs (prisma/schema.prisma:7584-7596).
Les ecrivains vivent dans src/services/creative/generation-journal.ts :
| Etat | Ecrivain | Appelants hors du fichier |
|---|---|---|
QUEUED | openGeneration (l. 93) | oui |
SUBMITTED | markSubmitted (l. 127) | 4 |
PROCESSING | markProcessing (l. 135) | 0 |
COMPLETED | settleDelivered (l. 152) | 5 |
FAILED | settleFailed (l. 184) | 5 |
CANCELLED | settleCancelled (l. 203) | 0 |
Trois consequences observables, pas une :
- une generation dont le processus meurt entre
markSubmittedet le reglage resteSUBMITTEDpour toujours. Aucun chemin ne la ferme ; RUNNING_STATUSES(generation-feed.ts:36) interrogePROCESSING, donc une des trois valeurs de la requete « ce qui tourne » est morte ;- l'outil MCP en vol
listStudioGenerationsannonce dans sa description « queued, submitted or processing ». Il promet un etat que le journal n'ecrit jamais.
→ item data-platform/2901.
1.3 [P1] Trois modes du composer sur quatre rendent une video de demonstration sous un prix reel
ComposerMode declare quatre modes (src/components/studio/types.ts:75),
et MODE_TABS les affiche tous les quatre (data.tsx:229-232, nova
portant meme un badge « New »).
Un seul produit quelque chose. gallery-context.tsx:282-338 : la branche
mode === "image" appelle generateStudioImage (ligne reelle, solde,
rendu, facturation) ; tout le reste tombe sur un setTimeout de
3,2 a 4,6 s qui resout sur DEMO_RESULT.videoSrc. Le commentaire l'assume
(« these stay on the bundled demo asset until a polling-based flow
replaces this timeout »), et rien n'est facture, ce qui est le bon choix.
Le trou est ailleurs : composerCosts (creative-os.ts:111-127) calcule
et affiche un prix pour les quatre modes depuis la liste de prix media
reelle, y compris replace a quinze secondes de video. L'ecran annonce
donc un tarif exact sur trois boutons qui ne produisent rien. Un operateur
ne peut pas distinguer « ca coute ce prix » de « ca ne marche pas encore ».
generateVideo existe pourtant et fonctionne, dans le chat
(STUDIO_MEDIA_TOOL_NAMES, studio-media-tools.ts:72-80). L'asymetrie
n'est pas un manque de moteur, c'est un manque de branchement.
→ item design-system/2902.
1.4 [P2] Deux modeles de voix sur trois sont inatteignables
VOICEOVER_MODELS declare trois modeles (voiceover.ts:56), dont
eleven_flash_v2_5 que le fichier decrit lui-meme comme « le brouillon pas
cher, moitie prix au caractere ». synthesizeVoiceover accepte
input.model (l. 110) et retombe sur le defaut (l. 153).
Le seul appelant du depot est ops/content/_actions/index.ts:277, qui
passe { text, slug } et jamais model ni language. Deux modeles sur
trois, et la voie moitie prix, n'ont donc aucun chemin.
Pas d'item ouvert, et c'est delibere : le fichier vit dans
src/services/creative/, que aucun pilier ne possede (cf. 2.3). Ouvrir
un item dans un repertoire sans proprietaire produirait une PR que
pnpm fleet:scope refuse. Il est bloque par platform-ops/2903 et sera
ouvert apres lui.
1.5 [P3] Le cockpit se compte lui-meme a douze et a treize, il est onze
Derive : OPS_ROUTES porte 13 entrees
(src/features/studio/ops-routes.ts), OPS_SECTION_IDS et
OPS_BOARDS en portent 11 (types.ts:179,
ops-boards/{creative,commercial,media,bulletin}.ts : 4 + 3 + 2 + 2).
L'ecart de deux est correct et explique : /ops/creative/studio EST le
cockpit, /ops/docs partage le corpus Academy.
Ce qui ne l'est pas : huit commentaires disent « douze » et sept disent
« treize », pour la meme chose. Notamment ops-boards/index.ts:4 (« le
registre des douze surfaces »), types.ts:146-148,
ops-board-content.tsx:6-12, board-content.tsx:6-17,
system-content.tsx:9-27. Seul README.md:151 dit onze.
→ item design-system/2907.
1.6 Ce qui a ete verifie et n'est PAS un trou
PLATFORM_PERMISSIONS: les cinq ids (permissions.ts:149-155) sont tous honores par au moins une porte reelle, y comprisplatform.hub.readqui ne passe pas par/opsmais parsrc/lib/hub/paywall-gate.ts. Rien a ouvrir.StudioSectionId: les neuf sections marchandes plus les onze du siege sont toutes rendues (studio.tsx:56-96, la brancheisOpsSectiond'abord, puis leswitch). Aucune section orpheline.MAX_IN_FLIGHT_PER_ORG/inFlightCount: knip les signale, ils sont lus paroverCapacity(generation-journal.ts:273) dans le meme fichier. Faux positif.- Les onze scopes MCP : chacun porte au moins un outil enregistre, et
les 18
registerToolderegister-tools.tscouvrent la totalite des outils declares. Aucun scope vide. - L'absence d'un scope
boostecom:studio.write: ce n'est pas un oubli, c'est un refus ecrit.mcp-scopes.ts(arbre de travail) le motive en neuf lignes : un scope se justifie quand il permet de refuser quelque chose d'une autre nature, et la generation depensant de l'argent, elle aura sa propre famille le jour ou elle existera. Rien a ouvrir. internal-pricing.ts:internalOrgSlugsetisInternalOrgsont signales par knip et sont lus parpricingForOrgetinternalOrgIdsdans le meme fichier, eux-memes lus par le cockpit. Faux positif.
2. Implemente mais pas atteignable
2.1 [Le focus] Ce qu'un agent externe peut faire sur le Studio, mesure
Au commit 23be2e2f, les trois portes du Studio, cote a cote :
| Porte | Ce qu'elle ouvre | Compte |
|---|---|---|
Chat (features/ai/tools/) | getStudioPipeline, getStudioClientGates, getStudioConcepts, getStudioQcQueue, getStudioGallery, getStudioCockpit (studio-tools.ts) plus les sept capacites media (studio-media-tools.ts:72-80) | 13 |
MCP (boostecom:studio.read) | getStudioSection, cinq sections d'agence, lecture seule | 1 outil |
REST (/api/organizations/[orgId]/studio/[section]) | les memes cinq sections, lecture seule | 1 route |
Ce qu'un agent externe ne peut pas faire aujourd'hui via MCP, liste exhaustive et derivee :
- lire le journal des generations (
GenerationRequest) : le lecteur existe (generation-feed.ts, quatre fonctions), son seul monteur estGenerationsPanelsur/collab/[storeId]et l'outil de chatgetStudioGallery; - lire la production board d'une marque (
buildStudioProduction,creative-os.ts:556) : gate, in-flight, rendus, livraisons, concepts, economie ; - lire le prix d'une generation avant de la demander (
composerCosts) ; - lire l'une des onze surfaces du siege :
readOpsBoard(ops-boards/index.ts:53) n'est appelee que par une server action ; - declencher quoi que ce soit : aucune ecriture, par refus explicite.
Les points 1 a 3 sont en cours de fermeture dans cet arbre de travail,
non commites. Le diff ajoute getStudioProduction,
listStudioGenerations et getStudioPricing au meme scope, avec la porte
relue a chaque appel (storeStudioPermissionsFor) et le cout retire de la
reponse plutot que mis a zero sans studio.economics.read. Le scope
passera de 1 a 4 outils et le serveur de 18 a 21.
Aucun item n'est donc ouvert pour les points 1 a 3. Ouvrir un item sur un travail en vol est le doublon le plus cher qu'un auditeur puisse produire. Les points 4 et 5 restent, et le 5 est un refus assume.
Le point 4 reste un vrai trou, mais il ne se traite pas par un outil
MCP : une connexion MCP est scopee a une BOUTIQUE ([storeId]), alors
qu'un board du siege est une lecture plateforme sous permission
platform.*. Le loger sur le relais store reproduirait exactement le
defaut que mcp-scopes.ts reconnait deja pour operator-docs.read (« la
ressource est plateforme, l'URL est une boutique, la vraie maison est une
route a elle »). Il est donc nomme ici et laisse sans item : c'est une
route MCP operateur a part entiere, une decision d'architecture qui
n'appartient pas a un auditeur.
2.2 [P1] La porte REST va rester en retrait de la porte MCP sur la meme ressource
src/app/api/organizations/[orgId]/studio/[section]/route.ts ouvre cinq
sections, en lecture. C'est tout ce que find src/app/api -ipath "*studio*"
rend, hors le cron studio-orphans-prune.
Quand le travail en vol atterrira, la MEME ressource sera lisible par un agent MCP sur trois surfaces de plus qu'elle ne l'est par un client HTTP authentifie. Les deux portes lisent les memes loaders et se reclament de la meme garde ; l'ecart n'a aucune justification ecrite nulle part, a la difference du refus d'ecriture qui, lui, en a une.
content/docs/en/api-overview.mdx ne nomme d'ailleurs pas le Studio une
seule fois, donc la porte REST existante n'est pas davantage annoncee.
→ item app-shell/2904.
2.3 [P1] Le moteur creatif entier n'appartient a aucun pilier
Derive de .claude/fleet/ownership.json, en croisant ses globs avec les
repertoires de premier niveau de src/services/, src/features/ et
src/lib/ : deux repertoires sur l'ensemble ne sont couverts par aucun
pilier, et l'un des deux est src/services/creative/ (9 fichiers :
generation-journal.ts, generation-feed.ts, render-dispatch.ts,
voiceover.ts, generated-assets.ts, template-catalog.ts,
templates.json, studio-blob-cleanup.ts, plus un test). L'autre est
src/lib/http/ (1 fichier).
C'est le journal des generations, le dispatch Remotion, la synthese vocale
et le catalogue de gabarits : le coeur de production des quatre surfaces de
cet audit. Ils tombent sur le conductor par defaut, ce que le $comment du
fichier decrit comme « un signal que la carte est perimee ».
Effet de bord immediat et mesurable : le trou 1.4 (les modeles de voix) ne peut pas etre ouvert avant celui-ci, parce qu'une PR qui touche ce repertoire ne sait pas quel pilier declarer.
→ item platform-ops/2903.
2.4 [P3] Le barrel du module Studio n'a aucun importeur
pnpm deadcode rend deux fichiers entierement morts, dont
src/components/studio/index.ts : le point d'entree public du module,
qu'aucun fichier n'importe (tout passe par les chemins profonds). Le second
est src/components/hooks/use-logs-count.ts, hors perimetre.
→ plie dans l'item design-system/2907.
3. Atteignable mais pas documente
3.1 [P0] Le Studio marchand, la surface vendue, n'a aucune page de documentation
C'est le trou le plus cher des trois sections, et il se mesure en une
commande. content/docs/en/studio.mdx fait 82 lignes et cinq titres : la
chaine a cinq etapes, les permissions, le score Go/No-Go, la livraison, et
la difference agence / studio. Il decrit exclusivement le Studio
d'agence. Aucune occurrence, dans tout le fichier, de : composer, mode,
generation, image, video, cockpit, credit.
En face, ce qui est livre et qu'un client paie : le Studio est un MODE du
cockpit de sa boutique, avec un rail a neuf sections, une galerie assise
sur GenerationRequest, un composer a quatre modes, un selecteur de
marque, un prix par generation lu sur la liste de prix media, et un solde
debite a chaque image.
Les trois autres portes de doc sont muettes aussi :
content/docs/en/credits.mdx ne contient ni « image » ni « video » ;
api-overview.mdx ne contient pas « studio » ; et
src/test/docs-corpus.test.ts derive bien les Systems, les roles, les
plans, les scopes MCP, les webhooks et les connecteurs, mais rien de la
surface Studio marchande, donc aucune garde ne pouvait signaler
l'absence.
C'est la forme exacte du defaut que CLAUDE.md decrit pour
/community/docs : un chiffre faux trompe un agent, une surface absente
trompe le client qui decide d'acheter.
→ item growth-web/2905.
3.2 [P1] Le siege /ops n'est couvert par aucune garde de corpus, et trois de ses treize surfaces ne sont nommees nulle part
Derive route par route, en cherchant chaque href d'OPS_ROUTES dans
content/runbooks/ :
| Route | Nommee dans le corpus operateur |
|---|---|
/ops/creative/{qc,generate,library,cockpit,pipeline,clients,concepts} | oui (creative.mdx) |
/ops/content | oui (content.mdx) |
/ops/bulletin/health | via /ops/bulletin dans content.mdx |
/ops/creative/studio | non, aucune occurrence |
/ops/growth | non, aucune occurrence |
/ops/docs | non, aucune occurrence |
Trois sur treize, dont /ops/creative/studio qui est le cockpit lui-meme,
c'est-a-dire la surface la plus recente et celle par laquelle une recrue
entre.
La cause est structurelle et c'est elle qu'il faut fermer :
src/test/internal-docs-corpus.test.ts derive ses exigences
d'ADMIN_CATEGORIES et ADMIN_ROUTES (src/config/admin-routes.ts), et
jamais d'OPS_ROUTES. Or /ops existe precisement parce que ces
surfaces ont ete SORTIES de /admin pour s'ouvrir sur une permission
plutot que sur le role. Elles sont donc sorties de /admin et, par le meme
mouvement, sorties du perimetre de la garde qui verifiait qu'elles etaient
documentees. Personne ne pouvait le voir.
Troisieme corpus, meme silence : content/handbook/fr/ (4 pages, le guide
qu'une recrue non technique lit le premier jour) ne contient pas une
seule occurrence de « studio ».
→ item growth-web/2906.
3.3 Ce qui est documente et n'est PAS un trou
- Les treize routes
/opssont toutes presentes dans le tableau Dashboard deCLAUDE.md, avec leur permission. La carte de demarrage est juste. - Les cinq permissions
platform.*sont couvertes pargrant-poles-cover-platform-permissions.test.tsetops-pages-name-their-permission.test.ts: une page livree sans porte echoue en CI. La garde d'ACCES existe, c'est la garde de DOC qui manque. content/docs/en/mcp-tools.mdxdocumentegetStudioSection, sa famillenativeet la raison de sa colonne Shopify vide. Quand les trois outils en vol atterriront,docs-corpus.test.tsexigera leur couverture depuisMCP_SCOPES: le mecanisme fonctionne, il n'y a rien a ajouter.
4. Avatar : ce que le depot porte deja, et le mot qui bloque
4.1 Une correction au constat de depart
« La section existe dans le rail et ne rend rien » n'est plus vrai depuis
design-system/2804. studio.tsx:85-96 rend un etat vide qui NOMME le
manque : « A reusable character for this brand, so a face stays the same
across every render. Nothing is built behind this row yet ». Le rectangle
noir muet a ete remplace.
4.2 Le substrat qui existe, derive
| Ce qui existe | Ou | Ce que ca donne a un avatar |
|---|---|---|
CreativeConcept.marketingAvatar | prisma/schema.prisma:7973 | l'avatar au sens PERSONA, avec son garde-fou explicite (:7972, « women 25-34 n'est pas un avatar ») |
| La grille Avatar Card du SOP | src/features/ai/skills/creative-ads/prompt.md §3a-3b | 2 a 4 cartes reellement distinctes, puis la matrice Avatar x Angle |
| L'ecran qui valide un avatar | /ops/creative/concepts et /[orgSlug]/~/studio/concepts | « Valider l'avatar et l'angle dont une production part » (ops-routes.ts) |
| Les images de reference en entree | features/ai/orchestrator/runtime/studio-references.ts | un personnage peut deja etre DONNE a une generation, il ne peut pas etre RETENU |
| La voix, avec alignement | services/creative/voiceover.ts | la piste audio d'un avatar parlant, caractere par caractere, existe deja et tourne |
| Le compositing deterministe | services/creative/templates.json | 10 gabarits x 6 toiles, dont 4 en motion |
StudioAsset + variantOfId / variantAxis | schema | le lineage ou une serie de rendus d'un meme personnage se rangerait |
4.3 Le trou reel, et il n'est pas technique
Deux sens du mot « avatar » cohabitent dans ce depot sans jamais se croiser : le persona marketing (partout : schema, SOP, concepts, gates) et le personnage visuel reutilisable (nulle part, sauf dans la copie de l'etat vide qui le promet).
Et sur le second, le depot porte deja son propre verdict, en archive :
backlog/_archive/ai-platform/0345 note que « aucun modele de l'AI Gateway
ne fait d'avatar parlant », et l'audit du 2026-09-18 pose que la liste des
moteurs est fermee (AI Gateway, ElevenLabs, Remotion, Playwright,
Postiz, Datafast). Aucun d'eux ne produit un personnage coherent.
Donc : la section Avatar ne peut pas etre cadree comme une fonctionnalite a construire tant que la question « lequel des deux sens » n'est pas repondue. Si c'est le persona, la surface existe deja trois fois et il faut relier le rail aux concepts plutot qu'ouvrir une page. Si c'est le personnage, il faut decider si on le derive de nos moteurs (references persistantes + lineage, ce que le substrat permet en partie) ou si on achete, et la seconde branche engage une ligne de depense qui n'appartient qu'au proprietaire.
→ item ai-platform/2908, de type research : il cadre, il ne tranche
pas. Il deviendra un decision le jour ou la branche « acheter un moteur »
sera retenue, et pas avant.
Les trois trous les plus couteux
- Le Studio marchand n'a aucune documentation publique (3.1). C'est la surface vendue, elle n'est decrite nulle part, et aucune garde ne pouvait le dire.
- Deux etats de generation sur six ne sont jamais ecrits (1.2). Une
generation interrompue reste
SUBMITTEDpour toujours, et l'outil MCP en vol promet deja un de ces deux etats. - Le moteur creatif n'appartient a aucun pilier (2.3). Neuf fichiers qui portent le journal, le dispatch et la voix, hors de la carte : c'est le trou qui en bloque d'autres.
Items ouverts
| Item | Pilier | Type | Taille | Trou |
|---|---|---|---|---|
2900 | security-identity | fix | M | 1.1 |
2901 | data-platform | fix | M | 1.2 |
2902 | design-system | fix | M | 1.3 |
2903 | platform-ops | chore | S | 2.3 |
2904 | app-shell | feature | M | 2.2 |
2905 | growth-web | doc | L | 3.1 |
2906 | growth-web | test | M | 3.2 |
2907 | design-system | chore | S | 1.5, 2.4 |
2908 | ai-platform | research | M | 4.3 |
Trois trous reels sont volontairement sans item, chacun pour une raison
ecrite ci-dessus : les points 1 a 3 du 2.1 (travail en vol), le point 4 du
2.1 (decision d'architecture), et le 1.4 (bloque par 2903).