Audits · septembre 2026Asymetrie Fondateur / Marchand dans le Studio

Asymetrie Fondateur / Marchand dans le Studio

Audit commande, hors rotation hebdomadaire. Semaine du 2026-09-15. Piliers couverts : design-system et ai-platform en premier chef, growth-web et data-platform en consequence.

Audit commande, hors rotation hebdomadaire. Semaine du 2026-09-15. Piliers couverts : design-system et ai-platform en premier chef, growth-web et data-platform en consequence.

Périmètre

Commit mesure : 43e2e3ca17d33152dd283502709e805fe0649491 (origin/main). Tous les chiffres de cette page sont derives d'un worktree detache sur ce commit, jamais de l'arbre de travail : un autre agent modifie en ce moment src/features/studio/**, src/components/studio/** et src/lib/studio/**.

Revu : les deux montages du module Studio (src/features/studio/creative-os-access.ts, creative-os.ts), les 28 fichiers de src/components/studio/, le registre d'outils (src/features/ai/tools/), le catalogue du canvas (workflow-capabilities.ts), le catalogue MCP (mcp-scopes.ts), les onze boards du siege (src/features/studio/ops-boards/), et les quatre moteurs nommes.

Non revu, volontairement : la resolution du tenant Fondateur. Elle est tranchee par l'ADR 0026 et son implementation est en vol. Aucun item de cette page ne la refait ; plusieurs s'appuient dessus et le disent.

Non mesurable depuis ce depot, et c'est ecrit plutot que devine : ce que INTERNAL_ORG_SLUGS vaut en production. La variable est lue cote serveur (internal-pricing.ts:41), son defaut est "" (src/env/server.ts:634), donc un clone ne peut pas savoir si une organisation de plateforme est designee.


La table d'ecart

Une capacite par ligne, et pour chacune ce que le montage MARCHAND (storeStudioHost, le mode studio de la scene du store) peut, contre ce que le montage FONDATEUR (openInternalCreativeOs, /ops/creative/studio) peut.

CapaciteFichierMontage marchandMontage FondateurEcart
Composer, mode Imageactions.ts:230rend vraiment, ligne GenerationRequest, debit ledgeridentiqueaucun
Composer, mode Videogallery-context.tsx:326clip de demo groupe, rien de factureidentiqueaucun
Composer, mode Novaidemclip de demoidentiqueaucun
Composer, mode Replaceidemclip de demoidentiqueaucun
Edition (Upscale, Variation)actions.ts:258image-vers-image reelidentiqueaucun
Galeriecreative-os.ts galleryItemsForlistStoreGalleryRows(storeId)identiqueaucun
Référencesstudio-references.tsscope stores/<id>/identiqueaucun
Production (6 blocs)buildStudioProductionblocs filtres par studio.*identiqueaucun
Couts affichescomposerCostscredits (retail)dollars (internal) si l'org est designeederive de l'ORG, pas du montage
Grille de prix, abonnement, profilpricing-plans.tsx, subscription-content.tsx, profile-content.tsxrendusrendus aussi, checkout a nullaucun, sauf un CTA mort en moins
Avatar / identitestudio.tsx:85ecran « rien n'est construit »identiqueaucun
AcademyacademyForOperatorliste vide, ligne absente du railcorpus content/handbook/rail
Platform SystemplatformSystemForappele aussi (vide sans mandat)remplirail
Brand SystembrandSystemFor9 entrees9 entrees des qu'une marque est choisieaucun
Selecteur de marqueretire. Le siege ouvre la marque designee (founder-tenant.ts)brands: [], composant rend nullliste des marques atteintesrail
Les 11 boards du siegeops-boards/index.tsinatteignables sans mandat11 boards en lecturerail
Remotion, ElevenLabs, Postiz, Datafastcf. §2inatteignablesinatteignables aussi depuis le cockpitaucun

Ce que la table prouve

Le module fait 5519 lignes sur 28 fichiers (wc -l src/components/studio/*.ts*). Le nombre de lignes de ce module qui se comportent differemment selon le montage est quatre, et les voici toutes :

Fichier, ligneCe qui differe
studio-sidebar.tsx:106host.academy.length decide si la ligne Academy existe
data.tsx:124links.operator decide si la ligne Platform System existe
system-content.tsx:106quelle des deux Systems est rendue
founder-tenant.tsle selecteur n'est plus monte. Sans marque designee, le siege ne produit pas

Les quatre vivent dans le RAIL ou dans ce que le rail ouvre. Aucun fichier du composer, de la galerie, de la modale de detail, de la production, du prix, du profil ou de l'abonnement ne lit links.operator, scope.brands ni academy. Le constat est donc exact au sens litteral : la seule chose qui differe est la sidebar.

Et l'asymetrie n'est meme pas propre dans ce sens-la : storeStudioHost appelle platformSystemFor(who.id) (creative-os-access.ts), donc un fondateur qui ouvre le cockpit d'une boutique CLIENTE y voit sa Platform System. C'est exactement la confusion que l'ADR 0026 §Contexte nomme au point 3 : le siege est devenu un outil pour operer la boutique d'un utilisateur, et ici il s'invite dans son cockpit.


1. Ce que chaque montage peut, en detail

1.1 Le composer produit une chose sur quatre

generateStudioImage est la seule generation reelle du module. Le commentaire de actions.ts:225 le dit lui-meme : « Video/Nova modes stay on the bundled demo clip ». Le chemin est ecrit dans gallery-context.tsx:326 : un setTimeout, puis DEMO_RESULT.videoSrc = /gallery/landscape-video-1.mp4, et « Nothing is charged for a demo clip ».

Trois des quatre onglets du composer sont donc une maquette, des deux cotes. Un fondateur qui ouvre son siege pour produire une video de lancement obtient le meme mp4 groupe qu'un marchand.

Pendant ce temps le registre d'outils porte un vrai generateVideo (handler-tools-build.ts:1353), qui ouvre sa ligne de journal et facture. Il n'est atteignable que par le chat.

1.2 Le cockpit lit onze surfaces et n'en opere aucune

Les onze boards de ops-boards/ portent chacun un champ action qui est un href sortant : /ops/creative/qc, /ops/creative/generate, /ops/creative/library, /ops/creative/cockpit, /ops/creative/pipeline, /ops/creative/clients, /ops/creative/concepts, /ops/growth, /ops/content, /ops/bulletin, /ops/bulletin/health.

Le docblock de media.ts l'assume mot pour mot : « Le geste reste sur la page, que l'action nomme ». C'est une decision explicite et lisible, pas un oubli. Mais additionnee au reste, elle produit le resultat que le proprietaire decrit : le cockpit Fondateur est une SALLE DE LECTURE dont toutes les portes sortent, et tout ce qu'on peut y FAIRE est ce qu'un marchand peut faire.


2. Les quatre moteurs, et ou ils vivent

Derive par grep des importeurs, au commit.

MoteurModuleImporteurs hors de lui-memeAtteignable depuis le cockpit ?Le composer le connait ?
Remotionservices/creative/render-dispatch.ts7 : growth/creative-render.ts, ops/content/page.tsx, ops/content/_actions/index.ts, ops/creative/_actions/generate.ts, ops/creative/generate/page.tsx, ops-boards/creative.ts (type + lecture)non : le board ops-generate le LIT, son action sort vers /ops/creative/generatenon
ElevenLabsservices/creative/voiceover.ts3 : ops/content/page.tsx, ops/content/_actions/index.ts, ops-boards/media.tsnon : readVoiceoverQuota + voiceoverReadiness sont lus par le board, synthesizeVoiceover n'est appele que par l'action de /ops/contentnon
Postizservices/growth/postiz.ts10 fichiers, dont 5 sous ops/growth et ops/contentnon : isPostizConfigured + listPostizIntegrations dans le board, la publication reste sur la pagenon
Datafastlib/datafast-api.tslu par ops-boards/media.ts (datafastBlock, partage par les deux boards media)lecture seule, et c'est correct : Datafast mesure, il ne produit passans objet

Reponse nette : aucun des trois moteurs de PRODUCTION n'est atteignable depuis le cockpit, et le composer n'en connait aucun. Le composer connait une chose, runStudioImageGeneration via actions.ts:37.

Le catalogue Remotion vaut la peine d'etre chiffre : templates.json declare 10 templates et 6 formats. Zero sont offerts dans le composer, qui offre 4 modes dont 1 fonctionne.

Le commentaire de creative-os-access.ts sur le lien growth du rail Fondateur decrit la situation exactement : « c'est la que les trois moteurs retenus se rejoignent deja. Un lien, pas une copie. » Un lien qui sort du cockpit est precisement ce que design-system/2799 disait vouloir supprimer.


3. Ce que l'ADR 0024 §1 promet, et ce qui en est fait

Fait

La famille studio-media existe dans le registre. src/features/ai/tools/studio-media-tools.ts:72 declare les sept noms exactement comme l'ADR les nomme : generateImage, composeProductImage, generateVector, generateVideo, saveStudioAsset, planCreativeVariants, saveCreativeConcept. Elle est enregistree dans TOOL_FAMILIES (index.ts:178), etape studio, couche connected. AtlasToolContext a gagne son champ ctx.studio (modes, attachedImages), et studio-media-parity.test.ts prouve que la famille et la construction par tour rendent le meme ensemble.

La construction par tour de chat subsiste : handler-tools-build.ts fait toujours 2518 lignes et handler-tools-assembly.ts 347. Le parity test en fait un doublon GARDE plutot qu'une divergence, ce qui est le bon etat transitoire.

Pas fait

Le canvas n'a gagne aucune generation. Dans workflow-capabilities.ts, le compte au commit est 7 capacites available et 10 planned. ai.generate-image est toujours planned (ligne 44), et il n'existe aucune capacite ai.generate-video, ai.generate-vector, studio.save-asset ni render.composition. Les sept noms de STUDIO_MEDIA_TOOL_NAMES n'apparaissent nulle part dans ce fichier.

Le MCP n'a gagne aucune generation. mcp-scopes.ts:158 porte toujours boostecom:studio.read, famille native, avec un seul outil, getStudioSection, en lecture. Aucun scope d'ecriture media n'existe.

Le cockpit /ops n'a gagne aucune generation par derivation. Il a gagne onze boards de lecture (design-system/2799) et le composer image, qui est le meme que celui du marchand.

Verdict de section : la tranche 1 de l'ADR 0024 est livree a moitie. Le rangement est fait, la derivation qui le justifiait ne l'est pas. La phrase de l'ADR, « le canvas, le MCP, l'API et le cockpit /ops gagnent la generation par derivation, sans qu'aucune liste ne soit recopiee », est encore fausse pour les quatre.


4. La verite produit du tenant Fondateur

L'ADR 0024 §4 : « sa verite produit est facts.md monte comme source de connaissance de ce store, avec sa garde ».

Ce n'est pas le cas. Le fichier existe, a un seul endroit : .claude/skills/boostecom-content/references/facts.md, 141 lignes.

Aucun fichier de src/ ne le LIT. Les cinq mentions de facts.md dans le code sont des commentaires ou des messages d'erreur :

Le seul fichier du skill que le code lit vraiment est plan-2026-q4.json, et par un test (plan-import.test.ts:252).

Le mecanisme d'accueil, lui, existe depuis longtemps : Store.knowledgeSources est une colonne JSON du schema (prisma/schema.prisma:494), editee par components/shared/store-switcher/settings.tsx et consommee par @Atlas. Rien ne l'a jamais remplie pour la marque de la plateforme, pour la raison que l'ADR 0026 explique : jusqu'a lui, il n'y avait pas de marque de la plateforme a remplir.

Consequence concrete, et c'est la reponse la plus directe au constat : le Studio Fondateur ne sait rien du produit qu'il est cense vendre. Un marchand ouvre son Studio avec son catalogue, son kit de marque et ses concepts valides. Un fondateur ouvre le sien avec la meme page blanche, et les 141 lignes de faits sourcés restent dans un repertoire que seul un agent Claude Code charge.


5. Ce qui a fait deriver

Chronologie derivee de git log origin/main -- src/components/studio src/features/studio src/app/(dashboard)/ops, la plus ancienne d'abord.

CommitItemCe qu'il a fait, et pourquoi il a fait deriver
e79f04beaucun« replace the cross-org creative QC with the internal Studio ». Le siege cesse d'etre une vue inter-organisations et devient une vue d'UNE organisation. Juste en soi, mais c'est le premier pas ou « operer la plateforme » se met a vouloir dire « operer une organisation ».
5fda163csecurity-identity/ops et les permissions platform.* naissent. Le siege gagne sa frontiere : c'est le bon mecanisme, et il est toujours la.
415b1c32design-system/2796La derive principale. 54 fichiers, 5353 insertions. Le prototype v0 est porte « pixel pour pixel » et monte deux fois. Or ce prototype etait une grammaire de MARCHAND : un solde de credits, une grille de prix a trois plans, une page abonnement, un onglet Upscale. Monter cette grammaire telle quelle dans le siege, c'est avoir decide que le fondateur est un client de son propre produit. L'item le dit lui-meme : « rien ne change en UI/UX ».
9b9c0c7bdesign-system/2797Le Studio devient un MODE de la scene et les six surfaces quittent /admin/creative. Excellent pour le marchand. Pour le siege, il ajoute links.operator : neuf liens SORTANTS, qui sont depuis la totalite de ce que le fondateur a en plus dans le module.
6718bcc8design-system/2799Platform System et Brand System entrent dans le rail. La regle y est : le cockpit est le generateur tout-en-un. Ce qui a ete livre est un index, pas un generateur : deux sections de cartes-liens.
b3d22680:Les douze surfaces se lisent dans le cockpit. Onze boards, onze action qui sortent. La lecture est rapatriee, le GESTE reste dehors : c'est le moment ou le cockpit devient definitivement une salle de lecture.
98ded7aedesign-system/2802StudioScope.storeId devient nullable pour que le siege s'ouvre sans marque. L'ADR 0026 §Contexte point 2 a deja juge ce commit : il a supprime l'ecran de selection sans corriger sa source, donc on ne peut plus produire du tout cote /ops sans designer une boutique cliente.

La cause, en une phrase

Les sept pas sont chacun defendables. Ce qui manque a la chaine, c'est qu'aucun ne s'est jamais demande ce que le fondateur peut FAIRE que le marchand ne peut pas. Le module a ete porte depuis une maquette de produit vendu, monte deux fois, puis on a passe trois items a lui ajouter des menus. La sidebar est le seul endroit ou quelqu'un a repondu a la question, et une sidebar ne produit rien.

L'ADR 0024 §4 avait pourtant la bonne formule : ce que le profil Founder a en plus est du CONTEXTE, pas du code. Mais un contexte doit ETRE MONTE quelque part pour exister, et les trois porteurs de ce contexte sont precisement les trois trous mesures ci-dessus : les mandats platform.* (montes, ils ne donnent que des liens), la lecture du depot comme verite produit (§4 : pas montee), les canaux Postiz de la plateforme (§2 : hors du cockpit).


Constats par severite

P0

Aucun. Rien ici ne perd de donnee, ne fuit un scope ni ne facture a tort. Le scope croise est tenu : actions.ts:154 resout les references dans { scope: { storeId } }, et le prefixe Blob est stores/<storeId>/studio.

P1

  1. Trois des quatre modes du composer sont une maquette, des deux cotes (gallery-context.tsx:326), alors que generateVideo existe et facture dans le registre. → design-system/2822.
  2. Les trois moteurs de production sont hors du cockpit (§2). Le fondateur ne peut declencher ni un rendu Remotion, ni une voix, ni un brouillon Postiz sans quitter le cockpit. → ai-platform/2823, ai-platform/2824, design-system/2825.
  3. facts.md n'est monte nulle part (§4). Le Studio du fondateur ne connait pas le produit. → ai-platform/2826.
  4. Le montage MARCHAND resout la Platform System (creative-os-access.ts, storeStudioHost). Un fondateur voit son siege dans le cockpit d'un client : c'est l'inversion que l'ADR 0026 ferme, reintroduite par une ligne. → design-system/2820.

P2

  1. La tranche 1 de l'ADR 0024 est a moitie livree (§3) : sept outils ranges, zero surface qui en derive. Mesure perimee le jour meme. Verifie le 2026-09-19 : le canvas DERIVE bien les sept capacites (capability-catalogue.ts ← describeAtlasTools), l'API /api/workflow/v2/capabilities les sert, le noeud capability les execute et the-studio-runs-what-it-declares.test.ts verifie leur etat depuis leur RISQUE. ai.generate-image reste planned et le doit : aiImage n'a pas d'executeur, le chemin qui genere est capability. Ne reste que le scope MCP d'ECRITURE, qui n'est pas un trou d'implementation mais la decision integrations/0714, et dont le travail est suivi par integrations/2767. → ai-platform/2821, clos sur ce constat.
  2. creative/library/ reste un second inventaire. generated-assets.ts liste des fichiers Blob sans ligne en base, pendant que le Studio ecrit des GenerationRequest. L'ADR 0024 §Consequences l'avait declare « chemin de transition » : il ne l'est que si quelque chose le termine. → data-platform/2828.
  3. La voix de la plateforme n'atteint pas le composer. copy-rules.ts encode les regles de copie, le composer Fondateur n'en applique aucune. → growth-web/2827.

Non-constats

Ce qui ressemble a un defaut et n'en est pas. Un prochain auditeur ne doit pas les rouvrir.

  1. Le module partage a 99,9% entre les deux montages n'est PAS le defaut. C'est la decision de l'ADR 0024 (« un seul moteur creatif ») et de l'ADR 0023, et elle est juste. Le defaut n'est pas que le noyau soit commun, c'est que rien n'ait ete ajoute au-dessus cote Fondateur. Refactorer vers deux implementations serait la pire reponse possible a ce rapport.

  2. Les onze boards en lecture seule avec un action sortant ne sont pas une paresse. Le docblock de ops-boards/media.ts argumente correctement : un board repond « est-ce que quelque chose m'attend ici ? », une page porte le geste et sa garde serveur. La regle est bonne ; ce qui manque est qu'aucun geste de PRODUCTION n'ait jamais rejoint le composer, ou il aurait sa place.

  3. La grille de prix rendue dans le montage Fondateur n'est pas un bug. checkout y est null (creative-os-access.ts), avec sa raison ecrite : « a CTA that 404s is worse than none ». La section reste atteignable par le rail, ce qui est discutable en ergonomie, pas en correction.

  4. INTERNAL_ORG_SLUGS vide n'est pas une regression a signaler ici. C'est le defaut d'origine, et l'ADR 0026 §Decision point 3 tranche deja ce qui doit se passer dans ce cas. Cet audit ne rouvre pas la question.

  5. handler-tools-build.ts construit encore les outils media par tour de chat. C'est un doublon, et il est GARDE : studio-media-parity.test.ts compare les deux portes sur chaque combinaison de modes. Un doublon sous garde derivee est une dette acceptable ; le supprimer est un nettoyage, pas une correction de risque.

  6. Datafast lu et jamais ecrit depuis le cockpit n'est pas un ecart. Datafast mesure. Un moteur de mesure qu'on pourrait « declencher » depuis un composer serait le defaut.

  7. Le mode avatar qui n'ouvre rien est deja traite, correctement : studio.tsx:85 rend un etat vide qui DIT ce qui manque, et l'ADR 0024 §6 a deja nomme l'objet (StudioIdentity) et refuse le mot « avatar » comme identifiant moteur. Aucun item n'est ouvert dessus par cet audit : il est en attente de la tranche qui porte le modele.


Sequence de fermeture de l'ecart

Neuf items, dans l'ordre de lancement. Chacun nomme son bloqueur.

#ItemTailleBloque par
1design-system/2820S:
2ai-platform/2821Lclos : la derivation etait deja livree, le reste est integrations/0714
3design-system/2822Llivre : un seul executeur video, Nova et Replace desactives
4ai-platform/2823Llivre : capacite du registre ; le reglage de la ligne est platform-ops/2830
5ai-platform/2824Mlivre : capacite du registre, voix sous le prefixe de sa boutique
6design-system/2825Llivre : trois gestes, chacun par sa capacite et sa permission
7ai-platform/2826Mlivre : monte comme source de connaissance du tenant designe
8growth-web/2827Mlivre : cadre derive, et fact.unsourced devient une regle
9data-platform/2828Mlivre : le second inventaire a cesse de grossir ; la vue unique est platform-ops/2830

Les items 7 et 8 sont independants des six premiers : ils peuvent partir en parallele, sur un autre pilier, des que l'ADR 0026 a pose la marque de la plateforme.

Correction du 2026-09-19 : un item de type decision EST ouvert et commande une partie de cette sequence — integrations/0714, « un agent MCP peut-il declencher une generation payante ». La phrase qui suit etait fausse a l'ecriture ; elle est conservee pour que la correction se lise.

Aucun item de type decision n'est ouvert. La seule question qui en etait une, « qui paie la production interne », a deja ete tranchee et livree (billing/0613, archive) : une organisation designee produit au cout fournisseur.