ArchitectureCarte des routes

Carte des routes

Deplace depuis la racine CLAUDE.md / AGENTS.md par platform-ops/3048 : cette carte est lue a la demande, plus chargee a chaque session. Les blocs encadres restent DERIVES du disque par leurs gardes, qui lisent desormais…

Deplace depuis la racine CLAUDE.md / AGENTS.md par platform-ops/3048 : cette carte est lue a la demande, plus chargee a chaque session. Les blocs encadres restent DERIVES du disque par leurs gardes, qui lisent desormais ce fichier : claude-md-marketing-routes, claude-md-minimal-routes, ops-pages-name-their-permission, et les claims de pnpm docs:claims (handlers d'API, webhooks, crons, slugs /features, Systems, outils MCP).

Routes

Marketing (public)

/                                      # Landing — hero chat + AI Teams carousel
/about                                 # Mission + founder bio. Chaque chiffre et chaque liste de la
                                       #   page sont LUS depuis le code (AGENTS, AUTONOMY_LEVELS,
                                       #   FEATURE_GROUPS, SYSTEMS, routing.locales, TOOL_KEYS) et
                                       #   gardes par about-page-claims.test.ts, qui refuse aussi
                                       #   qu'une page fille de (marketing)/about ne soit pas liee
                                       #   depuis ici. La page recitait « 12 features » pour huit
                                       #   surfaces, et six agents sous des roles que le registre
                                       #   avait renommes (growth-web/0647)
/about/scanner                         # Bot policy des DEUX user-agents sortants, BoostEcom-Scanner
                                       #   (crawler Store Intelligence) et BoostEcom-TrackingScanner
                                       #   (audit tracking a la demande) : les deux citent
                                       #   https://www.boostecom.app/about/scanner (growth-web/3018,
                                       #   qui a clos 0433). Ne JAMAIS renommer cette URL : elle vit
                                       #   dans les logs des sites tiers. Garde :
                                       #   bot-policy-covers-both-agents.test.ts
/contact                               # Contact (topic-routed)
/founding                              # Founding Cohort : page de CANDIDATURE, rien d'autre (offre,
                                       #   places, criteres, formulaire). Les premiers clients
                                       #   payants (25 places et Pro a l'annee par defaut). Compteur
                                       #   COMPTE depuis `Organization.foundingAdmittedAt`, jamais
                                       #   ecrit : founding-counter-is-derived.test.ts refuse un
                                       #   chiffre dans la copie. Places, seuil public, plan, periode
                                       #   et ouverture des candidatures se reglent dans
                                       #   /admin/revenue/founding (section `founding` de
                                       #   PlatformConfig), jamais dans le code. Ni jauge ni
                                       #   organigramme depuis le 2026-09-25 : la cible de
                                       #   financement vit en dollars dans
                                       #   /admin/revenue/platform-costs, avec l'editeur de l'equipe
/thesis                                # These fondateur (The Operational Intelligence Layer
                                       #   for Shopify), lue depuis `config/commerce-intelligence-thesis`
/pricing                               # Plan picker + comparison + credits + FAQ
/compare                               # Index des comparatifs PUBLIABLES, derive de
                                       #   publishableCompetitors() : un brouillon n'y est ni liste,
                                       #   ni lie, ni au sitemap
/compare/[slug]                        # Un comparatif par concurrent, jamais une matrice de paires.
                                       #   Publies : les cinq entrees publiables du registre.
                                       #   Chaque fait sur eux est lu sur LEUR domaine et date, la
                                       #   section « ou ils gagnent » est obligatoire, et la copie de
                                       #   chaque fait existe dans les six catalogues. Garde :
                                       #   comparison-claims-are-sourced.test.ts. La sixieme reste en
                                       #   brouillon : son domaine repond 403 a toute lecture
/blog                                  # Index — listContentEntries("blog"). Etait /insights jusqu'a
                                       #   growth-web/3014 ; l'ancienne URL rend 404 depuis le
                                       #   nettoyage des redirections du 2026-09-26 (aucun
                                       #   utilisateur, rien d'externe ne la citait). Le panneau
                                       #   Insights du cockpit (/[orgSlug]/[storeSlug]/insights)
                                       #   ne bouge pas
/blog/[slug]                           # Article detail (+ /blog/rss.xml)
/changelog                             # Release notes (noindex + hors sitemap tant qu'aucune entree n'est publiee)
/changelog/[id]                        # Entree changelog (COMPLETED seulement, Article JSON-LD)
/roadmap                               # Public milestones + voting. N'affiche que `PUBLIC_ROADMAP_WHERE`
                                       #   (jamais les lignes de la flotte, `growth-web/3001`) ; noindex
                                       #   + hors sitemap sous `ROADMAP_INDEX_FLOOR` lignes (`growth-web/3002`)
/roadmap/[id]                          # Item roadmap detail (statut + votes, Article JSON-LD).
                                       #   TOUJOURS noindex, follow et jamais au sitemap, quel que
                                       #   soit le nombre de lignes (decision proprietaire 2026-09-25)
/status                                # Uptime live + incidents + 90-day bars + subscribe form
                                       #   (public, mais sous le groupe (minimal), pas (marketing) :
                                       #   src/app/(minimal)/status/)
/status/history                        # Report card uptime mensuel sur 12 mois (noindex tant que
                                       #   `STATUS_HISTORY_MIN_MONTHS` mois de donnees ne sont pas reunis)
/status/incidents.rss                  # Flux RSS des incidents (90 derniers jours)
/agents                                # Index — orchestrator + 5 specialists. A absorbe
                                       #   /features/ai-team (ai-platform/3016) : deux pages
                                       #   pour les six memes agents. Libelle de nav « AI agents »
/agents/[slug]                         # Agent profile. Les slugs sont les PRENOMS, plus les roles
                                       #   (ADR 0040) : atlas, maya, marco, otis, faye, sam. Les
                                       #   anciens slugs de role (traffic, conversion, lifecycle,
                                       #   intelligence, retention) rendent 404, sans redirection :
                                       #   ils decrivaient un metier que le routeur ne leur donnait
                                       #   pas, et avaient deja derive une fois (growth-web/0647).
                                       #   Les roles sont ceux du runtime (specialists.ts), pas un
                                       #   second jeu. JSON-LD `SoftwareApplication`, jamais `Person`.
                                       #   Source : src/features/ai/agents/identity-registry.ts
/features                              # Index — 7 features (source : src/config/marketing-ia.ts)
/features/[ai-copilot|mcp|intelligence|studio|systems|launch|workspace]
/features/systems/[slug]               # Un System par page, slug = `id` du registre
                                       #   (`systemPageUrl`, src/features/systems/registry.ts).
                                       #   Ex-/marketplace/systems(/[slug]) : un System est un audit
                                       #   inclus dans les plans, pas une annonce. L'index et les six
                                       #   anciens slugs repondent 308 POUR TOUJOURS (bloc E de
                                       #   next.config.mjs) : l'UA du tracking scanner citait
                                       #   l'ancienne URL dans les logs des boutiques scannees
/features/mcp/connect/[claude|chatgpt|claude-code|cursor]  # Guides connect par client MCP (HowTo JSON-LD), le SEUL
                                       #   how-to par client. `mcp` est le « Shopify MCP » (ex-/features/api,
                                       #   ex-« Bridge AI », growth-web/3015). Les anciennes URLs
                                       #   (/features/api*, /marketplace/cli*, les tutos / posts qui
                                       #   racontaient la meme connexion) rendent 404. Les quatre clients se
                                       #   connectent en OAuth ; la cle statique est le repli sans navigateur
/marketplace                           # Index — apps, themes, extensions, MCPs, stores, agencies, freelancers
/marketplace/[type]                    # Category listing
/marketplace/[type]/[slug]             # Listing detail. JSON-LD par type d'annonce (`LISTING_SCHEMA_KIND`,
                                       #   `marketplace/3005`) : SoftwareApplication, Organization, ProfilePage…
                                       #   jamais un Product sans offre ni note. 308 si le type de l'URL est faux
/marketplace/hire                      # Hire freelancers funnel
/intelligence              → 308      # regle next.config.mjs vers /features/intelligence (plus de
                                       #   page.tsx depuis growth-web/3018)
/intelligence/<category>   → 308      # idem, pour les QUINZE slugs de
                                       #   src/services/discovery/categories.ts seulement, nommes un
                                       #   a un dans la regle : tout autre segment rend un vrai 404
                                       #   (l'ancienne page [category] redirigeait n'importe quoi).
                                       #   Les classements par categorie ne sont PLUS une surface :
                                       #   le moteur browse/rank vit dans le Hub OS de la home
/intelligence/stores/[domain]          # Intelligence record — UNE URL par store (growth-web/3018),
                                       #   construite par `intelligenceStorePath`
                                       #   (services/discovery/store-path.ts), rangee dans
                                       #   `top-stores`. Les quinze formes
                                       #   /intelligence/<category>/<domain> repondent 308 ici POUR
                                       #   TOUJOURS : les sorties des outils MCP qui les portaient
                                       #   sont en cache chez des agents tiers. noindex,nofollow +
                                       #   hors sitemap (juin 2026), joignable pour les liens
                                       #   directs et le MCP. Liens sortants vers les stores espionnes : URL
                                       #   propre SANS UTM + rel nofollow. Marketplace : UTMs
                                       #   gardes, et le lien « visit » d'une annonce porte
                                       #   rel sponsored si elle est payee ou mise en avant,
                                       #   nofollow sinon, ni l'un ni l'autre si elle est a
                                       #   nous (listingVisitRel, marketplace/3005)
/intelligence/directory                # Annuaires de boutiques (P6, ADR 0049) : index des listes assez longues
/intelligence/directory/[kind]/[slug]  # kind = niche | country | theme | app ; page 1 indexable si >= 12
                                       #   boutiques dont 8 assez mesurees, sinon noindex,follow et hors
                                       #   sitemap ; 404 si le nom n'est pas dans l'index en cache
/intelligence/directory/[kind]/[slug]/[country]  # niche x pays, kind = niche seulement (au moins 3 boutiques pour exister)
/intelligence/dossier/[domain]         # Dossier d'une des 500 premieres boutiques par trafic : tranches,
                                       #   source et periode, FAQ generee ; 404 si tombstone, opt-out ou
                                       #   hors du haut du classement
/intelligence/inspect                  # Index des inspecteurs (theme, apps, ads, emails) — indexable
/intelligence/inspect/[tool]           # Inspecteurs mono-sonde — indexables
/intelligence/radar                    # Radar — indexable tant qu'un angle est visible, noindex sinon
/intelligence/transparency             # Methode + provenance — indexable
/sell                                  # Sell-your-store funnel (public)
/sellers/[id]                          # Public seller profile
/network                               # Index des programmes (affiliate, partners)
/network/affiliate                     # LE programme qui paie (ledger `AffiliateCommission`,
                                       #   termes `AFFILIATE_*`), une page, deux sections
                                       #   d'audience : les clients qui parrainent, les
                                       #   createurs qui publient (ADR 0041, qui remplace 0015)
/network/partners                      # Programme agences + freelances seniors : il n'echange
                                       #   AUCUNE commission (ADR 0014), il donne une
                                       #   fiche d'annuaire, les demandes entrantes
                                       #   dessus, et un acces delegue dans l'org du
                                       #   client. Le chemin paye pour une introduction
                                       #   reste la section clients de /network/affiliate
/sponsor                               # Sponsor placements
/sponsor/[featured|newsletter]         # Sub-formats
/community                             # Hub — forum, events, levels, rien d'autre (growth-web/3014)
/tutorials                             # Index — listContentEntries("tutorials"). Etait
                                       #   /community/tutorials jusqu'a growth-web/3014 :
                                       #   des walkthroughs produit, pas du contenu communautaire
/tutorials/[slug]                      # Tutorial detail (How-To JSON-LD des que le MDX
                                       #   porte 2 steps, Article seul sinon). Cette ligne a
                                       #   annonce le How-To pendant des mois pendant que la
                                       #   page emettait un Article et rien d'autre ; les
                                       #   steps sont derives du MDX par extractHowToSteps
/community/[forum|events|levels]
/careers                               # Postes ouverts, puis postes a venir SANS montant.
                                       #   Equipe freelance (decision du 2026-09-25) : paye un %
                                       #   du CA mensuel ou un forfait, des que le mois le permet
                                       #   (config/team.ts). Etait /community/careers jusqu'a
                                       #   growth-web/3014 : une page d'entreprise, liee depuis
                                       #   le pied de page et /about
/careers/[slug]                        # Fiche de poste — la page QUE le JobPosting annonce.
                                       #   Les sept cartes pointaient toutes sur /contact, et
                                       #   les sept nodes JobPosting portaient cette meme URL :
                                       #   sept offres a une seule URL est la forme que Google
                                       #   for Jobs traite en doublon. Une page par role, et
                                       #   c'est son URL qui est balisee. JobPosting CONTRACTOR
                                       #   pour un role `hire-now` seulement, `baseSalary` (MONTH)
                                       #   seulement pour un forfait, jamais pour un %. Un role
                                       #   non ouvert garde une fiche lisible, noindex, sans
                                       #   JobPosting. Plus aucun salaire annuel (salaryRangeUsd
                                       #   YEAR retire). Perimetre derive de
                                       #   .claude/fleet/ownership.json
/community/forum/[slug]                # Fil de discussion + formulaire de reponse
/community/events/[slug]               # Detail d'une session : creneau dans le fuseau de
                                       #   l'evenement, animateur, RSVP, .ics, replay.
                                       #   Le lien de connexion (`Event.joinUrl`) n'est
                                       #   composé dans la reponse QUE pour un lecteur qui a
                                       #   RSVP — jamais dans le JSON-LD, jamais pour un
                                       #   anonyme. Cette ligne a dit « Detail RSVP (Event
                                       #   JSON-LD) » pendant des mois : litteralement exact,
                                       #   et pourtant la page n'a jamais rendu autre chose
                                       #   qu'un 404, parce qu'aucun chemin du depot ne savait
                                       #   creer un `Event`. Cf. docs/architecture/community-events.md
/community/levels/[level]              # Une page par barreau de l'echelle XP. Params derives de
                                       #   `LEVELS` (services/community/levels.ts), donc du ledger :
                                       #   pas de liste 1-8 ecrite a la main, et un barreau retire
                                       #   rend 404 au lieu d'une page sans copy. Les avantages y
                                       #   sont types `live` | `planned`, un `live` NOMME le fichier
                                       #   qui l'implemente et `levels.test.ts` le resout — la page
                                       #   ne peut donc pas promettre ce que le depot ne porte pas.
                                       #   noindex, follow et hors sitemap : huit pages quasi identiques
                                       #   (`growth-web/3002`) ; l'index `/community/levels` reste soumis
/legal                                 # Index — groupe par audience, dates lues dans
                                       #   src/app/(marketing)/legal/_registry
/legal/[privacy|terms|cookies|sales|legal-notice]
/legal/privacy/extension               # Politique de l'extension Chrome (URL de la fiche Chrome Web Store).
                                       #   Faits (permissions, hôtes, routes) dans legal/_registry/extension.ts,
                                       #   comparés au manifest de l'extension par
                                       #   src/test/legal-extension-matches-the-manifest.test.ts.
                                       #   Preuves ligne à ligne : docs/architecture/extension-privacy-claims.md
/legal/[subprocessors|dpa|marketplace|affiliate|aup|security]  # Ouverts par growth-web/0627.
                                       #   Les tableaux factuels de ces pages (sous-traitants,
                                       #   cookies, retentions, commission, fenetre de litige)
                                       #   sont DERIVES du code par cinq gardes
                                       #   `src/test/legal-*.test.ts` : une constante qui bouge
                                       #   sans la page casse le build. Avant, la page
                                       #   confidentialite publiait 7 sous-traitants sur ~30,
                                       #   trois durees de retention fausses sur trois, et les
                                       #   CGV vendaient « en euros TTC » ce que Stripe facture
                                       #   en dollars hors taxes

Aucune de ces pages n'est en ISR, quoi qu'en dise un export const revalidate. Le layout RACINE lit deux APIs de requete — le nonce CSP (headers()) et la locale (getLocale() → headers() puis cookies()) — et la doc Next 16 est explicite : « When Content Security Policy (CSP) nonces are used, all pages in your Next.js application must be dynamically rendered. This means static optimization and Incremental Static Regeneration (ISR) are disabled ». next.config.mjs n'active ni ppr ni cacheComponents, donc il n'y a pas de sortie de secours. Les six export const revalidate du depot rendent en realite a CHAQUE requete. Ce tableau annoncait « ISR 1h » et « ISR 5min » ; c'etait faux, et corriger la cause est une refonte du layout racine, suivie dans backlog/app-shell/0365. Ils etaient sept : /status/history pairait sa fenetre avec force-dynamic, donc son chiffre etait inerte deux fois et a ete supprime au lieu d'etre annote (app-shell/0500, moitie platform-ops). Le compte est tenu par src/test/isr-is-disabled-by-the-nonce.test.ts, pas a la main : sa liste KNOWN_INERT ne peut ni grossir en silence, ni garder une exception qui a cesse d'etre vraie.

0 routes sous (marketing)/intelligence sont des permanentRedirect depuis growth-web/3018 : les deux lignes marquees → 308 ci-dessus sont des regles de next.config.mjs, et leurs pages sont supprimees. Ce chiffre est derive par pnpm docs:claims, et le zero est le garde : une page de redirection reintroduite sous ce dossier le fait echouer. Ces deux pages ont ete decrites ici comme un « hub public » et des « classements par categorie indexables » pendant des mois apres la redirection, pendant que llms.txt envoyait les crawlers IA sur seize d'entre elles et que /api/intelligence/top rendait un lien web mort par categorie (intelligence/0240). Un agent qui boote sur ce fichier croyait a une surface qui n'existait plus.

Les redirections de next.config.mjs ne servent que des URLs qu'un systeme EXTERNE cite encore (nettoyage du 2026-09-26, plateforme sans utilisateur, avant indexation). Il en reste deux familles : les /marketplace/systems* (l'UA BoostEcom-TrackingScanner/1.0 les a ecrites dans les logs des boutiques scannees) et les /intelligence* (hub, quinze categories, quinze formes par store : les sorties des outils MCP, le manifeste MCP et /api/intelligence/top les ont donnees a des agents tiers). Le /en/* 308 et les prefixes de langue vivent dans src/proxy.ts, pas la. Toutes les autres anciennes URLs (renommages pre-indexation, refonte du menu features, /community/docs, /network/referral, /network/agencies, /feedback, /checkout/sponsor, les reshuffles /admin/*, /[orgSlug]/~/listings*) rendent 404 : une URL que seul ce depot liait se corrige au lien, jamais par une regle de plus.

Cette liste a aussi ete incomplete, ce qui est le meme defaut vu de l'autre cote : six pages (marketing) reelles n'y figuraient pas — /about/scanner, /changelog/[id], /roadmap/[id], /intelligence/inspect, /community/docs (depuis parti dans son propre groupe, cf. « Docs » ci-dessous) et /community/forum/[slug]. La plus couteuse etait /about/scanner : c'est l'URL que le user-agent BoostEcom-Scanner/1.0 cite a chaque store qu'il lit (src/lib/fetch-providers/types.ts), donc la page de politique qu'un proprietaire de site voit dans ses logs — elle DOIT rendre 200. Elles sont ajoutees ci-dessus.

Ce paragraphe se terminait sur son propre trou : « il n'existe pas de claim "pages (marketing) sur disque = pages listees", donc le reste est tenu a la main et peut re-deriver sans que rien n'echoue ». Il existe maintenant. src/test/claude-md-marketing-routes.test.ts derive les routes de src/app/(marketing) et echoue si l'une manque au tableau ci-dessus, en developpant la notation [a|b|c] et en acceptant qu'une route soit documentee dans le commentaire d'une ligne voisine, comme le fait ce tableau. pnpm docs:claims continue de deriver a part les slugs /features et le compte de redirections /intelligence.

Le garde ne lit QUE le bloc encadre, pas la section : la prose ci-dessus nomme /about/scanner deux fois, donc un garde qui lisait toute la section restait vert quand on supprimait sa ligne du tableau. Verifie en le supprimant.

La langue dans l'URL (pages publiques)

Depuis growth-web/3023 (ADR 0038), chaque page publique a une URL par langue, en as-needed : l'anglais garde son chemin (/pricing), les cinq autres langues passent sous leur prefixe (/fr/pricing, /fr pour la home). Aucun fichier de src/app n'a bouge :

  • src/proxy.ts (routeLocale) retire le prefixe et reecrit vers le chemin sans prefixe ; la langue passe au rendu par l'en-tete x-boostecom-locale, que src/i18n/request.ts lit AVANT le cookie ;
  • un prefixe n'est accepte que devant une racine de LOCALIZED_ROOTS (src/i18n/locale-path.ts, gardee contre le disque par src/test/localized-roots.test.ts). /fr/account repond 307 vers /account, qui repasse par le proxy SANS prefixe : aucune porte de securite n'a eu a apprendre les langues. /en/... repond 308 ;
  • une page publique sans prefixe est anglaise quel que soit le cookie ; un humain dont le cookie nomme une autre langue (le selecteur, une URL /fr ouverte, ou la langue detectee a son premier passage sur le tableau de bord, qui garde sa detection) est renvoye en 307 vers la version prefixee. Un robot n'a jamais de cookie et n'est jamais redirige. La detection (Accept-Language, pays) ne choisit plus la langue d'une page publique : LanguageBanner la propose ;
  • buildMarketingMetadata (async) pose le canonical sur l'URL de la page et le cluster hreflang complet (x-default = l'anglais sans prefixe). Une page dont le corps n'existe qu'en anglais (MDX non traduit via availableContentLocales, contenu en base : annonces, fils, changelog, roadmap) passe locales: ["en"] : sa version /fr pointe son canonical sur l'anglais et reste hors index, jamais une redirection (elle bouclerait avec celle du cookie). Le sitemap soumet chaque version avec le meme cluster ;
  • les liens : la nav, le pied de page, le rail des docs et les liens internes des MDX suivent la langue de la page (useLocalizedHref, LocalizedLink). Un lien non localise marche quand meme, via la 307 du cookie. Le tableau de bord et les ecrans authentifies n'ont pas de prefixe et lisent le cookie NEXT_LOCALE, comme avant ;
  • /admin et /ops sont en francais, quel que soit le cookie. Ce sont les surfaces du proprietaire et de son equipe : leurs pages ecrivent leur copie en francais, et le proxy leur pose l'en-tete fr (fixedLocaleFor, src/i18n/locale-path.ts) pour que le chrome partage (tableaux, menus) suive. /ops y est depuis growth-web/2803 : le module Studio qu'il monte lit desormais ses mots au catalogue studio, donc c'est l'epinglage de la ROUTE qui garde le cockpit operateur en francais, pendant que le Studio d'un marchand (le mode studio de /{org}/{store}) suit sa langue. L'en-tete est pose dans buildSecurityResponse, donc APRES la porte de role /admin, jamais dans routeLocale, dont la reponse sort avant les portes ; /ops n'a pas de porte de role au bord (une permission platform.*, verifiee par la page), et l'epinglage vaut aussi pour un non-admin. Gardes : src/proxy.admin-locale.test.ts, src/test/studio-copy-is-translated.test.ts.

Docs (public, groupe (docs))

/docs                                  # Hub — DERIVE l'index du corpus via `buildDocsNav`,
                                       #   trois troncs declares par le front-matter `section`
                                       #   (product | marketplace | developer) + FAQ JSON-LD.
                                       #   Aucune liste de slugs ecrite a la main, nulle part
/docs/[slug]                           # Une page de reference : ancres derivees des titres
                                       #   (`collectHeadings` + `slugifyHeading`, la MEME
                                       #   fonction que `proseComponents` utilise au rendu,
                                       #   donc la table des matieres ne peut pas pointer une
                                       #   ancre qui n'existe pas), sommaire a droite,
                                       #   precedent/suivant dans le tronc du lecteur

La doc a vecu sous (marketing)/community/docs jusqu'en septembre 2026. Ce n'etait pas qu'une URL : elle heritait du chrome d'ARTICLE marketing (CategoryArticle, fil d'ariane Accueil > Community > Documentation), des deux AdSlotsSidebar qui prennent les gouttieres, et elle reconstruisait son arbre de navigation dans chaque page — donc la position de scroll du rail etait jetee a chaque clic, sur un corpus plus haut que l'ecran. /community/docs* a repondu 308 jusqu'au nettoyage des redirections du 2026-09-26 et rend 404 depuis ; le repertoire est supprime : nav-coverage.test.ts traite tout repertoire sous /community comme quelque chose que le header doit lier.

Trois consequences a connaitre avant d'y toucher :

  • le rail de navigation vit dans (docs)/layout.tsx, pas dans les pages. C'est ce qui le fait survivre a une navigation. Il est donc client (usePathname) : un layout n'est pas re-rendu quand la route dessous change, donc le serveur ne peut plus decider la ligne active ;

  • /docs est un prefixe generique, contrairement a /community/docs. Les crawlers d'ingestion declarent des chemins shopify.dev/docs/... qui lui ressemblent exactement : nav-coverage.test.ts exclut src/services/algorithms/ingestion/ pour cette raison, nommement ;

  • le corpus est la SOURCE DE VERITE produit. Une information qui decrit ce que la plateforme fait va dans content/docs/, pas dans la copy d'une page marketing qui la re-raconte. src/test/docs-corpus.test.ts derive les surfaces du code et ECHOUE quand l'une d'elles est absente du corpus : les six Systems (chacun doit avoir SA page, titree de son propre nom — c'est la surface qui a le plus souvent ete livree sans porte), les cinq roles, les cinq plans, les scopes MCP publics et leurs outils, les quatre repertoires de webhooks, les six connecteurs. « Publics » n'est pas une formule : la liste est MCP_SCOPES.filter(s => !s.platformAdminOnly), parce qu'un scope retire de l'ecran de consentement d'un client n'a rien a faire dans une page que ce meme client lit. Sa couverture est exigee de l'autre corpus, et les deux gardes derivent la frontiere du MEME champ, donc un scope ne peut pas tomber entre les deux et finir documente nulle part. Le meme fichier refuse deux titres de meme ancre dans un document (collectHeadings et proseComponents derivent le meme id sans se voir : deux titres identiques donneraient un sommaire qui pointe a cote) et tout lien interne qui ne resout aucune route.

    Ce que la garde NE fait PAS, pour ne pas la croire plus forte qu'elle n'est : hors Systems, elle verifie qu'un identifiant apparait quelque part dans le corpus, pas qu'il y est bien explique. C'est un plancher — « le sujet n'a pas ete oublie » — pas une mesure de qualite. Elle ne couvre pas non plus les crons : soixante-quatre jobs n'ont pas soixante-quatre pages a ecrire, et exiger une mention par job produirait une liste que personne ne lit.

Docs operateur (/admin/docs, admin uniquement)

Il y a deux corpus, et c'est un repertoire qui les separe, pas un drapeau :

content/docs/content/runbooks/
Servi atout le monde, /docsun admin plateforme, /admin/docs
Repond acomment un CLIENT utilise le produitcomment NOUS operons la plateforme
Kinddocs (corpus public)runbooks (corpus internal)
Langueles six, anglais par defautfrancais seulement (OPERATOR_LOCALE, content/runbooks/fr/)
Sitemap, llms.*, recherche globale, MCP publicouijamais

Ce tableau a nomme content/docs-internal/ et un kind docs-internal pendant des semaines : le code lisait content/runbooks/. Et le corpus etait en anglais alors que tout ce que lit le proprietaire est en francais : il a ete traduit, l'anglais supprime, et les quatre lecteurs (les deux pages /admin/docs, searchOperatorDocs, getOperatorDoc) lisent OPERATOR_LOCALE. Une page operateur s'ecrit en francais, et nulle part ailleurs.

La regle qui decide ou va une page : qui se trompe si elle est fausse ? Un client agit dessus a tort, c'est public ; un operateur agit dessus a tort, c'est interne. Un internal: true sur un front-matter partage serait a un defaut oublie de publier un runbook a /docs ; un fichier est interne par l'endroit ou il vit, et aucune faute de frappe ne defait un repertoire.

Trois gardes, et elles ne se recouvrent pas. Toutes supposent la frontiere que le loader tient lui-meme : resolveContent refuse un slug qui n'est pas en kebab-case et verifie que le chemin resolu reste sous content/<kind>/<locale>/. Next decode %2F APRES avoir decoupe le chemin, donc /docs/..%2F..%2Frunbooks%2Fen%2Fx arrivait en ../../runbooks/en/x, et une page publique rendait le corpus operateur (growth-web/3009). Un lecteur de fichier construit sur un param de route passe par ce loader, ou porte les deux memes verrous.

  • src/test/docs-separation.test.ts prouve le RESULTAT, pas l'orthographe : le defaut corpus: "public" de searchDocs / getDocsDocument tient, aucune surface de crawl ne nomme docs-internal, le provider de recherche globale ne rend jamais un /admin/..., et tout lecteur du corpus interne hors de src/lib/content/ porte requireAdmin ou isPlatformAdminUser. Le moteur en est exempt volontairement : une garde d'acces dans une fonction de recherche est une garde qu'un second appelant oublie. Il refuse aussi tout lien github.com/BoostEcom/... dans le corpus PUBLIC — le depot est prive, donc c'etait un 404 offert sous la promesse « le raisonnement complet est ici » ;
  • src/test/internal-docs-corpus.test.ts exige une page par categorie d'ADMIN_CATEGORIES, la couverture des scopes platformAdminOnly et de leurs outils, et refuse un lien /admin/... que le registre ne declare pas ;
  • admin-conventions traite /admin/docs comme n'importe quel outil du panel : enregistre dans admin-routes.ts, titre pris du registre, requireAdmin() sur la page en plus du layout.

Cote MCP, deux outils native et platformAdminOnly lisent ce corpus (searchOperatorDocs, getOperatorDoc, scope boostecom:operator-docs.read). Trois portes : la famille native refuse d'enregistrer l'outil pour une cle statique (une cle nomme une boutique, jamais une personne, donc le controle d'admin n'aurait personne a controler), l'ecran de consentement retire le scope a qui n'est pas admin, et chaque APPEL relit User.role en base — un grant est durable, un role non.

Minimal (auth + onboarding)

/auth                                  # NextAuth OTP a six chiffres (Resend). SEUL provider :
                                       #   le magic link a ete retire, cf. security-identity/0267
/oauth/authorize                       # OAuth consent screen (Remote MCP)
/invite                                # Atterrissage d'une invitation d'organisation (`?token=`).
                                       #   Jeton 256 bits, seul le sha256 est stocke, 7 jours, revocable
                                       #   (DELETE /api/organizations/invite). Le jeton NE SUFFIT PAS :
                                       #   l'e-mail de session doit egaler celui de l'invitation, donc un
                                       #   lien transfere est inutile a qui n'est pas le destinataire
/onboarding                            # Post-signup wizard
/scan/tracking                         # Tracking scan (auth requise — redirige vers /auth) (+ /scan/tracking/[id] results)
/setup/tracking                        # Tracking setup wizard (+ /setup/tracking/[...slug]). Les guides
                                       #   `/setup/tracking/<guide>` sont PUBLICS et indexables depuis
                                       #   `security-identity/3004` ; l'accueil et `agent` restent derriere
                                       #   la session + le deverrouillage (`requireTrackingUnlock`).
                                       #   Les guides existent en anglais et en francais SEULEMENT
                                       #   (decision du proprietaire, 2026-09-26) : `/es|de|it|pt/...`
                                       #   rend l'anglais, canonical sur l'anglais et noindex. Garde :
                                       #   tracking-guides-are-en-fr.test.ts
/extension/install                     # Post-install BoostEcom Spy (pin → sign up → premiere boutique).
                                       #   Ouverte par chrome.runtime.onInstalled (Extensions/@BoostEcom)
/extension/uninstalled                 # Apres desinstallation de BoostEcom Spy : un sondage sur la raison
                                       #   et le lien de reinstallation. Ouverte par
                                       #   chrome.runtime.setUninstallURL (`?v=<version>&lang=<xx>`), noindex
/embed/bulletin                        # Formulaire d'inscription Bulletin, embarquable en iframe
/embed/widget                          # Widget embarquable — les deux sont les SEULS chemins ou le
                                       #   frame-deny global est leve (liste explicite dans src/proxy.ts)
/status                                # Uptime live (aussi liste en Marketing : la page est publique)
/status/history                        # Report card uptime 12 mois

/oauth/authorize ci-dessus vit en realite dans src/app/oauth/, hors du groupe (minimal). Il reste liste ici parce que c'est un ecran de consentement et que c'est la qu'on le cherche ; le garde ne derive que dans le sens disque → tableau, donc une entree qui n'est pas une page (minimal) ne le fait pas echouer.

Les premieres surfaces de cette liste a etre publiques par jeton — /drop/[token] et les deux /embed — ont manque a ce tableau pendant des mois. /drop/[token] (la livraison Creative Drop de l'agence) est supprime depuis le 2026-09-26 avec le plan C (ADR 0043) : un jeton encore present dans une boite mail rend 404, et /drop reste dans les racines « URL a capacite » du proxy, hors tracking et jamais Disallow, par hygiene. Ce 404 est rendu PAR LE PROXY (capabilityTombstone), avant toute session : laisse au routeur, /drop/<jeton> tombait sur (dashboard)/[orgSlug], dont le layout envoie un anonyme sur /auth (app-shell/3072, garde dans src/proxy.test.ts). Les tables StudioDrop, Prospect et ProspectEvent restent orphelines en base jusqu'a leur DROP operateur (data-platform/3073). C'est le defaut /about/scanner exactement : la carte sur laquelle un agent demarre ne nommait pas la surface ou un inconnu atteint les donnees d'un client. src/test/claude-md-minimal-routes.test.ts derive desormais ce bloc du disque, comme claude-md-marketing-routes.test.ts le fait pour le tableau Marketing.

« Ouverte par chrome.runtime.onInstalled », sur la ligne /extension/install, est le contrat attendu, pas un fait observe : le handler vit dans la branche Private/Extensions/@BoostEcom/Development du depot BoostEcom/Ecosystem, hors de ce depot, et rien ici ne peut le verifier. Au 2026-09-12 la source de l'extension l'honore (commit 6c4d482 du 2026-09-10, bump 1.0.1 en ba7f6d1) mais le package publie sur le Chrome Web Store reste, au moment d'ecrire cette ligne, en attente de review apres resoumission, donc le contrat ne tient pas encore en production observee (backlog/growth-web/0620). Lire cette ligne comme un fait a deja coute une decision produit : la page cochait automatiquement son etape « epingler » parce qu'elle se croyait ouverte par l'extension, donc le seul conseil actionnable de l'ecran se cachait pour 100% de son audience. Ce qui dit la verite maintenant est un chiffre, pas une phrase : le goal extension_install_view (INSTALL_GOAL, src/modules/analytics/spy-funnel.ts).

Dashboard (authenticated)

Desktop-only, >= 1024 px (ADR 0048 (ancienne 0043)) : sous lg, (dashboard)/layout.tsx rend un ecran « concu pour un ordinateur » en CSS pur ; aucun code du tableau de bord ne porte de variante mobile. Les surfaces publiques (marketing, docs, auth, onboarding, /embed, /status) restent mobile-first.

ECHANTILLON, pas un index, et garde par rien : find src/app -name page.tsx rend 234 pages. Plus de vingt pages reelles manquent ci-dessous (/account/domains, /account/watchlist, /[orgSlug]/~/agents, ~/memory, ~/settings/api-keys, ~/sponsor-placements, /[orgSlug]/[storeSlug]/insights/setup…). Deriver de find, ne pas lire cette liste comme la carte complete.

/account                               # redirect -> /account/settings. Il n'y a pas de page
                                       #   « profil » separee : la page qui vivait ici ne
                                       #   disait que « Account, teams, and domains will
                                       #   appear here » (app-shell/0282)
/account/domains                       # Domaines des boutiques du compte + etat du defi DNS
                                       #   TXT, lecture seule. La revendication se fait sur
                                       #   la boutique (POST /api/stores/[id]/verify-domain)
/account/settings/*                    # Parametres compte : activity (les AuditLog de
                                       #   l'utilisateur), authentication, communication,
                                       #   payouts, privacy, referrals, tokens,
                                       #   sign-in-with-@Atlas. Pas de page « facturation »
                                       #   ici : elle vit sous ~/billing
/account/orders                        # Buyer marketplace orders (PENDING/PAID/REFUNDED/FAILED filter)
/account/orders/[id]                   # Buyer order detail + "Open dispute" CTA if eligible (V4)
/account/offers                        # Buyer offers index (PENDING/ACCEPTED/REJECTED/COUNTERED/...)
/account/deals                         # Buyer deals index + mini-pipeline visualizer
/account/deals/[id]                    # Buyer deal detail (pipeline + checklist + chat, no org req)
/account/disputes                      # V4 — buyer disputes index (status filter)
/account/disputes/[id]                 # V4 — unified dispute thread (multi-role : buyer / seller / admin actions per role)
/account/saved                         # Saved marketplace listings (existant)
/account/settings/referrals             # Parrainage — code, gains, versements ; detail par filleul gate a partir de Max 5x
/account/settings/payouts              # V4 — Stripe Connect onboarding + status + Express dashboard CTA
/admin                                 # Panel admin
/admin/platform/{fleet,runs,scripts}   # Retirees : le cockpit est /ops/dev (?s=backlog|runs|ci) ;
                                       #   une fiche de run est ?s=runs&run=<id>
/admin/platform/dev-studio             # Reglages du Dev Studio (section `devStudio` de
                                       #   PlatformConfig) : fichiers design de l'equipe, et
                                       #   plafonds d'un delegue qui lance un run depuis /ops/dev
                                       #   ($5 par run, $100 par mois, decision du 2026-09-26)
/ops                           → 307  # Legacy Creative Studio removed; /ops/dev for authorized devs, else /.
/ops/dev                               # Dev Studio : le cockpit des devs et designers (Claude Code,
                                       #   Codex, Cursor, Figma, Excalidraw), sur LE shell et LE rail
                                       #   du Studio (`StudioShell`, `StudioRail`). UNE page, six
                                       #   sections en `?s=` : backlog, decisions, GitHub en direct
                                       #   (PR, runs Actions, quota detecte), runs d'agents et leur
                                       #   cout, CI, design. @Atlas lit les memes chiffres
                                       #   (`getDevSection`, skill `ops-dev`). `platform.dev.operate`, porte par
                                       #   un mandat : recruter un dev ne donne plus les 80 pages de
                                       #   /admin. Un delegue lance un run sous les plafonds de
                                       #   /admin/platform/dev-studio (`/api/ops/dev/runs`) ;
                                       #   abandonner reste admin, donc /admin/platform/{fleet,runs,scripts}
                                       #   restent en place tant qu'ils portent ce geste. Entree
                                       #   « Dev Studio » du menu profil, sous « Creative Studio »
/[orgSlug]                             # Vue d'ensemble organisation
/[orgSlug]/~/members                   # Membres
/[orgSlug]/~/billing                   # Facturation
/[orgSlug]/~/billing/activate          # Retour de checkout — verifie la session Stripe et
                                       # affiche l'etat REEL de chaque webhook (paiement,
                                       # subscription, plan, credits) avant de rendre la main.
                                       # Aucun timer : une org deja synchronisee ne voit
                                       # aucune attente
/[orgSlug]/~/settings                  # Parametres organisation
/[orgSlug]/~/deals                     # Index deals (seller + buyer side)
/[orgSlug]/~/deals/[id]                # Deal pipeline + asset checklist + messages
/[orgSlug]/~/disputes                  # V4 — vendor disputes index (status filter + deadline highlight)
/[orgSlug]/intelligence                # Intelligence org (+ /beta, /[domain])
/[orgSlug]/~/me                        # File de travail personnelle du membre
/[orgSlug]/[storeSlug]                 # Dashboard store
/[orgSlug]/[storeSlug]/workspace       # 307 vers /[orgSlug]/[storeSlug]. La page Workspace V2
                                       #   (696 lignes + 827 de primitives) etait une SECONDE
                                       #   grammaire du meme contexte, avec un apercu de branche
                                       #   casse ; Taches / Notes / Rapports sont un onglet du
                                       #   footer de la scene depuis app-shell/2778. A ne pas
                                       #   confondre avec /admin/platform/workspace, la vue
                                       #   operateur inter-stores, qui reste
/[orgSlug]/[storeSlug]/settings        # Parametres store — la DEUXIEME ETAPE de la scene depuis
                                       #   ai-platform/2779 : une vue du slot (scene)/@panel qui
                                       #   REMPLACE le storefront, pas une sheet par-dessus lui.
                                       #   « 1. Cockpit — 2. Settings » sont deux etats d'une
                                       #   region. Les trois sous-routes (connectors, rules,
                                       #   skills) redirigent vers son onglet (?tab=)
/[orgSlug]/[storeSlug]/insights        # Insights store
/[orgSlug]/[storeSlug]/intelligence    # Intelligence store
/[orgSlug]/[storeSlug]/systems/[aeo|aov|conversion|seo|tracking|vitals]  # Une page par System du
                                       # registre (liste derivee par `pnpm docs:claims`). Le mot
                                       # « installes », ecrit ici jusqu'en septembre 2026, etait
                                       # faux : rien ne lit `SystemInstall`. Depuis app-shell/2741
                                       # ces six pages vivent dans `(scene)/@panel/systems/` : un
                                       # slot de route parallele, donc l'URL ne bouge pas et la
                                       # page s'ouvre comme une VUE au-dessus du storefront, la
                                       # scene et son footer restant montes (cf.
                                       # src/app/(dashboard)/CLAUDE.md, « La scene du store »)
/[orgSlug]/[storeSlug]/studio  → 307  # ancien lien : redirect() vers /[orgSlug]/[storeSlug] (Preview).
                                       # L'ancienne interface Studio marchand a été retirée ;
                                       # Chat Atlas et Workflow conservent les médias Shopify.
/sell/dashboard                        # Tableau de bord vendeur — LA surface vendeur (mes listings,
                                       # stats, table + bulk actions). `/[orgSlug]/~/listings*`
                                       # a ete fusionne ici par `marketplace/0145` (l'ancienne URL
                                       # rend 404 depuis le 2026-09-26) : les deux lisaient
                                       # les MEMES lignes via `listSellerListings(user.id)`, et
                                       # `MarketplaceListing` n'a pas d'`orgId` — le `[orgSlug]`
                                       # gatait sur l'appartenance a l'org puis ne scopait rien
/sell/dashboard/new                    # Create form vendor (save draft OR submit for review)
/sell/dashboard/analytics              # Vendor analytics (KPIs 30j + top performers)
/sell/dashboard/[id]                   # Detail listing + offres pendantes + accept/reject + deals
/sell/dashboard/[id]/edit              # Form edit listing (rhf+zod, markdown preview, image upload, screenshots editor)
/sell/dashboard/[id]/analytics         # Analytics par listing (fenetre 7/30/90j)
/sell/deals/[id]                       # Seller deal detail

Le scan tracking public vit dans (minimal) : /scan/tracking(/[id]) + /setup/tracking, il n'y a pas de section /tracking dans le dashboard.

SEO / AI crawl surfaces

/sitemap.xml                           # Dynamic — auto-derives MDX + marketplace + agents
/sitemaps/stores.xml                   # Index des sitemaps des annuaires et dossiers (P6) ; chunks de 5 000 URLs
/sitemaps/stores/<n>.xml               # Un chunk : pages indexables seulement, tombstones filtres a chaque fetch
/<INDEXNOW_KEY>.txt                    # Fichier cle IndexNow (reecrit vers /api/indexnow/key/<key>), 404 sans cle
/robots.txt                            # AI crawler allowlist (GPTBot, ClaudeBot, anthropic-ai, Perplexity, Google-Extended…)
/llms.txt                              # Hand-curated editorial index for LLM crawlers (markdown)
/llms.json                             # Structured variant for modern AI agents
/llms-full.txt                         # Long tail : les memes sections + docs, tutos, annonces LIVE
/api/og                                # Edge-rendered Open Graph image generator
/.well-known/ucp-agent                 # Profil d'agent UCP — lu par CHAQUE boutique Shopify que
                                       # nous interrogeons, pour negocier nos capacites avant de
                                       # repondre. Lecture de catalogue uniquement, aucun secret.
                                       # Cf. docs/architecture/ucp-client.md

Routes API (460 handlers : find src/app/api -name route.ts | wc -l)

CategorieEndpoints
AuthHandlers NextAuth, suppression de compte
OrganisationsCRUD, invitation (lien e-mail + liste des invitations en attente de la session), export
StoresCRUD, export, verification domaine, indexation connaissances
CreditsAchat (Stripe checkout), redeem code, daily bonus, history
CheckoutGET /api/stripe/checkout/status — etat d'activation post-paiement (Stripe + DB), gate par appartenance a l'org de metadata.orgId
UsageGET /api/usage (live ledger)
SearchGET /api/search — recherche globale, fan-out parallele sur le registre de providers (src/lib/search/registry.ts : stores, organizations, scans, audits, tasks, conversations, members, marketplace, pages/admin, docs) scope par acteur. Le provider docs partage son moteur (lib/content/docs-search.ts) avec les outils MCP searchDocs / getDoc : un agent et un humain obtiennent la meme reponse au meme corpus, et il cherche les titres de SECTION, donc un hit renvoie vers l'ancre et pas vers le haut de la page. Reutilise searchListings (marketplace) et withSessionAuth pour l'auth + le rate limit, ne duplique aucun des trois moteurs existants (marketplace, knowledge par store, nl-search intelligence). Cf. docs/architecture/global-search.md
ChatPOST /api/chat (le tour) et GET /api/chat/[conversationId]/stream (reprise d'un tour interrompu : rejoue le flux SSE enregistre sur Upstash, 204 quand il n'y a rien a reprendre. Ne facture rien et n'appelle aucun modele, cf. backlog/ai-platform/0392)
CanauxWhatsApp webhook (le canal REST /api/channels/api a ete retire avec la famille sk_, cf. docs/decisions/0007-retrait-des-cles-sk.md)
ConnecteursGoogle, Meta, Shopify, Notion OAuth
Cron71 jobs dans vercel.json (limite Vercel : 100/projet depuis jan 2026) : billing (reset-credits, daily-bonus, reconcile-stripe, expire-grants — retrograde les plans offerts arrives a echeance), gdpr-erasure (draine les effacements RGPD Shopify : vecteurs, liens client, attribution, archive AuditLog — completedAt est la preuve de fin, une ligne sans lui est une obligation non tenue et visible), cleanup-chat-attachments (purge les pieces jointes binaires du chat sur Vercel Blob passe 30 jours : leur URL publique n'a jamais eu ni TTL ni balayeur, cf. platform-ops/0371 — la purge borne la duree d'exposition, elle ne rend pas l'objet confidentiel), marketplace-payouts (escrow vendeur post-fenêtre dispute 14j), affiliate-payouts (commissions de parrainage, apres 30j de retention, clawback net des remboursements), intelligence/* (×13), discovery-* (×3), vitals-* (×5), commerce-, aeo-, status-*, launch-tick (execution des milestones du wizard store + detection des transferts dev store), bulletin-radar (remplit la memoire Ecosysteme depuis les flux de sources primaires, horaire — tout entre en NEW, rien n'est publiable sans jugement), bulletin-dispatch (Demandes approuvees -> envoi, toutes les 15 min), bulletin-weekly (compose l'edition Ecosysteme depuis les Signaux, lundi), bulletin-store-weekly (compose l'edition par boutique suivie depuis le graphe d'intelligence, lundi — calcul une fois par store, personnalise par abonnement), growth-attribution (reconcilie l'attribution observee : inscriptions confirmees + envois reels, quotidien), scans-prune (retention des scans publics : les captures de storefronts TIERS en blob et le hash d'IP du soumetteur partent a 30 jours, la coquille comptable pas avant 62 — le plafond de depense du scanner public somme le mois calendaire), weekly-digest (le rapport hebdo par organisation : revenus / commandes / panier moyen des 7 derniers jours compares aux 7 precedents, par boutique CONNECTEE, lu sur RevenueDaily, aux owners ET admins — lundi), competitor-watch-digest (la veille concurrents : un e-mail hebdo ou quotidien aux seuls utilisateurs qui l'ont active dans /account/settings/communication, uniquement quand une marque suivie franchit un seuil ou qu'une recherche enregistree du Store Spy, alerte activee, voit entrer de nouvelles boutiques apres sa reference (section du meme e-mail, memes verrous de plan et de budget de lecture que l'explorateur) — rien n'est envoye sinon ; une ligne DigestSend par destinataire et periode est ecrite AVANT l'envoi et gardee seulement si la file l'a accepte, quotidien a 07:41), store-alerts-dispatch (mail les StoreAlert de severite critical a l'owner et aux admins, horaire : les detecteurs remplissaient la table depuis des mois sans qu'aucun email ne la lise, cf. platform-ops/0530), platform-alerts (le pendant PLATEFORME du precedent, horaire : un digest a ADMIN_EMAIL des inscriptions et des echecs de paiement Stripe. Aucun des cinq chemins d'echec du webhook n'ecrit au proprietaire — ils mailent le client, l'owner de l'org cliente ou le vendeur Connect, et payment_intent.payment_failed ne maile personne — donc un achat de credits rate etait silencieux pour tout le monde. La fenetre part du filigrane publie par le dernier run SUCCESS (resumeFrom), pas de now - 1h : un run saute couvre son ecart au lieu de laisser un trou, cf. platform-ops/0621), calculate-kpis, kpi-snapshot, cleanup-drafts, outcome-attribution, ingestion-tick, weekly-audits… Monitoring : /admin/platform/crons
Webhooks9 handlers. Stripe (idempotents via StripeEvent — V4 ajoute account.updated, charge.refunded, payout.failed) ; Shopify (events, idempotent via ShopifyWebhookEvent, + les 3 endpoints GDPR obligatoires — customers/redact leve une barriere ShopifyCustomerRedaction AVANT de repondre 200 et le cron gdpr-erasure draine l'effacement ; shop/redact enregistre une ShopifyComplianceRequest que le MEME cron draine — credentials, namespaces vectoriels, payloads bruts archives dans AuditLog, free-text des commandes — en stampant completedAt et en NOMMANT dans pendingSteps ce qui reste a un operateur ; customers/data_request reste entierement manuel. handledAt n'est pose que par un humain, et au-dela de 30 jours une ligne sans lui leve un log.error. Idempotence par ces deux tables, pas par ShopifyWebhookEvent) ; Resend (/webhooks/resend — livraison, bounces, plaintes spam, signature Svix, idempotent via EmailDeliveryEvent.providerEventId ; et depuis app-shell/2710 email.received, la reponse d'un client au desk, rattachee a son SupportThread par le support+<id>@ du destinataire, cf. docs/architecture/support-inbox.md) ; [platform] (Chat SDK / WhatsApp)
MCPAnalytique d'usage, enregistrement
CommunautePOST /api/community/events/[id]/rsvp (toggle + email de confirmation + XP) et GET /api/community/events/[id]/ics (le creneau en fichier calendrier, public : ajouter une session a son agenda ne demande pas de compte — mais le .ics ne porte le lien de session que pour un appelant identifie qui a RSVP). Plus les routes forum (threads, posts, reactions, reports)
FeedbackRetours utilisateur
StatusGET /api/status/summary (public, cache 60 s : l'etat de la plateforme pour la pastille du pied de page — un etat, un compte, un horodatage, jamais un detail d'incident), POST /api/status/subscribe, GET /api/status/confirm, GET /api/status/unsubscribe (public double-opt-in)
MarketplacePOST /api/marketplace/listings (drafts), POST /listings/[id]/save|reviews|checkout (+ DELETE /listings/[id]/reviews : auteur ou admin), POST /offers + /offers/[id]/verify, POST /leads, POST /deals/[id]/advance (cote acheteur de la FSM : c'est l'acheteur qui confirme la cloture, pas le vendeur), GET /recommendations|stats|sponsor-state + admin CRUD (cf. docs/architecture/marketplace.md)
Marketplace V4POST /api/marketplace/disputes (raise), GET/PATCH /api/marketplace/disputes/[id] (party-gated, buyer withdraw), POST /api/vendor/marketplace/disputes/[id]/respond (seller), POST /api/admin/marketplace/disputes/[id]/resolve (admin + audit log + Stripe refund)
Stripe Connect (V4)POST /api/vendor/stripe/connect/onboard (Express account + onboarding link), GET /api/vendor/stripe/connect/status (?refresh=1 pour pull live Stripe), POST /api/vendor/stripe/connect/dashboard (Express dashboard login link), GET /api/vendor/stripe/connect/payouts (balance + transfers list V5)
Marketplace V5.1POST /api/marketplace/kyc/start (enhanced KYC, returns hosted URL), POST /api/marketplace/signatures/[id] (sign/decline action), POST /api/vendor/marketplace/deals/[id]/request-signature (NDA/LOI/APA request)

Outils Shopify (serveur MCP)

OutilScopeDescription
Remote MCP server (implementation src/app/api/mcp/[storeId]/, URL non versionnee gardee pour les clients installes ; alias versionne src/app/api/mcp/v1/[storeId]/route.ts, 21 lignes qui re-exportent les memes handlers)Store management24 outils enregistres par register-tools.ts (le shopify-bridge est le client Shopify que ces outils appellent, pas un second registre), gates par toolIsRegistrable (scope OAuth approuve + scope Custom App, plus un appelant identifie pour les outils native). Consent /oauth/authorize, catalogue dans lib/security/mcp-scopes.ts
Shopify AI Toolkit PluginDevelopment (poste local, HORS depot)16 skills annoncees (docs, validation, Liquid, Hydrogen, Polaris) : chiffre invérifiable depuis un clone. Reste hors du depot : c'est un plugin d'editeur, pas un serveur MCP, donc .mcp.json ne peut pas le porter
@shopify/dev-mcpDev guidance (dans le depot depuis .mcp.json)Dev resources pour les outils AI. Lance par npx -y @shopify/dev-mcp@latest, donc toujours absent de package.json : c'est un serveur, pas une dependance de build

Le serveur MCP BoostEcom gere les operations store (scope webmaster) et vit dans ce depot. Le Dev MCP gere le dev guidance et la validation, et il est desormais porte par .mcp.json : toute session Claude Code sur ce clone l'a, sans installation. Seul le Shopify AI Toolkit reste un outillage de poste, parce qu'un plugin d'editeur n'est pas un serveur MCP et qu'aucun fichier du depot ne peut le declarer.