Audits · septembre 2026Audit final de parcours — nouvel utilisateur / power user / admin (2026-09-11)

Audit final de parcours — nouvel utilisateur / power user / admin (2026-09-11)

Lecture seule sur le code, sauf les correctifs listes en tete de chaque section (appliques dans la meme session, testes, pnpm test + pnpm typecheck + pnpm lint verts avant commit). Demande initiale : un dernier audit…

Lecture seule sur le code, sauf les correctifs listes en tete de chaque section (appliques dans la meme session, testes, pnpm test + pnpm typecheck + pnpm lint verts avant commit). Demande initiale : un dernier audit complet une fois toutes les sessions fleet precedentes terminees, pour fermer les derniers trous avant l'accueil massif d'utilisateurs reels. Aucune refonte, aucune nouvelle feature — audit de coherence uniquement.

Etat au demarrage

  • origin/main a ac77568 (PR #1018 mergee), aucune branche fleet/* sur origin, pnpm fleet:status : active locks 0.
  • 25 items de backlog portaient status: "claimed" ou "in-progress" avec une branche absente d'origin : verrous fantomes. Verification PR par PR (pull_request_read, methode get, jamais list — son champ merged n'est pas fiable) : les 25 sont mergees. Tous archives dans cette session, avec pr: renseigne et une section ## Livre (ou la section narrative existante suffisait deja) — voir backlog/_archive/*.
  • L'audit complet du 2026-09-03 avait ouvert 113 items references par ce document (dont les 4 P0 cross-tenant) : les 113 sont archives. Les 4 P0 (ai-platform/0160, 0161, integrations/0163, commerce-systems/0162) sont verifiees corrigees en code, pas seulement sur parole (lu ligne a ligne : getStoreAccess pose avant tout acces, CSP nonce en en-tete sur le proxy preview).

Methode

Huit agents en lecture seule, un par pilier concerne, charges de tracer les parcours majeurs de trois personas (nouvel utilisateur, power user, admin) en lisant le code (pas de serveur dev/DB disponible en session distante) et de chercher des regressions, dead ends et incoherences residuelles entre systemes desormais stabilises — en particulier dans les zones a fort volume de PR recentes (pricing v4.0, refonte menu Features, purge ContextIQ, pages Spy). Consigne explicite : aucune nouvelle feature, aucun refonte.

Correctifs appliques dans cette session

#ConstatFichier(s)Gravite
1DELETE /api/integrations/shopify/custom-app ecrivait status: "disconnected", valeur absente de l'enum Postgres ConnectionStatus (pending|paired|active|revoked) : le write levait, 500 systematique, token + secret jamais effacessrc/app/api/integrations/shopify/custom-app/route.ts, src/types/integrations.ts, test associeMajeur — un marchand ne pouvait pas deconnecter sa boutique Shopify
2/[orgSlug]/~/agents lit et ecrit l'autonomie des agents via withSessionAuth({ requireOrg: true }) sans jamais passer ?orgId= : un membre de deux orgs ouvrant l'org B lisait/ecrivait l'autonomie de l'org A (fallback sur l'adhesion la plus ancienne)src/app/(dashboard)/[orgSlug]/~/agents/page.tsx, src/components/patterns/agent-autonomy/autonomy-selector.tsxBloquant — gouverne si l'IA agit sans validation
3Trois helpers de mutation (updateOrganization, updateStore, updateUser) n'inspectaient jamais res.ok / le shim d'erreur : un refus serveur (403 role insuffisant, 400 validation) laissait le patch optimiste en place et l'appelant affichait "Changes saved"src/components/contexts/tenant-context.tsxMajeur
4Suppression compte/org/store : les trois onDelete du SettingsDropdown n'inspectaient pas res.ok — un 403 (non-owner) ou un 500 (echec RGPD) etait annonce comme un succes et redirigeait l'utilisateursrc/components/shells/app-shell/shell-client.tsxMajeur — chemin Article 17
5Zone de danger + formulaire de [orgSlug]/[storeSlug]/settings rendus a tous les roles alors que DELETE est owner-only (access.isOwner) : un viewer pouvait taper le nom, confirmer, et recevoir "Owner only"settings-manager.tsxMajeur
6onSave du formulaire store n'attendait pas updateStore(...) (pas de await) : meme apres le correctif #3, l'erreur devenait un rejet de promesse non gere au lieu de remonter au try/catch d'EntityFormsettings-manager.tsxMajeur
7Page /pricing correcte ($79/$199/$399) mais la home (FAQ + FAQPage JSON-LD) vendait encore $49/mo Pro et "$49-$299/mois" dans les 6 locales — servi aux answer engines comme reponse structuree au prixmessages/*.json (home.faq.groups.pricing.items.cost.a, …comparisons.items.vsFreelancer.a)Critique — premiere page du parcours, contredite au clic suivant
8/features/workspace affirmait encore "Included credits match the plan price" (la regle 1:1 que l'ADR 0012 a explicitement retiree), en 6 locales (FR traduit, EN/DE/ES/IT/PT identiques = jamais traduits)messages/*.json (marketingPages.features.subpages.workspace.howItWorks[2])Majeur
9/network/referral calculait l'exemple de gain sur l'ancien prix Pro ($49 → $14.70/mois), sous-estimant la vraie commission de $9/mois/parrainage ($79 → $23.70)src/app/(marketing)/network/referral/page.tsx + messages/*.json (desormais derive de PLAN_PRICING + AFFILIATE_COMMISSION_PCT, ICU params)Haute
10Bloc i18n fantome pricing.plans.* / pricing.subscription.* / pricing.enterprise.* (top-level, distinct de nav.pricing.* qui est le vrai lu par la navbar) : zero lecteur, mais memes chemins de cles que la nav — piege de derive qui aurait fait revenir $49 en prod au premier refactor de la navmessages/*.jsonMoyenne (latent)
11Copy "Systems installed on this store" (6 locales) — regression du retrait documente dans CLAUDE.md (rien ne lit SystemInstall)messages/*.json (features.workflow.systemsTab.intro), docblock systems-tab.tsxHaute
12llms.txt/llms-full.txt/community/docs gardaient des mentions "CLI"/"SDK" retirees (marketplace/0587), dont une auto-contradiction a 41 lignes d'ecart dans le meme fichiersrc/app/llms.txt/route.ts, llms-full.txt/route.ts, community/docs/page.tsx, messages/*.json (3 cles × 6 locales)Moyenne
13Lien mort /docs/marketplace/verified-revenue (aucune route /docs/* n'existe) sur le formulaire d'edition vendeursell/dashboard/[id]/edit/page.tsx, ancre #faq ajoutee sur /sellMineure mais 404 franc
14Formulaire admin de resolution de litige : defaut sur REFUND_FULL, qui leve toujours escrow-refund-manual sur un litige de deal (pas d'escrow cable) — l'admin voyait le code d'erreur brut au lieu de l'option correcteaccount/disputes/[id]/_components/admin-resolve-form.tsx + page appelanteHaute — chemin de mediation d'argent
15Copy "magic-link" residuelle dans /account/settings/authentication (6 locales) + 2 docblocks — le magic link est retire depuis security-identity/0267, OTP seul providermessages/*.json, (minimal)/auth/page.tsx, api/organizations/invitations/route.tsMineure

Trois nouveaux tests de regression source-level (memes conventions que connector-oauth-entrypoints.test.ts : un template literal n'a ni compilateur ni routeur pour repondre de lui) : src/test/agents-autonomy-scopes-to-current-org.test.ts, src/test/store-settings-danger-zone-is-owner-gated.test.ts, src/test/tenant-mutations-check-res-ok.test.ts.

Ce qui reste ouvert — reference complete par pilier

Les huit rapports d'agent complets (chaque constat cite fichier:ligne + preuve verbatim) ne sont pas reproduits ici pour eviter un second fichier de 500 Ko : ils ont ete lus integralement pour produire ce resume et les items de backlog ouverts ci-dessous. Ce qui suit est la liste des constats non corriges dans cette session, par gravite decroissante.

P0/P1 — a traiter en priorite par le pilier concerne

  • marketplace — La porte KYC unique (ADR 0011, apaSignedAt sur DILIGENCE → APA_SIGNED) est contournable : le VENDEUR peut signer l'APA sans aucune verification KYC (l'exemption vendeur est deliberee et testee), et c'est cette meme colonne partagee que lit la transition — donc sur un deal ou le vendeur signe en premier, l'acheteur n'est jamais verifie avant l'entree en escrow. src/services/marketplace/seller.ts:545-551 (le check ne teste deal.buyerId === userId) + deal-stage-policy.ts:106 (bouton seller-only) + sell/deals/[id]/[doc]/page.tsx. Correctif : deplacer la condition sur la TRANSITION (DEAL_ADVANCE.DILIGENCE.requires), jamais sur qui signe. → backlog/marketplace/0596.
  • ai-platform — useResumePendingMessage (0590) est branche a l'envers : mort sur le SEUL mount dashboard (qui passe onAuthRequired, donc la garde if (onAuthRequired || sent.current) return sort tout de suite), vivant sur ~20 pages marketing (MarketingFaqSection, /pricing) qui ne le passent pas — un visiteur connecte avec du solde y envoie un tour /api/chat facture dans un composer compact dont la transcription n'est jamais rendue. Meme racine : le bouton Envoyer et le chip Audit/Scan sont inertes pour un visiteur deconnecte sur ces memes pages (onAuthRequired optionnel, jamais fourni). → backlog/ai-platform/0597.
  • ai-platform — Le separateur de compaction (0394) va s'afficher sur chaque tour passe 15 messages au lieu de marquer une coupure unique : droppedCount est un etat permanent (croissant de 2 a chaque echange), pas un evenement, et getCompaction: () => droppedTurns > 0 ? {...} : undefined (handler.ts:1319) le re-declare a chaque tour. Regression exacte de ce que l'item 0394 voulait eviter. → backlog/ai-platform/0598.
  • app-shell — Revenir d'un pas dans l'onboarding apres un reload cree une DEUXIEME organisation (le garde d'idempotence est un useRef, ne survit pas au reload que le wizard sait par ailleurs reprendre), efface currentStore, et peut terminer le wizard sur un 404 (onboarding.tsx:372-382). → backlog/app-shell/0599.
  • app-shell / marketplace — Les cinq surfaces acheteur (/account/orders, /offers, /deals, /disputes, /saved) n'ont aucune entree de navigation permanente : /account/deals a litteralement zero lien entrant dans tout le depot hors sa propre balise canonical. Meme defaut que celui deja corrige pour ~/listings (marketplace/0145), reproduit sur cinq surfaces a la fois. → backlog/app-shell/0600.
  • intelligence — Consentement beta Intelligence : role est selectionne puis jamais verifie (un viewer peut engager la boutique dans le partage de donnees L4↔L1), aucun AuditLog sur l'action, et aucun chemin de revocation cote produit (revokedAt n'est ecrit nulle part). src/app/api/intelligence/beta/consent/route.ts:31-59. → backlog/intelligence/0601.
  • commerce-systems — /systems/aeo et /systems/seo affichent un score 0/100 · "needs improvement" ET "No recommendation: nominal state" simultanement pour un store jamais audite (capturedAt: null rendu comme rien du tout). Contredit le principe du pilier ("un systeme qui affiche un score explique le score") et le voisin /systems/vitals qui modelise correctement l'absence en null. → backlog/commerce-systems/0602.
  • commerce-systems — Boucle cassee sur /systems/tracking : le CTA "Run scan" pointe vers un scan PUBLIC qui ne portera jamais storeId (donc n'apparaitra jamais dans l'historique juste en dessous), et l'historique qui EST attribue au store porte un lien "View" qui 404 systematiquement (les scans lances depuis @Atlas mintent un shareId synthetique sans rapport persiste). → backlog/commerce-systems/0603.
  • billing — /[orgSlug]/~/billing appelle creditCycle() sans son second argument (cycleAnchorDay), donc affiche la fenetre de cycle 1er-du-mois pour toute org dont l'ancre n'est pas le jour 1 — la carte usage voisine (org-usage-card.tsx) le fait correctement 30 lignes plus loin dans le meme repertoire. → backlog/billing/0604.
  • growth-web — Le bouton "Customize" de la banniere cookies promet un centre de preferences ; /legal/cookies n'a aucun controle (juste de la prose), et un visiteur qui a clique "Accept all" n'a plus aucun moyen produit de revenir dessus (le retrait passe par les devtools). RGPD. → backlog/growth-web/0605.
  • marketplace — /sell affiche 3% de commission dans son hero (derive de BOOSTECOM_MARKETPLACE_FEE_RATE) et 5% dans la modale SellStoreWizard montee 100 lignes plus bas sur la MEME page (COMMISSION_PCT = 5 en dur). Le 5% contredit ce que le checkout facture reellement. → backlog/marketplace/0606.
  • marketplace — marketplace/0587 (retrait categorie CLI) n'est pas fini cote serveur : POST /api/vendor/marketplace/listings, POST /api/admin/marketplace/listings et le registre MARKETPLACE_LISTING_TYPES acceptent toujours type: "CLI". Une soumission publique de type CLI reste possible, approuvable en admin, et l'email d'approbation pointe vers une URL qui 301 loin du listing. → backlog/marketplace/0607.

P2 — reels mais moins urgents, un item chacun ou groupes

  • integrations — Split-brain Connector (Google/Meta/Klaviyo) vs IntegrationConnection (Shopify/Figma seulement) : la bande connecteurs du composer (connectors-inline.tsx) lit /api/stores/[storeId] qui ne voit que la seconde table, donc Google/Meta/Klaviyo ne peuvent JAMAIS apparaitre "connected" dans le chat malgre un OAuth reussi. → backlog/integrations/0608.
  • integrations — Revocation Klaviyo jamais appelee (revokeToken existe, zero appelant) ; echec de refresh Figma avale en silence (catch {} sans log, viole la regle non-negociable du pilier) ; branche chrome-extension de connectors-inline.tsx injoignable depuis CONNECTOR_ORDER — un id CWS y a ete pose aujourd'hui dans du code mort. → groupes dans backlog/integrations/0609.
  • app-shell — /[orgSlug]/[storeSlug]/intelligence n'a aucune porte de navigation, et son propre docblock affirme le contraire ("from the store nav") alors que storeRoutes n'a pas d'entree intelligence. → backlog/app-shell/0610.
  • commerce-systems — Le plan d'action strategique envoie l'action CRO vers /insights (qui n'a pas de funnel) au lieu de /systems/conversion (qui en a un, calcule par la meme fonction) ; trois Systems du registre (conversion, seo, tracking) ne sont jamais lies depuis le plan ; la copie du plan est du francais code en dur dans une app a six locales (src/features/action-plan/synthesizer.ts). → backlog/commerce-systems/0611.
  • marketplace — L'ecriture AdminAuditLog d'une resolution de litige est catch {} silencieuse APRES l'execution du remboursement Stripe : un echec laisse zero trace de qui a decide quoi pour combien. → backlog/marketplace/0612.
  • growth-web — /features/intelligence affiche "Twenty probes" (titre de section) et "30+ probes" (badge + stat) dans le meme viewport, dans les six locales ; le vrai compte est 35 cles / 34 sondes (probes/index.ts). → backlog/growth-web/0613.
  • growth-web — /community/docs vend 362 endpoints REST versionnes et une doc a plusieurs versions qui n'existent pas (content/docs/en/ ne contient qu'un fichier), en pointant sur /features/api dont la FAQ dit l'inverse ("Not yet, and we would rather say so than sell one"). → backlog/growth-web/0614.
  • growth-web — Parcours d'installation Spy : l'etape "Pin" se coche automatiquement pour 100% de l'audience visee (la page n'est ouverte que par chrome.runtime.onInstalled, donc installed est toujours vrai, et le seul conseil actionnable — "epingle-moi" — se cache exactement pour eux) ; le tutoriel MDX renvoie vers une fiche marketplace dont le CTA mene a la home, jamais au Chrome Web Store. → backlog/growth-web/0615.

P3 — mineurs, coherence/doc uniquement (pas d'item individuel — a corriger a l'occasion d'un passage dans le fichier concerne)

  • docs/architecture/marketplace.md affirme un ISR (revalidate = 300) que le layout racine (nonce CSP) desactive de fait ; contredit sa propre section 10, quinze lignes plus bas.
  • Plusieurs commentaires perimes dans shell-client.tsx / sub-header-nav.tsx (routes rules/connectors non mappees dans activeTab, entree ~/activity qui ne mene nulle part, studio toujours cite comme "deux contextes" alors qu'il n'en a plus aucun dans la barre).
  • /account/settings/privacy reste orpheline (aucun lien entrant) malgre un controle produit reel (opt-out classement).
  • setFeedbackResponse, updateRoadmapItem, deleteRoadmapItem, updateChangelogEntry : quatre server actions admin sans appelant.
  • CompactionSeparator/ComposerUrlSuggestions : deux composants ayant perdu leur dernier appelant lors des livraisons recentes (0394, 0573), a trancher (brancher ou supprimer) au prochain passage sur ces fichiers.

Verification

  • pnpm test : suite complete verte (voir run post-correctif).
  • pnpm typecheck : vert.
  • pnpm lint : vert.
  • pnpm i18n:parity, pnpm docs:claims, pnpm copy:dashes, pnpm copy:case : verts apres chaque lot d'edits messages/*.json.
  • pnpm fleet:status : active locks 0, plus aucun item claimed/ in-progress orphelin.

Verdict

Le produit est structurellement pret : l'auth, le RBAC, l'isolation tenant, l'idempotence billing et les quatre P0 du 3 septembre tiennent a l'epreuve d'un second passage independant. Les quinze correctifs ci-dessus fermaient des dead ends et incoherences reelles introduits ou manques par le rythme de livraison des derniers jours (pricing v4.0, refonte Features, purge ContextIQ). Les points restants (KYC bypass, navigation acheteur absente, split-brain connecteurs, boucle scan cassee) sont reels et meritent chacun leur item — aucun n'est bloquant pour un premier accueil d'utilisateurs, mais le contournement KYC et l'absence de nav acheteur sont a traiter en priorite haute avant une croissance de volume sur le marketplace.