Audits · septembre 2026Audit — les quatre piliers de distribution (2026-09-12)

Audit — les quatre piliers de distribution (2026-09-12)

Lecture seule sur le code. Demande : auditer la surface extension Chrome, le bridge, puis audit/scan, pour definir ce qui va, ce qui ne va pas et ce qui manque, croise avec l'etat de l'art 2026. Recadre : ce sont quatre…

Lecture seule sur le code. Demande : auditer la surface extension Chrome, le bridge, puis audit/scan, pour definir ce qui va, ce qui ne va pas et ce qui manque, croise avec l'etat de l'art 2026. Recadre : ce sont quatre piliers de distribution, le quatrieme etant le wizard de creation / connexion d'une boutique Shopify, et l'objectif est que chacun soit « 100 % pret, plus besoin de revenir dessus ».

Etat au demarrage : origin/main a aa257dcc (PR #1157 mergee).

Ce que cet audit n'a pas pu verifier

A lire avant le reste, pour ne pas prendre un compte derive pour une mesure :

  • la suite de tests n'a pas ete executee. Les dependances ne sont pas installees dans cette session distante (node_modules/.bin/vitest absent). Tous les comptes de couverture ci-dessous sont derives des fichiers presents sur disque, pas d'un run ;
  • le code de l'extension n'a pas ete relu. Il vit dans BoostEcom/Ecosystem, branche Private/Extensions/@BoostEcom. Les constats du pilier 01 portent sur la surface plateforme et sur les contrats documentes ;
  • l'etat reel de la fiche Chrome Web Store n'est pas lisible depuis un agent. Les dates et politiques Chrome et Shopify viennent des sources listees en fin de document.

Verdict

Aucun des quatre moteurs n'a besoin d'etre reecrit. Ce qui bloque est ailleurs, et c'est la bonne nouvelle : trois des quatre bloqueurs sont des decisions ou des actions ops, pas des chantiers d'ingenierie. Le quatrieme est un sprint sur un socle deja en production.

PilierBloqueursCe qui bloque
01 — Extension Chrome2le build publie sur le CWS, et le panel de navigation
02 — Bridge MCP0rien ; retard de spec a planifier
03 — Audit & Scan1le job durable n'a jamais ete ecrit
04 — Wizard Shopify1on decrit au marchand un ecran supprime en janvier

01 — Extension Chrome

Ce qui va

Le contrat de presence est le bon design, et il est teste. Une page web ne peut pas detecter une extension installee : Chrome ne l'expose pas, deliberement, parce que ce serait un vecteur de fingerprinting. La plateforme lit donc trois signaux independants en OU — marqueur DOM, handshake postMessage, ping runtime legacy — dont un seul coute quelque chose a l'extension. Le handshake exige event.source === window et une origine same-origin, donc un iframe tiers ne peut pas pretendre que l'operateur a notre extension. src/features/store-runtime/extension/presence.test.ts (7 cas) epingle la separation des dialectes ; docs/architecture/extension-presence-contract.md donne une procedure de diagnostic en dix secondes. C'est la reference de qualite a viser sur les trois autres piliers.

Le funnel d'install est mesure. src/modules/analytics/spy-funnel.ts nomme les goals en un seul endroit et documente explicitement les deux diagnostics que le couple extension_click / extension_install_view permet de trancher.

Ce qui ne va pas

Le build publie sur le Chrome Web Store n'est pas celui qui ouvre notre page — deja suivi par backlog/growth-web/0620, aucun item ouvert ici. La source est correcte depuis le commit 6c4d482 (2026-09-10). Le package n'a pas ete re-uploade. Deux cases ops, ouvertes depuis deux items consecutifs. Tant qu'elles le restent, tout le funnel construit derriere mesure zero.

Le panel de navigation collecte sans lecteur, sous une politique qui l'interdit. → backlog/intelligence/0598 (type: decision). IntelligencePanelEvent est ecrit par /api/intelligence/panel/ingest et lu par personne : timeSeries.query({ series: "panel" }) n'a aucun appelant, la « Phase 2 » du contrat (panel-rollup) n'existe pas, et le cron de purge qualifie lui-meme la table de « currently dormant ». En face, la politique CWS applicable depuis le 1er aout 2026 interdit la collecte d'activite de navigation hors fonctionnalite user-facing decrite en evidence. Risque maximal, valeur produit nulle, pendant une review. Deux defauts mineurs a corriger avec la sortie choisie : la branche panel de timeSeries.query ignore storeIntelligenceId dans son where (elle rendrait tous les domaines), et la retention de 180 jours est calibree sur des courbes que rien ne rend.

Trois noms morts, dont un qui est un contrat vivant. → backlog/app-shell/0610 et backlog/integrations/0598.

  • data-contextiq est le marqueur de presence que le build publie ecrit. Le renommer d'un cote seulement casse la detection pour tous les installes. Migration en deux temps obligatoire.

    Suite, 2026-09-13. Etape 1 livree (app-shell/0610, PR #1173) : le lecteur accepte les deux noms. Le nom canonique a ensuite ete renomme une seconde fois, data-boostecom-spy → data-boostecom-extension, pendant qu'il etait encore gratuit : aucun build publie ne l'avait jamais ecrit, donc aucun parc a migrer. La source de l'extension ecrit desormais les DEUX noms (section [Unreleased], sans bump : un bump signifie qu'un package a ete uploade). Il ne manque plus de code pour l'etape 2, il manque une publication CWS puis une mesure d'adoption.

  • /api/sidekick/* : deux endpoints publics authentifies par HMAC, sans aucun client. Trois sources du depot se contredisent sur qui les appelle (docstring : « Theme Copilot AI » ; agentic-stack.md : « a creer / a wirer » ; shared-secrets.test.ts : « BoostEcom Spy envoie deja »). Tranche le 2026-09-12 : aucune n'est plus vraie. Le secret annonce dans les docstrings (SIDEKICK_SHARED_SECRET) n'existe pas dans le schema env ; le code lit VITALS_BEACON_SECRET, qui signe aussi les beacons vitals et l'identite acheteur inter-commandes.


02 — Bridge MCP

Le bridge est le serveur MCP consomme par les clients LLM externes — Claude, ChatGPT, Cursor, Claude Code. Transport distinct de l'extension, mais meme cerveau : les deux lisent la meme projection.

Ce qui va

La surface OAuth est au niveau de la spec, sans etape proprietaire. BoostEcom est son propre serveur d'autorisation, multi-tenant par audience : le storeId voyage dans resource (RFC 8707), se fige sur le code puis sur le token, et /api/mcp/[storeId] refuse un token emis pour un autre store. RFC 9728, RFC 8414, RFC 7591 (DCR), rotation de refresh obligatoire, RFC 7009 (revoke). La step-up authorization de MCP 2026-07-28 est implementee. Un client qui parle OAuth 2.1 + MCP se connecte en decouvrant tout depuis un 401. C'est le meilleur actif technique des quatre piliers.

Le MCP Intelligence public repond deja au coup joue par la concurrence. Dix outils (searchStoreIntelligence, searchSimilarStores, predictStoreTrajectory, findEmergingNiches, getWinningAngles, getSupplierIntel…), quotas par tier (60 req/min anonyme, 6 000 avec cle bei_, bucket par organisation et non par cle). Quatorze outils cote store MCP, gates par scope OAuth + scope Custom App. Un outil du marche vend aujourd'hui son MCP comme differenciateur a partir de 49 $/mois : nous avons la meme surface, publique. A traiter comme un canal d'acquisition, pas comme de la plomberie.

Ce qui ne va pas

Le contrat que deux piliers partagent n'est epingle par aucun test. → backlog/intelligence/0599. L'extension lit l'Intelligence en REST (lookup?rich=1, hub/scan, similar?enrich=1), les clients MCP la lisent en outils : les deux passent par hub-projection.ts → HubStore. Ce contrat est documente dans un audit date (2026-09-10 §D), pas dans une garde. Le cout d'une derive est asymetrique : cote MCP un client se plaint, cote extension le build est deploye separement, affiche un vide, et personne ne relie ce vide a une PR mergee des semaines plus tot.

Retard sur la revision 2026-07-28, sans consequence immediate. → backlog/integrations/0600. Mcp-Method / Mcp-Name (SEP-2243) absents, donc notre propre rate limit doit parser le corps pour savoir ce qu'il limite. ttlMs / cacheScope (SEP-2549) absents, donc un client ne sait pas combien de temps un tools/list reste frais. Extension Tasks absente — et c'est exactement le besoin du pilier 03, donc les deux se concoivent ensemble. Roots, Sampling et Logging sont deprecies par cette revision : ne rien construire dessus.


03 — Audit & Scan depuis le chat

Ce qui va

Le moteur de scan tracking est un vrai produit. 41 checks — 7 infrastructure, 14 fondations, 12 destinations, 8 conformite — chacun dans son fichier, plus des detecteurs par vendeur, des validateurs croises, cinq phases et quatre analyseurs qui transforment le constat en plan d'action. La retention est pensee en deux etages (redaction du payload a 30 jours, suppression de la coquille jamais avant 62), avec plafond de depense sur le scan public et hash d'IP du soumetteur. C'est rare et c'est correct.

Ce qui ne va pas

Le job durable a ete livre comme document, jamais comme code. → backlog/ai-platform/2642. docs/architecture/durable-audit-jobs.md decrit precisement la cible. L'item qui l'a produit (ai-platform/0592, PR #1011) porte type: "docs", status: "done". Le document est bon ; le job n'existe pas : 18 types de jobs enregistres, ni store-audit ni tracking-scan-durable, et HEAVY_JOB_TYPES ne contient toujours que intelligence-scan-store. Donc runFullStoreAudit et runTrackingScan tournent encore inline dans le tour de chat sous maxDuration = 300 : coupe nette, pas de reprise, pas d'idempotence produit. Les quatre consequences que le document annonce sont toutes encore vraies.

Correction du 2026-09-12, en preparant l'implementation. Ce rapport a d'abord qualifie l'item de « sprint, pas un chantier : deux handlers, deux entrees de registre ». C'est faux, et la raison est une fourche d'architecture que ni le document de design ni l'item n'avaient vue.

Les deux fonctions dependent de ctx.requestOrigin + ctx.requestCookie — un saut HTTP vers /api/tracking/scan portant le cookie de session de l'utilisateur (tools/tracking-scan.ts:180, store-audit/engines/tracking-pillar.ts:104). Ce saut existe pour deux bonnes raisons documentees : importer le scanner directement a casse le build Vercel (Playwright + Browserbase + chromium-bidi tires par Turbopack), et la route fait son propre controle de session.

Or le design §3.4 interdit explicitement de rejouer ce cookie dans QStash. Les deux sont justes separement et incompatibles ensemble.

Un worker qui reconstruit le contexte sans ces champs n'echoue pas bruyamment : le pilier tracking se degrade proprement et rend quatre piliers a covered: false. Un audit durable livre en l'etat serait donc systematiquement plus faible que l'audit inline, sans que l'operateur sache pourquoi — exactement la degradation silencieuse que ce rapport reproche ailleurs.

La fourche, la recommandation et son chiffrage sont dans backlog/ai-platform/2642, qui passe de taille M a L et porte desormais un blocked_by.

Le verdict que le scan facture n'est garde par aucun test. → backlog/commerce-systems/0603. checks/ 45 fichiers → 1 test (sur les URLs de guide, pas sur la logique) ; detectors/ 9 → 0 ; validators/ 4 → 0 ; phases/ 5 → 0 ; analyzers/ 4 → 0 ; clients/ 4 → 0 ; cmp/ 4 → 0 ; scoring.ts, orchestrator.ts, runner.ts → aucun. Les dix tests qui existent couvrent la peripherie, et la couvrent bien. Rien ne couvre le calcul. Une regression silencieuse dans un check transforme un « tout va bien » en mensonge facture.

La file de rapports hebdomadaires n'a toujours pas de consommateur — deja suivi par backlog/platform-ops/0257 et la decision backlog/platform-ops/0368, aucun item ouvert ici. Le confinement est bien fait : la moitie qui empilait des StoreReport en pending est coupee par un drapeau, et un test a double sens echoue le jour ou un consommateur apparait sans que le drapeau bouge.


04 — Wizard store Shopify

Ce qui va

Le moteur d'execution autonome n'a rien a envier a un orchestrateur commercial. Playbook type de 29 jalons en DAG (13 auto + 16 human, chacun avec dependsOn), 13 executeurs cables — un par jalon automatique, aucun jalon auto sans executeur —, un runner avec cycle de vie complet, claim atomique anti-double-execution et retries exponentiels stampes. Le cron launch-tick (739 lignes) parcourt le graphe, dispatche ce qui est pret, re-queue ce qui a expire, et detecte l'acceptation du transfert de boutique en sondant shop.json. DevStorePool fait le reste : claim atomique, token chiffre AES-256-GCM au repos, machine a etats du transfert, et une absence de FK volontaire pour qu'une boutique transferee puis supprimee ne resurgisse jamais comme AVAILABLE.

Ce qui ne va pas

On envoie le marchand sur un ecran que Shopify a supprime en janvier. → backlog/integrations/0597. La copie affichee dit litteralement « Settings → Apps and sales channels → Develop apps → Create an app », en six locales, et le bouton CTA deep-linke sur admin.shopify.com/store/<shop>/settings/apps/development. Depuis le 1er janvier 2026, Shopify n'autorise plus la creation de custom apps depuis l'admin : elles passent par le Dev Dashboard. Les apps creees avant continuent de fonctionner — c'est exactement ce qui a rendu le defaut invisible : ca marche pour nous et pour tous les marchands deja connectes, et seulement pour eux.

Correction du 2026-09-12, apres lecture du code. La premiere version de ce paragraphe ajoutait « le token colle reste valide, c'est l'itineraire qui a change ». C'est faux pour un nouveau marchand : une app du Dev Dashboard ne delivre jamais de shpat_ collable, elle donne un Client ID + secret shpss_ et un token client_credentials de 24 h.

Ce que cette erreur cachait est une bonne nouvelle : le chemin moderne existe deja et fonctionne. /api/integrations/shopify/custom-app fait le client_credentials, rafraichit le token 24 h (features/shopify/sdk/token.ts), et c'est lui que le panneau Settings → Connecteurs utilise — avec la bonne copie. Le dialogue d'onboarding l'appelle aussi, et le marque « Recommended ». Seul le secret permet de verifier la signature des webhooks, donc seul ce chemin allume la sync live.

Le defaut est donc strictement de la copie, et il est plus etroit que l'item ne le disait : le wizard envoie le marchand chercher la mauvaise chose au mauvais endroit, alors que les champs qui l'attendent juste en dessous sont les bons.

Treize executeurs ecrivent chez le marchand, un seul fichier de test. → backlog/ai-platform/2643. Meme defaut que le pilier 03, sur une surface plus sensible : ces executeurs ne lisent pas, ils ecrivent — theme, produits, pages legales, JSON-LD — dans la boutique d'un marchand payant, sans relecture humaine sur les jalons auto. Le seul test porte sur la couche de transport. Trois d'entre eux ecrivent du contenu public et indexable : une erreur y est visible par Google avant de l'etre par nous.


Erratum du 2026-09-22 — l'echeance 2027 ne vise pas la Custom App merchant-owned

La suite integrations/0599 ouverte par cet audit reposait sur une premisse fausse : l'obligation Shopify du 1er janvier 2027 sur les offline tokens expirants concerne les public apps. Shopify exclut explicitement les custom apps et les apps creees par les marchands.

Le chemin merchant-owned moderne mesure ici reste valide : Dev Dashboard du marchand → scopes → release/install → Client ID + Client Secret → client_credentials (token 24 h, renouvelable). Le runtime BoostEcom le fait deja.

L'architecture retenue depuis cet erratum est l'ADR 0033. 0599 est archive sans implementation et remplace par integrations/2951, dont le probleme est l'onboarding et la couverture de permissions du chemin merchant-owned — pas une migration OAuth publique imposee par 2027.

Sources Shopify :

Les items ouverts

PriItemCe que c'est
P2backlog/intelligence/0598decision — couper le panel de navigation, ou le declarer. Etait P0 : la premisse (une collecte en cours, sous review CWS) a ete mesuree FAUSSE le 2026-09-13, l'extension publiee n'envoie rien. Decision intacte, urgence tombee
P0backlog/integrations/0597la copie de connexion Shopify, 6 locales — fait, archive
P0backlog/ai-platform/2642les deux handlers de job durable
P1backlog/commerce-systems/0603tests du moteur de scan — fait, archive
P1backlog/integrations/0598supprimer /api/sidekick/* — fait le 2026-09-13, archive
P1backlog/intelligence/0599epingler le contrat HubStore
P1backlog/ai-platform/2643tests des executeurs publics — fait, archive
P1backlog/app-shell/0610migrer data-contextiq en deux temps — etape 1 faite, etape 2 gatee sur une publication CWS
P2backlog/_archive/integrations/0599-porter-la-connexion-shopify-sur-oauth-avant-l-echeance-2027.mdOAuth managed install Shopify — supersede le 2026-09-22 : premisse 2027 fausse pour les custom apps
P1backlog/integrations/2951-shopify-custom-app-merchant-owned-onboarding-permissions-maximales.mdCustom App merchant-owned : onboarding guide, credentials, grant reel et permissions maximales
P2backlog/integrations/0600rattraper la revision MCP 2026-07-28

Ouverts apres coup, le 2026-09-13

PriItemCe que c'est
P1backlog/commerce-systems/2651les quatre distorsions de la note du scan : un warn coute autant qu'un fail, un skip reste au denominateur, une categorie absente vaut 0 en se declarant couverte a 100, et requiredPhase est declare 41 fois pour n'etre lu nulle part. Les quatre etaient epingles par les gardes de 0603 et aucun n'etait corrige
P1backlog/ai-platform/2645(minimal)/scan/tracking, 7504 lignes et 45 composants, seconde surface de rendu du meme rapport
P2backlog/commerce-systems/2650deux chips, deux intentions, trois skills pour une question

Et un livre le meme jour : ai-platform/2644, une seule porte d'audit dans le chat (ADR 0016).

Deja suivis ailleurs, volontairement pas rouverts : le re-upload CWS (growth-web/0620) et l'orchestrateur de rapports (platform-ops/0257, decision platform-ops/0368).

Sources de la veille 2026