ADRADR-0004 · Le Studio agence est un tenant de la plateforme, pas un back-office separe

ADR-0004 — Le Studio agence est un tenant de la plateforme, pas un back-office separe

BoostEcom Studio est aujourd'hui deux choses qui portent le meme nom et ne se parlent pas.

Statut

Remplacé par ADR-0043 · 2026-08-21

Piliers : security-identity, app-shell, data-platform

Contexte

BoostEcom Studio est aujourd'hui deux choses qui portent le meme nom et ne se parlent pas.

  • Dans Notion : un OS d'agence creative complet, 5 bases sources (Prospects, Clients & Marques, Avatars & Angles, Production creative, Performance & Apprentissages), 24 composants de Bibliotheque systeme, 3 playbooks de role, un SOP de bout en bout, des gates chiffres (scoring prospect /100, score onboarding >=80, Go/No-Go, QA). Statut auto-declare : READY FOR PILOT PRODUCTION.
  • Dans la codebase : @Atlas Studio (moteur media) + le process vertical BoostEcom Creative decrit par l'ADR 0003, StudioAsset, StudioDrop, le skill creative-ads, la QC humaine, la cadence.

Le fondateur est seul et recrute trois personnes. L'objectif est que l'equipe travaille dans l'app et non dans Notion : chacun avec son role, ses droits, sa file de travail, ses taches ; l'admin avec la vue globale. Et la meme mecanique doit rester vendable aux marchands de la plateforme.

La question posee n'est donc pas « quoi construire » mais ou brancher ce qui existe deja. L'inventaire montre que le squelette est en place :

  • Organization + OrganizationMember (4 roles) + OrganizationInvitation
    • emails d'invitation — la structure d'equipe existe.
  • permissions.ts porte deja un resolveur a deux couches (baseline de role
    • override {allow, deny} par membre) et un PERMISSION_REGISTRY que l'UI admin lit : le systeme de droits fins existe, il n'a juste aucune permission Studio.
  • Store est deja l'unite « marque » : creativeCadence, StudioAsset, StudioDrop, StoreContext.brandKit, Task, StoreNote, StoreReport y sont tous rattaches.
  • Task + TaskActivity + TaskStatus (6 etats) portent deja le travail, avec journal d'activite.
  • PlatformActivity porte deja le flux d'evenements org-scope.
  • L'Intelligence fournit deja l'etape OBSERVE du SOP (getCompetitorAds, getWinningAngles, AdCreativeAnalysis).

Trois manques bloquent, et un seul empeche litteralement de recruter.

Décision

Le Studio agence est une Organization de la plateforme, operee par ses membres. Une marque cliente de l'agence est un Store de cette org. Une recrue est un OrganizationMember. Aucun back-office parallele, aucun second modele de donnees, aucun « mode agence ».

Consequence directe : l'agence est le premier utilisateur du produit qu'elle vend. Toute surface construite pour l'equipe interne est la meme que celle livree au marchand, gatee par permission et non par duplication. Si une fonctionnalite n'a de sens que pour l'agence, elle est une permission, pas une branche de code.

Quatre regles en decoulent.

  1. Aucune surface d'equipe ne vit derriere requireAdmin(). requireAdmin verifie User.role === "ADMIN", un flag plateforme global : il ouvre le ledger de facturation, toutes les organisations, tous les utilisateurs, les feature flags. Aujourd'hui la QC creative (/admin/operations/creative-qc) est derriere ce guard. Donner a un producteur le droit de valider une creative reviendrait a lui donner la plateforme entiere. C'est le blocage reel du recrutement, et il est de nature securite, pas de nature produit.

  2. Le role metier est une composition de permissions, pas un enum. Les quatre roles du SOP (Ventes, Client Ops, Strategie, Production/QA) deviennent des presets ecrits dans OrganizationMember.permissions, au-dessus des quatre roles generiques existants. Aucun nouvel enum, aucune migration de role, et un membre peut porter deux casquettes : ce qui est le cas normal a trois personnes.

  3. Le travail assignable est Task, etendu. Pas de nouveau modele de tache. Task a deja le statut, la priorite, les dependances, le journal. Il lui manque un assignataire humain et une echeance.

  4. Ce que Notion porte et que le code ne porte pas devient une entite, pas un champ texte : le prospect, le concept creatif valide, et l'apprentissage. Ces trois-la sont les seules vraies creations de modele : tout le reste est du branchement.

Alternatives écartées

OptionPourquoi non
Garder Notion comme back-office et synchroniserDeux sources de verite sur le meme travail. La regle d'administration du hub Notion l'interdit deja pour elle-meme (« une information critique possede une seule source de verite ») ; l'appliquer entre deux systemes est pire, pas mieux. Et un sync bidirectionnel prospect/tache/asset coute plus cher a maintenir que les trois modeles a creer.
Un espace /studio separe du dashboard orgDuplique la navigation, l'auth, le scoping tenant et les shells. Surtout : casse la regle qui fait la valeur du choix — l'agence cesse d'etre utilisatrice du produit, donc les bugs du marchand ne sont plus vus par l'equipe.
Un nouvel enum StudioRole sur OrganizationMemberMigration + un second axe d'autorisation a cote de role, donc deux resolveurs qui divergeront. Le resolveur hasPermission() existant absorbe le besoin sans schema change, et supporte nativement le cumul de casquettes.
Promouvoir les recrues User.role = ADMIN pour debloquer la QCTrois personnes avec acces au ledger de facturation, a toutes les organisations clientes et aux feature flags de production. Non.
Un modele AgencyClient distinct de StoreStudioAsset, StudioDrop, creativeCadence, brandKit, Task, StoreReport sont deja tous store-scoped. Un second porteur de marque dedoublerait chacun d'eux.
Attendre d'avoir 10 clients avant de modeliser prospect / concept / learningVaut pour ce qui depend de donnees reelles. Ne vaut pas ici : le process est deja ecrit, teste sur trois marques, et documente ligne a ligne dans le SOP. C'est de l'encodage, pas de la speculation.

Conséquences

Ce que ca coute.

  • permissions.ts passe d'un registre de 15 permissions a un registre qui porte aussi le namespace studio.*. Le fichier devient un point chaud : toute surface Studio doit y declarer sa permission avant d'exister.
  • La QC creative change d'URL. L'ancienne route admin reste, en lecture cross-org, parce qu'un operateur plateforme a une raison legitime de voir la production de toutes les orgs : mais elle cesse d'etre le seul chemin.
  • Trois modeles nouveaux (Prospect, CreativeConcept, CreativeLearning) et deux colonnes sur des modeles existants. Le schema guard genere absorbe tout, aucun step manuel.

La dette acceptee.

  • Le SOP Notion reste la reference methodologique. Le migrer n'est pas l'objet : ce qui migre est l'etat operationnel (qui fait quoi, ou en est-on, qu'est-ce qui bloque), pas la doctrine. La Bibliotheque systeme (24 composants) reste dans Notion tant qu'aucun d'eux n'est execute par du code : le jour ou un prompt est execute, il rejoint src/features/ai/skills/ et sort de Notion.
  • Store.framework est Shopify-only. Une marque cliente hors Shopify entre quand meme comme Store (le domain est nullable), mais sans bridge Admin GraphQL : l'etape UNDERSTAND du skill perd sa source catalogue. Accepte — l'ICP du SOP est explicitement e-commerce a catalogue actif.
  • La V0 reste statics-only tant que HIGGSFIELDS_MCP_URL / HIGGSFIELDS_MCP_TOKEN ne sont pas poses. L'offre du SOP (8 videos/mois) n'est pas productible par la plateforme aujourd'hui. C'est une action operateur, pas un chantier de code. Plus vrai depuis le 2026-09-04 (ai-platform/0345) : la video part en direct sur Veo 3.1 via l'AI Gateway, sans aucune cle MCP. L'offre « 8 videos/mois » est productible. Le texte barre est conserve parce qu'un ADR enregistre ce qui etait vrai a sa date ; il ne decrit plus le present. Voir la note datee au bas de l'ADR 0003 pour le detail.

Le signal de revisite. Si une deuxieme agence (autre que la notre) opere sur la plateforme avec ses propres membres, la question devient « l'org suffit-elle a isoler deux agences concurrentes sur les memes marques ». Tant que nous sommes la seule, l'org suffit.

Comment c'est appliqué

  • Permissions : namespace studio.* dans PERMISSION_REGISTRY (src/lib/security/permissions.ts) + presets de role metier. Le resolveur hasPermission() est inchange.
  • Guard : un requireStudioPermission(orgId, permission) cote serveur, construit sur le scoping tenant existant (src/lib/security/tenant-auth.ts), jamais sur requireAdmin.
  • Surfaces : sous /[orgSlug]/~/studio/*, dans le shell org existant. Chaque page declare la permission qu'elle exige.
  • Travail : Task.assigneeUserId + Task.dueAt, avec TaskActivity comme journal, le meme que celui deja ecrit par src/features/ai/tasks/actions.ts.
  • Entites metier : Prospect, CreativeConcept, CreativeLearning dans prisma/schema.prisma, derives par pnpm db:guard.
  • Non applique mecaniquement : rien n'empeche un owner de continuer a tout faire lui-meme sans assigner. Les presets sont une aide, pas une contrainte, et c'est voulu tant que l'equipe fait trois personnes.