BoostEcom Studio — l'OS d'agence dans l'app (plan C retire, archive)
NOTE POST-NETTOYAGE (8 octobre 2026) : ce texte documente une architecture ou un audit historique, avant les PR #1717–#1724 et #1728. Les anciennes interfaces Growth/Cinema et l'ancien Studio marchand dans boostecom.app…
NOTE POST-NETTOYAGE (8 octobre 2026) : ce texte documente une architecture ou un audit historique, avant les PR #1717–#1724 et #1728. Les anciennes interfaces Growth/Cinema et l'ancien Studio marchand dans
boostecom.appont été retirés. Le contrat CURRENT estdocs/architecture/shopify-media-boundary.md; les capacités média Shopify et Orbit Studio sont distinctes. La validation des builds/tests/E2E reste déléguée à un autre agent.
Comment l'agence creative quitte Notion et vient s'operer dans la plateforme, sans construire un deuxieme produit.
Le pourquoi vit dans
docs/decisions/0004-le-studio-agence-est-un-tenant-de-la-plateforme.md. Le comment de la chaine creative vit dansdocs/ops/creative-drop-runbook.md. Ce fichier-ci est la carte : ce qui existe, ce qui manque, dans quel ordre.
Plan C retire le 2026-09-26 — lire ce document comme l'histoire. ADR 0043 remplace l'ADR 0004 : BoostEcom n'a ni agence ni client externe. Sont supprimes du code tout ce que ce document decrit autour d'une marque CLIENTE : le pipeline de prospects (
Prospect,ProspectEvent), le portefeuille et la gate Go/No-Go (colonnes de production deStore), la cadence et les drops (StudioDrop, croncreative-drop-tick, jobopen-creative-drop), la livraison publique/drop/[token], le Studio d'organisation/[orgSlug]/~/studio/**,/ops/creative/{pipeline,clients}, les presetssalesetclient_opset les permissionsagency.prospect.*,agency.client.*,agency.cadence.manage,studio.drop.*,studio.gate.read. Restent vivants : les concepts (CreativeConcept,CreativeLearning,/ops/creative/concepts) comme briefs d'angle Growth, la QC/ops/creative/qccomme revue avant Postiz, le cockpit/ops/creative/cockpit, la taxonomie, la feedback loop, et le Studio marchand (plan B). Les chemins de fichiers supprimes que ce document cite encore ne sont pas des erreurs a corriger : ils datent ce qui a existe. Les tables retirees du schema restent orphelines en base ; leurDROPest une action operateur ulterieure.
Ou vit le code (depuis
app-shell/0144). Les loaders, guards, server actions et composants du Studio vivent danssrc/features/studio/. Ils ont vecu jusqu'en septembre 2026 danssrc/app/(dashboard)/_studio/: un dossier prive du App Router, donc sans URL, mais importe parfeatures/ai/tools/studio-*.tset deux route handlers d'API. Un module metier lu depuis quatre points hors du groupe de routes n'est pas une colocation de route, etpnpm fleet:boundariesne pouvait pas voir ce couplage tant qu'il vivait soussrc/app/. Les items archives anterieurs a ce deplacement citent encore les chemins_studio/…: lirefeatures/studio/…. Les regles pures restent a part, danssrc/lib/studio/— voir §« Une seule orthographe par terme » pour pourquoi.
1. Le mapping, ligne a ligne
L'agence = une Organization. Une marque cliente = un Store. Une recrue
= un OrganizationMember. Tout le reste en decoule.
| Notion | Codebase aujourd'hui | Etat |
|---|---|---|
| Hub — Control Hub | /[orgSlug] (vue d'ensemble org) | ✅ existe |
01 — Operations quotidiennes (5 files de role) | /[orgSlug]/~/me + 4 sections Studio gatees par permission | ✅ chaque role a sa porte |
02 — SOP & Centre de connaissances | docs/ops/creative-drop-runbook.md + SOP Notion | 🟡 partiel, reste en doc |
🧩 Bibliotheque systeme (24 composants) | registre : studio-system-library.md | ✅ 14/24 executes, 24/24 sortis de Notion |
99 — Administration du systeme | prisma/schema.prisma + /admin | ✅ existe |
🎯 Prospects & Ventes | Prospect + /[orgSlug]/~/studio/pipeline | ✅ existe |
🏷️ Clients & Marques | Store + brandKit + /[orgSlug]/~/studio/clients | ✅ existe |
🧠 Avatars & Angles (concepts) | CreativeConcept + /[orgSlug]/~/studio/concepts | ✅ existe |
🎬 Production creative | StudioAsset + Task (assignataire, echeance, blocage) | ✅ existe |
📈 Performance & Apprentissages | CreativeLearning + la section Feedback Loop de /~/studio/concepts | ✅ la boucle revient depuis l'item 0031 |
Brand System (brandsystem.md) | Brand Kit wizard → StoreContext.brandKit | ✅ existe |
| Scoring prospect /100 | features/studio/prospect.ts (grille pure, priorite A/B/C derivee) | ✅ existe |
| Score onboarding /100 + Go/No-Go | production-gate.ts + /~/studio/clients (score recalcule, jamais saisi ; 11 conditions pour les 12 lignes du SOP) | ✅ existe |
| Cadence contenu (Quotidien → Sur mesure) | Store.creativeCadence (NONE/W/BW/M) | 🟡 4 valeurs vs 5 |
| Socle one-shot / Content Factory | Store.oneShotFoundation[] / Store.contentFactory[] | ✅ existe |
| QA checklist + 9 motifs de rejet | StudioAssetRejection + /[orgSlug]/~/studio/qc | ✅ existe, gate par permission |
| Livraison client | StudioDrop + /drop/[token] + /[orgSlug]/[storeSlug]/studio | ✅ existe, et la marque retrouve ses drops sans qu'on lui envoie le lien |
| Type de livrable (Video / PDP / Icon / Top-PDP) | StudioAssetKind (media, pas livrable) | ❌ manque |
| Veille concurrentielle | Intelligence (getCompetitorAds, AdCreativeAnalysis) | ✅ existe, plus riche que Notion |
Statuts + Bloque / Blocage | Task.blockedReason + Prospect/CreativeConcept bloquables | ✅ existe |
| KPIs Creative Engine | /[orgSlug]/~/studio/cockpit, gate agency.economics.read | ✅ 11 KPIs sur 11 |
Lecture du tableau, au 21 aout 2026 (fin de la phase 1.5) : la chaine complete — prospecter → qualifier → onboarder → valider un concept → produire → livrer → apprendre — existe dans l'app, avec son vocabulaire en base, ses regles en fonctions pures testees, et un ecran par etape. Une recrue se connecte, voit sa file, et travaille depuis l'interface.
Ce qui reste dans Notion releve maintenant de la methode (§5), plus de l'etat operationnel. Les chantiers ouverts (phases 2 et 3) ne comblent plus un trou : ils font tourner l'usine toute seule (3.8, 3.9), l'ouvrent au marchand qui l'achete (3.11, livre le 22 aout 2026), rendent la Bibliotheque systeme executable (3.10, 19 composants sur 24 au 24 aout 2026, et les 24 sortis de Notion) et referment la boucle sur la memoire (3.12).
2. Le blocage numero un, leve depuis la phase 0
Depuis septembre 2026,
/admin/creative/*n'est plus une vue cross-org. C'est le Studio interne : les cinq memes sections que/[orgSlug]/~/studio/*, lues pour l'organisation dont l'admin connecte est membre (src/features/studio/internal-scope.ts, regle pure danssrc/lib/studio/internal-scope.ts). La vue « toutes les organisations » que decrit le paragraphe ci-dessous a existe d'aout a septembre 2026 ; elle melangeait la production interne et celle des clients, et le proprietaire n'avait rien a approuver dans la file d'un client.loadQcBoard()sansorgIdreste possible dans le code, mais plus aucune page ne l'appelle ainsi.
/admin/creative/qc etait la seule surface de QC, et elle est
derriere requireAdmin(), qui teste User.role === "ADMIN" : un flag
plateforme global.
Ce que ce flag ouvre, en plus de la QC : /admin/revenue/billing-ledger,
/admin/people/organizations (toutes les orgs clientes), /admin/people/users,
/admin/settings/feature-flags, /admin/settings/security,
/admin/marketplace/*. Soit l'integralite de la plateforme et les donnees
de tous les marchands.
Conclusion, telle qu'elle a ete ecrite : aucune des trois recrues ne
pouvait valider une creative sans devenir administrateur de toute la
plateforme. Ce n'etait pas un manque de fonctionnalite, c'etait une
frontiere de securite, et tant qu'elle n'etait pas deplacee le reste du
plan n'avait pas de porte d'entree. Elle l'a ete en phase 0 (§3.1) :
la QC de l'agence vit sur /[orgSlug]/~/studio/qc, gatee par
agency.qc.review, et /admin/creative/qc ne reste que la vue
cross-org de la plateforme. Ce paragraphe est reste au present pendant
des mois apres la levee du blocage qu'il decrit.
3. Les chantiers, par ordre de deblocage
Phase 0 — ouvrir la porte (les 3 recrues peuvent travailler) : ✅ complete
3.1 Les permissions Studio : ✅ livre (backlog/_archive/security-identity/0012)
PERMISSION_REGISTRY porte les permissions du process en DEUX familles
depuis l'ADR 0013 : sept agency.* (agency.prospect.*,
agency.client.*, agency.qc.review, agency.cadence.manage,
agency.economics.read) qui sont le metier de la plateforme, et les
studio.* (studio.concept.*, studio.asset.create, studio.qc.read,
studio.drop.read|publish, studio.gate.read, studio.economics.read)
qui sont la production, vendue au marchand. Plus quatre presets de
role metier : sales, client_ops, strategist, producer : references
par id depuis OrganizationMember.permissions.studioRoles. Aucun enum,
aucune migration.
Ce « ✅ livre » a ete a moitie faux pendant un an, et c'est la moitie qui comptait. Les quatre presets etaient LUS par le resolveur (
permissions.ts,studioPresetPermissions) et ECRITS par aucun code de production :grep studioRoleshors tests ne rendait que le resolveur et ce paragraphe. Pire, leur unique ecrivain possible,setMemberPermissions, reconstruisait{allow, deny}et detruisait le champ au passage — poser un preset a la main en base puis cocher une case dans la matrice admin faisait disparaitre le metier de la personne sans un mot.Le mecanisme cense debloquer le recrutement n'avait donc pas de porte d'entree, pendant que cette page annoncait que les recrues pouvaient travailler. La destruction est corrigee (
setMemberPermissionsfusionne desormais), et le fait qu'une permission puisse etre accordee par l'organisation elle-meme plutot que par un admin plateforme reste a livrer : c'est la phase 3 deacces-delegue-et-frontiere-studio.md, et c'est un manque juridique autant qu'ergonomique — l'autorisation ecrite prealable du responsable de traitement ne peut pas etre donnee par un tiers.
Trois invariants sont verrouilles par les tests
(src/lib/security/permissions.test.ts, studio-guard.test.ts) :
- aucune permission
studio.*n'est dans une baseline de role non-owner : nimember, ni memeadmin. Une organisation qui ne fait pas de Studio ne peut pas donner des pouvoirs creatifs a ses membres par le seul fait de l'appartenance ; agency.cadence.manageetagency.economics.readne sont dans aucun preset : abonner une marque a une cadence est un engagement commercial, et la marge par marque n'est pas une donnee de production. Owner, ouallowexplicite ;denybat un preset : retirer un acte a une personne ne demande pas de demonter son metier. Les presets se cumulent, et le preset est porte par l'adhesion, pas par la personne : le meme utilisateur peut etre producteur dans une org et simple membre dans une autre.
L'entree de navigation suit (backlog/_archive/design-system/0019) :
l'onglet Studio du contexte organisation n'apparait que pour un membre
portant au moins une permission studio.*. La reponse est resolue
serveur, dans le layout d'org qui connait deja le membre, et voyage sur
le ServerOrgSnapshot : le shell est un composant client et ne paie pas un
aller-retour par rendu pour decider de dessiner un onglet. Cacher l'entree
reste de l'ergonomie : la page se garde elle-meme. La liste de permissions
existant a deux endroits (layout et page), un test structurel
(src/test/studio-nav-conventions.test.ts) verifie qu'elles restent
identiques : une divergence ferait mentir l'onglet dans un sens ou dans
l'autre.
Le gate serveur est requireStudioPermission(orgId, permission)
(src/lib/security/studio-guard.ts), compose sur getOrgAccess +
hasPermission. Il repond notFound() (jamais un redirect, jamais un
403) pour qu'un sondage d'URL ne distingue pas « mauvaise org » de
« droit manquant ». getStudioPermissions() resout un jeu de permissions
en une seule lecture d'adhesion, pour les pages qui affichent plusieurs
sections appartenant a des metiers differents.
3.2 Sortir la QC de l'admin plateforme, ✅ livre (backlog/_archive/app-shell/0013)
Le board de production vit a /[orgSlug]/~/studio/qc, scope a
l'organisation, et chaque section y est gatee separement : une section que
l'acteur ne porte pas est absente, pas grisee. Un producteur voit la
file de revue et la file de livraison ; la marge par marque n'est pas sur
sa page.
La route /admin/creative/qc survit en vue cross-org pour
l'operateur plateforme. Les deux surfaces partagent un seul chargeur
(features/studio/board.ts, parametre par orgId) et un seul jeu d'actions
(features/studio/actions.ts) : une seconde copie serait l'endroit exact ou le
filtre org disparaitrait en silence.
Les actions ne font plus confiance au garde de la page. Chacune re-resout
l'organisation depuis le store cible (features/studio/guard.ts) avant de
toucher Prisma, et accepte un admin plateforme comme repli nomme,
enregistre sur la ligne d'audit (changes.via) avec targetOrgId. Neuf
verifications structurelles dans src/test/creative-qc-conventions.test.ts
empechent la frontiere de se rouvrir : dont « aucune action n'appelle
requireAdmin » et « chaque action ouvre sur un garde requireStudioActor* »,
verifiees corps par corps pour qu'une nouvelle action ne puisse pas se
reposer sur le garde de sa voisine.
3.3 Le travail assignable (✅ livre (backlog/_archive/data-platform/0014)
Task porte desormais assigneeUserId (un humain, distinct de
assigneeAgent qui nomme le specialiste IA) une tache peut avoir les
deux : l'agent redige, la personne repond du resultat), dueAt, et
blockedReason.
blockedReason n'est pas un statut : bloque est orthogonal a
l'avancement. Une tache peut etre IN_PROGRESS et bloquee, et un statut
qui aurait avale l'information aurait perdu ou le travail en est
reellement. Une raison vide debloque au lieu d'enregistrer un blocage
sans contenu, « bloque, raison : (rien) » est exactement l'etat que le SOP
existe pour empecher.
Les lectures de « Ma file » vivent avec les actions (listMyTasks,
listUnassignedTasks), pas dans la page : le travail non assigne est une
question differente (« qu'est-ce que personne ne tient ? »), posee par
qui assigne : les melanger transforme une file personnelle en to-do
partagee.
Task.orgId et storeId nullable sont reportes : ils ne servent qu'aux
taches org-level, qui n'existeront qu'avec 0016 et 0018, et les rendre
possibles aujourd'hui toucherait cinq piliers a vide. Le filtre org passe
par la relation (where: { store: { orgId } }), donc sans colonne
denormalisee ni derive possible.
3.4 « Ma file », ✅ livre (backlog/_archive/app-shell/0015)
/[orgSlug]/~/me, l'equivalent de 01 — Operations quotidiennes : mes
taches ouvertes triees par echeance puis priorite, chaque blocage affiche
avec sa raison (un badge sans la raison deplacerait la question au
lieu d'y repondre), l'attente Studio en compteurs, le tas non assigne, et
ce que j'ai cloture sur 7 jours.
Tout est derive : aucune table, aucun statut tenu a la main. Deux
gates : le tas non assigne derriere workspace.write (« qu'est-ce que
personne ne tient » est la question de qui assigne, pas de tout le monde),
l'attente Studio derriere les permissions du board.
Une section dont la source n'existe pas encore ne s'affiche pas :
prospects a relancer (0016), concepts a valider (0017), clients
incomplets (0018) arriveront avec leur modele. Un panneau vide laisserait
croire que la donnee manque alors que c'est l'entite qui n'existe pas.
Phase 1 — encoder ce que seul Notion sait
3.5 Le pipeline commercial, ✅ modele livre (backlog/_archive/data-platform/0016)
Modele Prospect (org-scoped) : marque, site, produit, contact, canal,
angle personnalise, score /100 avec la grille du SOP (produit demontrable
20, creas ameliorables 20, potentiel UGC 15, angles 15, site 10, preuves
10, contact 10), priorite A/B/C derivee, 8 statuts, prochaine action,
prochaine relance, blocked/blockedReason. Conversion Prospect → Store en une operation :
setProspectStatus refuse WON, parce qu'un statut gagne atteignable seul
produirait une marque sans store et un handoff reduit a une etiquette.
L'angle personnalise et le score survivent au passage : c'est le contexte
dont l'onboarding a besoin, et une version en deux temps le perd.
La surface est arrivee en phase 1.5 : /[orgSlug]/~/studio/pipeline
(backlog/_archive/app-shell/0021).
3.6 Le concept creatif et l'apprentissage, ✅ modeles livres (backlog/_archive/data-platform/0017)
CreativeConcept : le concept cesse d'etre une string libre sur
StudioAsset et devient une entite validable (avatar, awareness, funnel,
angle, douleur/desir/objection, declencheur, hooks, formats, score, statut
Recherche → Brouillon → A valider → Valide). StudioAsset.conceptId la
reference. CreativeLearning ferme la boucle et devient le writer que
StudioAsset.performance n'a jamais eu : le snapshot est derive du
learning, jamais passe a cote, donc la colonne et la ligne ne peuvent pas
raconter deux histoires. Toutes les metriques sont optionnelles, le SOP
interdit d'inventer un chiffre, mais un learning doit finir sur une
hypothese ou un prochain test, sinon c'est un post-mortem et la boucle ne
tourne pas.
La surface est arrivee en phase 1.5 : /[orgSlug]/~/studio/concepts
(backlog/_archive/app-shell/0022), d'ou se saisit aussi le learning.
3.7 Le gate Go/No-Go client (✅ modele + regle livres
(backlog/_archive/data-platform/0018)
Sur le Store : score onboarding /100 derive d'une grille pure (brand data
15, produit/offre 20, assets 15, >=2 avatars 15, >=3 angles 15, claims
verifiables 10, CTA+plateforme+objectif 10), assetsReady,
claimsValidated, capacityValidated, marginViable,
oneShotFoundation[] vs contentFactory[], et goProduction) le seul
horodate et attribue, parce que les cases sont un etat de travail et le GO
est un engagement.
Deux regles font la difference entre un gate et une formalite :
- Les lignes comptables sont comptees, pas declarees. Les avatars et
les angles se lisent depuis les
CreativeConceptvalides : en distinct, donc trois concepts ecrits pour la meme persona valent un avatar. Sous le plancher du SOP la ligne vaut zero, pas la moitie : « un avatar » n'est pas « la moitie de deux avatars », c'est une marque pour laquelle on ne sait pas varier un message. setCreativeCadencerefuse une cadence sans GO, en nommant chaque condition manquante. Couper une cadence reste toujours possible : le gate qui dit qu'une marque doit s'arreter ne doit jamais etre ce qui l'y enferme. Retro-compatible par construction — toutes les colonnes sont optionnelles ou defaultees, le croncreative-drop-tickne lit toujours quecreativeCadence, donc une marque deja livree continue de l'etre.
La surface est arrivee en phase 1.5 : /[orgSlug]/~/studio/clients
(backlog/_archive/app-shell/0023).
Phase 1.5 — les trois ecrans que les modeles n'avaient pas : ✅ complete
Les items 0016-0018 ont livre des modeles sans page, deliberement. Les
items 0021-0023 sont ces pages, plus le cadre qui les relie.
Le Studio devient quatre sections sous /[orgSlug]/~/studio, chacune
gatee par sa permission et absente, jamais desactivee, pour qui ne la
porte pas. L'onglet d'en-tete pointe desormais sur /~/studio, une porte
d'entree qui redirige chaque membre vers la premiere etape qu'il detient :
il pointait droit sur /qc, ce qui devenait un cul-de-sac des qu'un sales
cliquait dessus.
| Section | Route | Ouvre avec |
|---|---|---|
| Pipeline | /~/studio/pipeline | agency.prospect.read |
| Clients | /~/studio/clients | agency.client.read |
| Concepts | /~/studio/concepts | studio.concept.read |
| Creative QC | /~/studio/qc | agency.qc.review · drop.publish · cadence.manage · economics.read |
Trois regles tenues par des tests structurels
(src/test/studio-surfaces-conventions.test.ts) :
- Aucune page ne charge avant d'autoriser. Le test compare les appels,
pas les imports : sa premiere version mesurait l'ordre des lignes
importet passait quoi que fasse la page. - Les scores restent derives. Ni
onboardingScoreni la priorite prospect n'ont de champ de saisie ; les formulaires importent les grilles pures plutot que de recopier une formule qui divergerait. - Aucun controle n'offre un choix que l'action refuse.
WONest absent du selecteur de statut :setProspectStatusle rejette, la conversion passe parconvertProspectqui cree le store dans la meme operation.
Les metriques d'un learning restent optionnelles dans le formulaire : le SOP interdit d'inventer un chiffre, et un champ requis en produit des inventes, indiscernables d'une mesure une fois stockes. Ce qui est exige, c'est une hypothese ou un prochain test, sinon la boucle ne tourne pas.
Au passage, conceptReadiness a quitte concept-actions.ts pour un module
pur : c'etait la seule des trois grilles du SOP a ne pas en etre un, et un
export synchrone depuis un fichier "use server" n'est pas ce que cette
directive veut dire. Elle vit aujourd'hui dans
lib/studio/concept.ts, a cote de la
taxonomie et pour la meme raison : deux mondes la lisent, le dashboard, qui
demande ce qu'un humain doit encore fournir, et le runtime IA, qui ecrit un
concept et rapporte ce qui manque.
Phase 2 — l'usine tourne seule: 🟡 3.8, 3.9 et 3.11 livres, 3.10 a 19/24
3.8 La cadence cree du travail, pas seulement un DRAFT : ✅ livre
(backlog/_archive/platform-ops/0024)
Le tick ouvre le drop et cree trois Task, une par etape du cycle
(strategie → concepts, production → livrables, QA → publication), portant
la meme echeance. StudioDrop.dueAt stocke enfin cette echeance, que le
tick connaissait et jetait : d'ou l'impossibilite de calculer « livre a
l'heure ».
L'assignation n'a lieu que s'il y a un proprietaire evident : un seul
porteur du preset → la tache lui revient ; zero ou plusieurs → le tas non
assigne, que /~/me affiche deja. Pas de round-robin invente. Le
proprietaire de l'org ne compte pas comme porteur unique : il a toutes les
permissions, donc le compter assignerait tout le cycle au fondateur.
Task.createdBy vaut system:creative-drop-tick, jamais un id emprunte :
attribuer a un humain un travail cree par un cron fait mentir l'audit.
3.9 Le cockpit agence, ✅ livre (backlog/_archive/app-shell/0025)
/[orgSlug]/~/studio/cockpit, garde par agency.economics.read : les
11 KPIs du SOP, tous derives au moment de la lecture, aucune table de
snapshot, aucun compteur incremente a l'ecriture. Un KPI stocke derive du
travail qu'il pretend mesurer : meme argument que le score d'onboarding,
applique a l'agence au lieu d'un client.
Deux regles portent la valeur du tableau :
nulln'est pas0. Un KPI sans donnee affiche « no data ». Zero est une mesure : l'imprimer sur une periode vide affirme que l'equipe n'a rien produit alors que personne ne lui avait rien demande.- Le denominateur d'un taux exclut ce qui n'est pas decide. Un lot en attente de revue n'a pas echoue, et le compter comme tel fait passer chaque bonne semaine pour un effondrement.
Chaque KPI se compare a la periode precedente, et la fleche lit
higherIsBetter plutot que le signe : un cout qui baisse est bon, un
throughput qui baisse ne l'est pas.
3.10 La Bibliotheque systeme devient executable, 🟡 19 composants sur
24 (backlog/ai-platform/0028, backlog/app-shell/0029,
backlog/app-shell/0030, backlog/app-shell/0031,
backlog/app-shell/0032, backlog/app-shell/0033)
Un composant ne doit jamais exister aux deux endroits : deux copies
d'une regle divergent, et plus personne ne sait laquelle fait foi. Chaque
composant cable sort donc de Notion.
Lecture composant par composant de la data source 794b3bd2 au 23 aout
2026 :
Le detail composant par composant vit desormais dans
studio-system-library.md— une ligne par entree Notion, avec le chemin exact dans le code. Ce paragraphe raconte, ce fichier-la fait foi, etsrc/test/studio-library-register.test.tsverifie que le chiffre ci-dessus en est derive.Le tableau agrege qui vivait ici a ete supprime pour cette raison. Il comptait
2 + 12 + 5 + 2 + 5 = 26destinations pour 24 composants, et rangeaitOffer Architecturedans « reste a cabler » alors queStore.oneShotFoundation[]/contentFactory[]et la conditionofferScopedu Go/No-Go le cablent depuis l'item 0029. Deux erreurs dans un tableau tenu a la main, trouvees le jour ou on l'a derive.
| Destination | Nb |
|---|---|
| Regles pures (code) | 7 |
| Instructions de skill (prompt) | 12 |
| Orchestration, documentee et radiee de Notion | 5 |
Le decoupage suit le livrable, pas la ligne Notion. skills/router.ts
selectionne un seul skill par message : douze petits skills se
seraient disputes hook, angle, avatar, et le classifieur aurait
tranche au hasard, en payant un aller-retour Haiku a chaque fois. D'ou
deux destinations seulement :
creative-adsabsorbe la chaine strategique du SOP a l'endroit ou elle travaille, pas en annexe : la veille concurrentielle dans son etape OBSERVE, les Avatar Cards / Matrice Avatar × Angle / Hooks dans IDEATE, le script video 10–15 s dans CREATE, la QA video dans QC.pdp-conversionest nouveau : le module PDP (architecte de galerie, brief slide par slide, systeme d'icones, QA en 17 points) decrivait un livrable vendu et n'avait aucune surface d'execution.
Ce qui est migre l'est litteralement : 12 champs d'Avatar Card, 14
familles d'angles fermees, 10 hooks en 7 types, 7 sorties de script, 15
points de QA video, 6 a 10 slides, 17 points de QA PDP.
src/test/studio-library-migration.test.ts les compte — une prose
peut s'eroder en douceur (« quelques hooks » la ou le SOP dit dix) sans
que personne le voie, un test qui compte, non.
Quatre trous nommes en migrant, hors scope de 0028 :
-
Le gate Go/No-Go est plus faible que le SOP.Corrige par l'item 0029.productionReadinessverifie desormais 11 conditions pour les 12 lignes de la checklist. Les quatre ajoutees — Brand System exploitable, cadence tranchee, responsables nommes, DoD convenue : sont actionnables depuis/~/studio/clientset lisibles cote marchand.Deux decisions de modelisation portent ce correctif :
cadenceDecidedest un drapeau sur la DECISION, pas sur la valeur.creativeCadencea un defaut non-nul (NONE), donc « pas de cadence, deliberement » et « personne n'a tranche » s'ecrivent pareil. Exiger!= NONEbloquerait un client socle-seul legitime.oneShotFoundation/contentFactoryn'ont pas ce probleme : un tableau vide n'est pas une decision, donc « au moins un des deux non vide » est deja la bonne regle.- Les points 6 et 7 du SOP restent fusionnes en un
offerScope.Logique — Offer Architecturevend explicitement les deux formes separement ; exiger les deux refuserait une offre que l'agence vend. D'ou 11 conditions pour 12 lignes, et non un oubli.
-
brandsystem.mdest deja reclame par le Brand Kit wizard (create-shopify-brand). Il lui manque les champs de production creative du SOP : claims autorises, claims interdits, objections, declencheurs d'achat, winners existants. -
Deux collisions de declencheurs preexistantes,
onboarding(ship-shopify-store vs store-setup) ettracking(seo-audit vs tracking-audit), epinglees par le test : la liste peut retrecir, jamais grandir. -
La taxonomie North Star a neuf termes, l'asset en portait sept.Corrige par l'item 0030 : voir ci-dessous.
La Creative Taxonomy : item 0030
Product × Marketing Avatar × Angle × Awareness × Funnel × Placement × Format × Hook × Result
Neuf termes. StudioAsset en portait sept : funnel vivait sur
CreativeConcept et pas sur l'asset (l'etage de tunnel d'un CONCEPT etait
connu, celui de la creative qui en sortait ne l'etait pas) et placement
n'existait nulle part, Store.platforms melangeant les deux notions
(meta a cote de reels). Les deux colonnes ont rejoint la table.
Trois decisions portent l'item, et elles se defendent chacune seule :
- Ca ne bloque pas. Le
Seuil / Gatedu composant Notion est vide : la taxonomie demande a etre coherente et tracable, pas a etre bloquante. Refuser unsaveStudioAssetinventerait une regle que le SOP ne pose pas, et perdrait un asset deja ecrit dans Blob. Meme forme queunmetered(item 0010) : mesurer et exposer. L'outil renvoietraceableettaxonomyMissing, le tableau QC compte, un humain tranche. Resultn'est pas un manque. Une creative generee il y a une heure n'a pas encore de performance ; la compter comme incomplete marquerait toute la file comme non tracable le jour ou le chiffre compte le plus. Huit termes sont exigibles a l'ecriture, le neuvieme arrive avec les resultats.- Une seule orthographe par terme.
CreativeConcept.awarenessstockaitPROBLEM(enum Prisma) quand la creative qu'il produisait stockaitproblem(chaine libre) : rien ne les joignait, ce qui est precisement pourquoi il fallait le corriger avant que quelque chose essaie. L'outil accepte encore les deux (durcir l'enum ferait rejeter l'appel entier par zod, donc perdre l'asset) et normalise a l'ecriture.
Le module vit dans src/lib/studio/taxonomy.ts, pas dans features/studio/
avec les autres regles pures : deux mondes le lisent, le dashboard qui
compte et le runtime IA qui ecrit. La raison donnee ici etait « rien hors de
(dashboard) n'importe _studio » — c'etait deja faux quand la phrase a
ete ecrite (features/ai/tools/studio-tools.ts importait ces loaders), et
app-shell/0144 a retire le route group de la question. Le placement tient
desormais sur une regle verifiee : pnpm fleet:boundaries fait de
src/lib le plancher qu'aucune feature ne remonte, donc une regle pure lue
par features/studio ET features/ai vit la, sinon la lecture de l'une
devient une arete inter-features. Le filtre de comptage est derive de la
liste des termes, jamais ecrit a la main : un audit qui cesse silencieusement
d'auditer une colonne affiche zero et se lit comme propre.
La QC avant l'upload, et la matrice de variantes : item 0375
La taxonomie ci-dessus mesure et expose, et c'est la bonne regle. Elle laissait pourtant deux depenses sans contrepartie.
La liste des claims existait et rien ne la lisait. brand-system.ts
normalise allowedClaims / forbiddenClaims depuis
StoreContext.modules.brandKit depuis l'item 0034 ; le prompt
creative-ads ordonne « aucun claim invente » ; la seule chose qui
pouvait comparer les deux etait un modele a qui on ne donnait pas la
liste. lib/studio/qa-gate.ts la lui donne, et tourne avant
storage.upload : c'est le seul endroit ou un refus est encore possible,
puisque une fois les octets dans Blob refuser perd un asset qui existe.
La regle du gate est plus etroite que celle de la taxonomie, deliberement :
seul un claim interdit bloque. Tout le reste sort en qaWarnings et
l'asset est sauve, exactement comme taxonomyMissing. Un claim non
enregistre n'est pas un claim interdit — sinon une liste vide devient un
arret de production, et la reponse humaine fiable a un arret de production
est de cocher la case, pas de remplir la liste.
Une variante n'etait pas distinguable d'un re-rendu. « Fais-moi
quelques variantes » est la demande la plus frequente apres une creative
qui plait, et sans cadre elle produit quatre visuels ou le hook, l'angle
et le plan d'ouverture ont bouge ensemble : l'un gagne, personne ne sait
quoi, et le lot suivant repart de zero. lib/studio/variants.ts +
planCreativeVariants posent la contrainte a un endroit ou elle est
verifiable plutot que demandee : une seule variable bouge, et le test
l'assert directement au lieu de faire confiance a l'appelant, qui est un
modele de langage.
Les axes offerts sont les termes de la taxonomie ci-dessus, et c'est la meme raison qui fonde les deux : ce sont les seules dimensions sur lesquelles le registre sait regrouper un resultat. Faire varier ce que la table ne stocke pas produit un rendu, jamais une lecture. Le produit n'est pas un axe : deux produits sont deux tests, et c'est comme ca qu'un « hook gagnant » se revele etre un produit gagnant.
Le lien vit sur StudioAsset.variantOfId + variantAxis (auto-relation
nullable, SetNull : perdre la base ne doit pas supprimer les variantes
qui lui ont survecu). Sans cette paire la matrice redevient un lot : la
planification peut etre parfaite, si le lien n'atteint pas la ligne rien
n'a ete appris.
La Feedback Loop : item 0031
WINNER = Avatar + Angle + Hook + Format + Offer + Product
LOSER = meme taxonomie + hypothese sur la faiblesse
NEXT TEST = une variable principale a modifier
Cinq defauts, et le premier explique les autres.
Un WINNER n'avait pas d'identite, parce qu'on le notait au mauvais
niveau. LearningForm ecrivait toujours conceptId, jamais assetId :
alors que recordLearning acceptait les deux depuis le premier jour. Or un
CreativeConcept porte hookIdeas[] et recommendedFormats[] : des
pluriels, des candidats. Le hook et le format reellement diffuses ne
vivent que sur le StudioAsset. L'app savait donc dire « ce concept a
gagne » et jamais quelle creative avait gagne, et « decliner le
winner », le rituel hebdomadaire du SOP, n'avait rien a decliner. Meme
forme que le defaut corrige par l'item 0022 : un writer qui existe, aucun
ecran pour l'atteindre.
Rien ne relisait la boucle. nextTest, hypothesis, whatWorked
etaient ecrits, comptes par le KPI « learnings actionnables » du
cockpit, et jamais affiches. Le KPI prouvait que la phrase EXISTAIT et ne
disait rien de ce qu'elle disait. La section /~/studio/concepts les
affiche enfin, en trois listes : winners a decliner, losers a remplacer,
next tests ouverts. Elle vit la et pas derriere une septieme route Studio
parce que decliner un winner, c'est ecrire le concept suivant : meme
ecran, meme permission, meme personne.
whatFailed n'avait pas de champ : la colonne et l'action l'avaient,
le formulaire non, donc un LOSER ne pouvait pas porter sa faiblesse.
Trois des dix metriques n'existaient pas : cpmUsd, taux d'ajout au
panier, taux de conversion. La liste des metriques est desormais derivee
d'un seul endroit (LEARNING_METRICS) : elle avait justement derive parce
qu'elle etait ecrite a la main a trois endroits.
« Ne jamais conclure a partir d'une metrique indisponible », la seule
regle dure du composant. Elle n'interdit pas d'enregistrer : le qualitatif
est sur sa propre liste de donnees valides. Elle interdit qu'un avis se
lise comme une mesure. D'ou measured / asserted, compte a part et
affiche, jamais bloquant : le Seuil / Gate est vide, comme pour la
taxonomie.
Deux decisions de modelisation portent l'item :
offervit sur le learning, pas sur l'asset. La meme video 9:16 tourne plein tarif en semaine 1 et a -20 % en semaine 2 et produit deux resultats differents : l'offre est une propriete du test, pas du fichier. Elle ne rouvre donc pas les neuf termes que l'item 0030 a fixes. (Le SOP porte deux taxonomies divergentes : 9 termes sans Offer cote Creative Taxonomy, 10 avec Offer et Proof cote Creative Equation. Cet item ne tranche pas, il place chaque terme la ou il est vrai.)nextTestDoneAtest un drapeau sur la DECISION. Rien ne relie une creative au learning qui l'a provoquee ; deduire « fait » de l'apparition d'un nouvel asset fermerait des tests que personne n'a menes, et la fermeture serait ensuite indistinguable d'une vraie. Un humain coche. Sans ce champ la liste des tests ouverts ne ferait que grandir : l'exact defaut d'affordance decrit plus bas.
3.11 Surface Studio cote marchand : ✅ livre
(backlog/_archive/app-shell/0027)
/[orgSlug]/[storeSlug]/studio, onglet studio de la barre store. Le
marchand voit sa marque : le verdict de gate et ce qui manque, les
drops publies avec leur lien de partage et leur retard eventuel, ce qui est
en preparation, ses concepts avec leur taux d'approbation, et son economie
sur 30 jours.
Trois decisions portent cette surface.
- Une boutique, les memes chargeurs.
loadQcBoard,loadConceptBoardetloadClientGatesont appris un scopestoreId(dans la clausewhere, jamais en filtre memoire) au lieu de recevoir des copies. Une seconde implementation de la meme arithmetique est exactement ce queboard.tsdit exister pour empecher, et c'est la que le filtre org disparait sans que personne le voie. SeulloadStoreDeliveriesest nouveau : personne ne chargeait les drops publies, donc le livrable de toute l'usine etait le seul artefact sans page dans l'app. - Des resultats, pas des controles. Toutes les sections sont en
lecture. Approuver une creative, valider un concept et lever la gate
restent sur la surface agence, ou vit le contexte de la decision, et
pour un marchand solo cette surface est sa propre org, donc rien n'est
hors de portee. Afficher une file de verdicts sans les boutons de verdict
serait le defaut de
/~/meque l'audit d'aout 2026 vient de corriger. - Traduite. La console de l'agence peut vivre en anglais ; une surface
vendue dans six locales, non. Le loader ne fait aucune fusion de
fallback : une cle absente en
frjette pour les seuls utilisateurs francais.
Le meme mot dans deux contextes, deux perimetres : l'onglet org ouvre le
tableau de l'agence sur toutes les marques, l'onglet store ouvre cette
marque-la. studio.* reste une permission d'organisation dans les deux
cas, donc serverOrg.studioAccess repond pour les deux, et
src/test/store-studio-conventions.test.ts echoue si les deux
destinations se confondent.
Phase 3 — l'amelioration continue
3.12 La boucle d'apprentissage alimente la memoire. Un
CreativeLearning marque Gagnant doit ecrire un MemoryEvent / StoreFact
pour que le prochain brief parte de ce qui a deja gagne. C'est la seule
forme d'« auto-entrainement » qui soit honnete : pas de fine-tuning, une
memoire structuree que le skill relit. Le socle existe
(docs/architecture/memory-layer.md).
3.13 L'audit permanent. Le repo a deja codebase-auditor,
pnpm docs:claims, le schema guard et pnpm fleet:scope. Etendre
check-doc-claims.mjs aux affirmations du Studio (nombre de permissions
studio.*, presets de role, motifs de rejet) pour que la doc ne puisse
plus deriver silencieusement : comme elle l'a fait sur le modele image.
4. Les actions operateur, hors code
Trois choses bloquent sans qu'aucune ligne ne soit a ecrire.
| Action | Effet | Ou |
|---|---|---|
HIGGSFIELDS_MCP_URL + HIGGSFIELDS_MCP_TOKEN | Plus une action bloquante depuis ai-platform/0345. La ligne disait « debloque video / voix / lipsync / vecteur » et que sans elles « l'offre 8 videos/mois du SOP n'est pas productible » : la video part desormais en direct sur Veo 3.1 (AI Gateway), la voix-off est generee DANS le clip, le vecteur est ecrit par le modele, et le lipsync n'a jamais eu de route — il n'en a toujours pas, avec ou sans ces cles. Le serveur Higgsfields reste un add-on optionnel. | — |
| Creer l'org « BoostEcom Studio » et y inviter les 3 recrues | Le tenant de l'agence existe. Les invitations + emails existent deja. | /[orgSlug]/~/members |
Entrer les 3 marques du SOP comme Store | Nacre Bijoux, Valise Demo, Boutique Demo deviennent des clients operables. | /[orgSlug] |
5. Ce qui ne migre pas
Tout ne doit pas quitter Notion, et le dire evite un chantier inutile.
- Le SOP complet reste la reference methodologique. Ce qui migre est l'etat operationnel (qui fait quoi, ou en est-on, ce qui bloque), pas la doctrine. Un playbook de role est un document, pas une table.
- Le glossaire, les modeles de fiches, la certification pre-production
sont de la documentation d'equipe. Ils ont leur place dans Notion ou dans
docs/, pas dans Postgres. - La Bibliotheque systeme migre composant par composant, et seulement quand un composant devient executable (§3.10). Un prompt jamais execute par du code n'a rien a faire dans le repo.
La regle : ce qui a un statut migre, ce qui a une methode reste.
6. Par ou on pilote
Le §1 dit quoi correspond a quoi. Celui-ci dit par ou on agit dessus : la question que ce document ne repondait pas, et celle qu'on pose en premier quand on arrive.
| Surface | Etat | Ce qui existe |
|---|---|---|
| Panel agence / admin | ✅ | 7 routes : ~/studio/{pipeline,clients,concepts,qc,cockpit}, la porte d'entree ~/studio, et [storeSlug]/studio cote marchand |
| Server actions | ✅ | 4 fichiers d'ecritures dans features/studio/ — actions, concept-actions, gate-actions, prospect-actions |
| API | ✅ | GET /api/organizations/[orgId]/studio/[section], une seule table de correspondance dans lib/security/studio-sections.ts |
| Chat / Atlas | ✅ | Lecture dans studio-tools.ts, ecriture dans studio-write-tools.ts — meme porte, meme via, meme ligne d'audit que les server actions |
| MCP | ✅ | boostecom:studio.read → getStudioSection. Le Studio est le seul systeme natif du catalogue |
Autrement dit : le Studio est pilotable par les quatre portes. Un @Atlas
ne se contente plus de dire ou en est la production : il peut la faire avancer,
a travers les memes gardes que la souris.
Ce tableau a annonce ❌ sur ses trois dernieres lignes bien apres leur livraison (
app-shell/0082,ai-platform/0081,integrations/0083). Un tableau d'etat qu'on ne rejoue pas devient une liste de travaux deja faits. C'est exactement ce que le §3.13 de ce document existe pour empecher, et c'est l'argument le plus fort qu'il en reste a faire. L'etat courant se derive :node scripts/four-doors.mjs.
Le seul MCP dans le tableau va dans l'autre sens
HIGGSFIELDS_MCP_URL est un MCP que le Studio consommerait s'il etait
configure. Ce n'est pas le Studio expose : la confusion est facile et elle
inverse le sens de la fleche. Depuis ai-platform/0345 il ne produit plus
rien d'indispensable — c'est un add-on, la production passe par l'AI Gateway.
Les trois portes d'autorisation
Un lecteur qui vient brancher une nouvelle surface doit savoir contre quoi il autorise. Il y en a trois, et une seule accepte un admin plateforme :
| Porte | Repli admin plateforme |
|---|---|
lib/security/studio-guard.ts — lectures de page | non |
features/studio/guard.ts — creatives d'un store | oui, enregistre en via |
features/studio/prospect-actions.ts — mutations prospects | non |
Le repli est l'exception, pour une raison ecrite dans guard.ts : un
operateur a un motif legitime de debloquer une file de production. Le detail et
la decision vivent dans backlog/_archive/security-identity/0051, et
src/test/studio-authorization-doors.test.ts echoue si l'ensemble change.
Toute nouvelle surface doit passer par ces portes, pas en ouvrir une quatrieme. C'est la condition pour que « pilotable depuis le chat » ne veuille pas dire « pilotable sans droits ».
Les outils IA de lecture le font, et c'est la seule chose difficile de ce
chantier. Les deux couches n'autorisent pas dans le meme langage : un outil
@Atlas branche sur canRead(ctx.userRole) connait quatre roles, la ou le
Studio en accorde onze avec un override par membre — le Studio serait tombe
dans les mains de tout member. studio-tools.ts appelle donc
studioAccessFor(), la moitie sans session de getStudioAccess(), avec
l'appelant que AtlasToolContext porte deja. Le chat est un nouvel
APPELANT de la porte, pas une nouvelle porte : c'est cette distinction que
studio-authorization-doors.test.ts protege, et il n'a pas eu a changer.
L'ecart avec la cible
La cible : tout doit etre pilotable depuis le MCP, l'API, le Chat cote utilisateur et le panel cote agence, et rien ne doit rester dans un coin sans etre relie.
Le Studio en couvre quatre sur quatre. Les quatre items decoupes depuis
inbox/0073 sont livres et archives : ai-platform/0080 (lecture chat),
ai-platform/0081 (ecriture chat), app-shell/0082 (API) et
integrations/0083 (scopes MCP).
Une nuance qui n'est pas une case a cocher : l'API et le MCP sont en lecture. L'ecriture passe par le panel et par le chat, qui partagent les memes portes ; l'ouvrir a un programme tiers est une decision produit, pas un reste de chantier.
L'ecart restant du Studio n'est plus une porte, ce sont les manques de §1 et §3 : le type de livrable non modelise, la 5e valeur de cadence, la bibliotheque a 19 composants sur 24, la boucle §3.12 et le garde de §3.13.
7. Dependances entre chantiers
3.1 permissions ──┬─→ 3.2 QC hors admin ──┐
│ ├─→ 3.8 cadence cree du travail
3.3 Task humain ──┴─→ 3.4 Ma file ────────┘
└─→ 3.9 cockpit agence
3.5 Prospect ─────→ 3.7 Go/No-Go ──→ 3.6 Concept ──→ 3.12 memoire
└─────────→ 3.11 surface marchand
3.1 et 3.3 sont independants et parallelisables. Rien d'autre ne demarre avant eux.