Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.snakysec.com/llms.txt

Use this file to discover all available pages before exploring further.

Audit UX externe SnakySec MSSP — Recommandations correctives (Phases 3, 4, 5)

Consultant UX senior · audit externe · Avril 2026 Cible : V1 commerciale Q2 2026, solo founder, PME France régulées NIS2 Source : Claude Web, à partir du prompt docs/project/CLAUDE_WEB_PROMPT.md

Arbitrages produit (Phase 4 — décidés 2026-04-29)

Décisions prises par Nicolas (founder) après revue des recos Claude Web. Ces arbitrages priment sur les recos dans le reste du doc en cas de conflit.

A1 — Identité du portail client : SnakySec V1, white-label V2

Contexte : Q6 du doc (white-label vs identité SnakySec). Décision : Le portail conserve l’identité SnakySec en V1 (header <PortalSidebar> avec Wordmark + “Client Portal”). White-label dynamique (logo client, couleurs custom, nom tenant) différé V2. Pourquoi : Le pitch “marque blanche” est un argument commercial mais pas un blocker pour la cible Q2 2026. Effort de templating non justifié sur ~3 clients pilotes. À reconsidérer dès qu’un MSSP partenaire revend SnakySec sous sa marque. Impact recos : aucune reco modifiée, simplement REC-MIN à venir pour V2 quand le besoin émerge.

A2 — Formats de rapport sur le portail client : PDF Executive seul

Contexte : REC-CRIT-05 propose PDF + Excel sur le portail. Décision : Portail client = PDF Executive uniquement. Excel / HTML / JSON restent MSSP-only via <AuditActions>. Le composant unifié <ReportDownloadMenu> accepte une prop availableFormats qui filtre selon le contexte (portal['pdf-executive'], dashboard['pdf-executive', 'pdf-technical', 'excel', 'html', 'json']). Pourquoi : Un Excel téléchargé par le client = données brutes des 220 contrôles, forwardable à un concurrent (assureur, autre MSSP). C’est l’actif commercial du MSSP. Le PDF Executive (vue de comité) est suffisant pour le besoin “rassure mon assureur” du persona PME. Impact recos : REC-CRIT-05 amendée — formats portal restreint à PDF Executive.

A3 — DR Runbook layout : sidebar dashboard collapsée (pas masquée)

Contexte : REC-IMP-07 propose de masquer la sidebar dashboard sous /dashboard/help/dr/*. Décision : Pas masquer. Solution alternative — collapse automatique de la sidebar dashboard en mode icônes-seules (~64px au lieu de 256px) quand on entre dans /dashboard/help/dr/*. Le contenu doc gagne 192px utiles, la convention “sidebar toujours visible” tient. Pourquoi : Masquer casse une convention transverse pour un cas niche (la doc DR consultée en incident). Le collapse icon-only est un compromis qui préserve la nav (clic icône reste possible) et libère de l’espace. Si test pilote montre toujours trop chargé visuellement, bascule sur la solution Claude Web en V1.5. Impact recos : REC-IMP-07 amendée — collapse au lieu de masquage.

A4 — Vocabulaire FR canonique : “Écart” pour Finding

Contexte : Q2 du doc + REC-CRIT-04 (glossaire). Décision : Terme canonique en FR = “Écart” (ou “Écarts” au pluriel). “Finding” reste en EN. Impact sur tous les libellés UI FR : “Écarts ouverts”, “Écarts critiques”, “X écarts”, “Severity de l’écart”. Le DB schema (GapFinding) ne change pas. Pourquoi : Aligné avec la méthodologie ANSSI (référence transverse du produit, cf. PSSI / EBIOS / SecNumCloud). Compréhensible immédiatement par un DSI PME non-cyber. Évite l’anglicisme inutile sur la cible commerciale franco-française. Impact recos : REC-CRIT-04 amendée — “Écart” tranchée canonique. REC-MIN-20 confirmée. Glossaire à initialiser avec ce choix.

Section 1 — Top 10 priorités V1 (priorisé)

Le critère de priorisation combine : impact sur les deux personas (MSSP Operator quotidien + Client PME ponctuel), risque commercial pour la V1 (un prospect PME qui voit un mix FR/EN sur le portail va douter du sérieux du produit), et effort relatif au temps disponible avant Q2 2026.
RangID problème existantRecommandation (1 phrase)Impact utilisateurEffort estimé
1PROB-INC-01, BUG-04Éradiquer tous les strings hardcodés et passer 100% de la plateforme par next-intl avec validation CIFortL
2PROB-ORPH-01, PROB-ORPH-02Ajouter une entrée “Documents” et “Plan de remédiation” dans le header de la page client + sidebar contextuelleFortM
3PROB-CTX-02Ajouter un breadcrumb au layout portal et un back-link sur les pages de détailFortS
4PROB-INC-06, PROB-CTX-01Refondre le header de la page client : 3 actions max + menu “Plus”, tabs synchronisés URLFortM
5PROB-DUP-01, PROB-DUP-02, PROB-DUP-03, PROB-DUP-04, BUG-05Unifier le composant TriggerAudit en un seul point d’entrée modale et fusionner DownloadReportButton/AuditActionsMoyenM
6PROB-INC-11, PROB-INC-02, PROB-INC-03, PROB-INC-04Établir et appliquer un glossaire FR/EN unique (Findings = Écarts, Trigger = Lancer, Sign Out partout)MoyenM
7PROB-CTX-04Inliner un mini AuditLiveMonitor (progress + stage) dans le banner “Audit en cours” de la page clientMoyenS
8PROB-CTX-05Remplacer les confirm() natifs par un AlertDialog shadcn standardisé avec preview de l’actionMoyenS
9PROB-INC-05, PROB-CTX-03Imposer la convention “Breadcrumb obligatoire sur toute page de profondeur ≥ 2” et l’appliquer en priorité aux 7 routes manquantesMoyenM
10BUG-01, BUG-02, BUG-03, BUG-06Corriger les 4 bugs techniques bloquants (STEP_ORDER, lien couverture, redirect filtres, error=forbidden)Faible-MoyenS
Total effort : 3 L (mais REC-CRIT-01 i18n est le seul vrai L au sens “2-3 jours pleins”), le reste est S/M absorbable en 2-3 semaines de focus.

Section 2 — Recommandations critiques (REC-CRIT-NN)

REC-CRIT-01 — Internationalisation 100% via next-intl + validation CI

Problèmes adressés : PROB-INC-01, BUG-04, et indirectement PROB-INC-04 Cible utilisateur : les deux (mais surtout Client PME francophone qui voit “Logout”, “Overview”, “Findings” sur son portail) Frame : Un prospect PME qui clique sur le drapeau français s’attend à ce que toute l’interface bascule en français. Aujourd’hui la moitié reste en anglais — ça projette une image de produit non fini, particulièrement gênant sur le portail qui est la vitrine commerciale du MSSP vers son client. Solution proposée :
  • Auditer exhaustivement les fichiers portal-sidebar.tsx, dashboard-filters.tsx, audit-filters.tsx, settings/users/page.tsx, settings/audit-log/audit-log-content.tsx, portal/audits/[id]/page.tsx, portal/documents/page.tsx, clients/[id]/edit/page.tsx, settings/page.tsx et migrer chaque string vers messages/fr.json + messages/en.json.
  • Ajouter un script CI (scripts/check-hardcoded-strings.ts) qui parse les fichiers .tsx sous platform/src/app/** et platform/src/components/** et lève un warning si un littéral string >2 mots est trouvé hors t(), Trans, getTranslations, attributs techniques (className, href, key, id, role, type) et imports.
  • Faire échouer le build CI sur ce script.
  • Établir une exception list explicite (i18n-ignore.json) pour les noms propres (“SnakySec”, “CIS”, “SCuBA”, “Microsoft 365”, “DICT”, etc.) — ce sont des marques, pas des labels.
  • Documenter la convention dans docs/conventions/i18n.md.
Pourquoi cette solution :
  • La cible commerciale est franco-française régulée NIS2 — le français parfait n’est pas optionnel, c’est un signal de crédibilité produit.
  • La validation CI empêche la régression : sans elle, la dette se reformera dès le prochain feature.
  • next-intl est déjà en place ; on ne migre pas, on complète.
Métriques de succès :
  • 0 string hardcodé détecté par le script CI sur platform/src/app et platform/src/components.
  • Toggle FR/EN sur n’importe quelle page bascule 100% du contenu visible.
  • Test manuel sur les 6 pages portail : aucun mot anglais résiduel.
Risques / tradeoffs :
  • Le script CI peut générer des faux positifs (aria-label, placeholder) — prévoir 1 itération de tuning sur la regex.
  • Effort de traduction réel sur les ~80-100 strings résiduels (4-6h dédiées + 4-6h de migration code + 2-3h de mise en place CI).
  • La discipline doit être tenue dans la durée — pas un one-shot.

REC-CRIT-02 — Page client : header refondu en 3 zones (Identity / Operations / Plus)

Problèmes adressés : PROB-INC-06, PROB-CTX-01, et facilite l’adressage de PROB-ORPH-01, PROB-ORPH-02 Cible utilisateur : MSSP Operator (page la plus visitée quotidiennement) Frame : La page /dashboard/clients/[id] est le cockpit quotidien du MSSP. Aujourd’hui c’est 8 boutons d’action en ligne dans le header, 3 tabs, 2 banners possibles. Sous 1280px ça wrap mal, et le MSSP qui jongle entre 5-30 tenants doit re-scanner toute la barre à chaque visite parce qu’aucune hiérarchie visuelle ne dit “voilà l’action principale”. Solution proposée :
  • Action primaire unique : Trigger Audit reste seul, à droite du header, en variant default (primary).
  • Tabs élargies à 5 onglets synchronisés par query param ?tab=, devenant la surface de navigation interne au client : Overview (état + features) · Audits (history) · Findings (écarts) · Remediation (renvoi workspace + plan exec) · Documents (renvoi hub GRC client + secrets + edit en sub-actions).
  • Menu “Plus” (kebab) à gauche du bouton Trigger, qui regroupe les actions rares ou administratives : Edit, Credentials, Trajectory, CISO Assistant, Guide PDF.
  • Suppression des boutons individuels Findings, Remediation, Documents du header (ils deviennent des tabs).
  • Conserver le breadcrumb Dashboard > Clients > [name] et ajouter le 4e niveau dynamique (nom de l’onglet actif).
  • Sync defaultValue du Tabs avec searchParams.tab côté Server Component.
Pourquoi cette solution :
  • Hiérarchie d’action enfin lisible : 1 primaire, 5 navigations, le reste en “Plus”.
  • Couvre PROB-ORPH-01 et PROB-ORPH-02 sans créer de nouvelle route — les pages dédiées restent accessibles via les tabs.
  • Tab persisté en URL résout PROB-CTX-01 et permet de partager un lien profond.
  • Modèle reproductible pour /dashboard/audits/[id] qui souffre du même problème.
Métriques de succès :
  • Test à 1280px : header tient sur une ligne, aucun wrap.
  • Reload sur l’onglet History conserve l’onglet (vérifié avec ?tab=history).
  • Hub documents client atteignable en 1 clic depuis le client (vs 0 chemin direct aujourd’hui).
Risques / tradeoffs :
  • L’opérateur a peut-être appris l’emplacement actuel des 8 boutons — il y a un coût de réapprentissage.
  • Le menu kebab cache des actions, ce qui peut allonger le chemin pour Edit ou Credentials. Acceptable car ce sont des actions rares (~1 fois par cycle de vie client).
  • Sub-routes orphelines (/edit, /secrets, /trajectory) restent indispensables — il faut s’assurer que le breadcrumb reste cohérent quand on quitte une tab pour une sub-route.

Problèmes adressés : PROB-CTX-02, en partie PROB-INC-05 Cible utilisateur : Client PME (utilisateur ponctuel, niveau technique faible, doit pouvoir se repérer) Frame : Le DSI de PME se connecte 2-3 fois par an, ouvre un mail “votre rapport est prêt” qui le mène sur /portal/audits/[xyz], lit le score, et… ne sait pas comment voir les autres audits ou retourner à son tableau de bord. Le bouton retour navigateur fonctionne, mais l’absence de breadcrumb donne une impression de “page perdue” qui mine la confiance. Solution proposée :
  • Ajouter un composant <PortalBreadcrumb> rendu dans /portal/layout.tsx (juste sous le Header), avec une logique simple basée sur usePathname().
  • Profondeur 2 maximum : Portail > Audits > Détail ou Portail > Documents.
  • Le 1er niveau “Portail” pointe sur /portal.
  • Sur les pages de détail (/portal/audits/[id], futurs /portal/findings/[id]), ajouter un back-link textuel ”← Retour aux audits” en haut du contenu, en plus du breadcrumb.
  • Réutiliser le composant shadcn <Breadcrumb> déjà importé côté dashboard pour cohérence visuelle.
  • Pour le mode impersonate, le banner orange “Quitter” reste, mais ajouter un sous-texte “vous voyez le portail comme [client name]” pour ancrer le contexte.
Pourquoi cette solution :
  • Le client PME a besoin de repères forts car il revient peu — chaque visite est quasiment une découverte.
  • Le coût d’implémentation est faible (1 composant + 1 import dans le layout), pour un gain de lisibilité immédiat.
  • Symétrise avec le dashboard MSSP qui a déjà des breadcrumbs : cohérence interne.
Métriques de succès :
  • 100% des pages /portal/* ont un breadcrumb visible.
  • Test PME pilote : le DSI arrive sur /portal/audits/[id] depuis un mail, peut retourner à la liste des audits sans utiliser le bouton back du navigateur.
Risques / tradeoffs :
  • Ajouter un breadcrumb de 24px de hauteur réduit l’espace utile pour le contenu — sur mobile ça compte.
  • Convention à propager : si demain /portal/findings/[id] est créé, il faudra que le breadcrumb suive.

REC-CRIT-04 — Glossaire FR/EN unique et application transverse

Problèmes adressés : PROB-INC-11, PROB-INC-02, PROB-INC-03, PROB-INC-04 Cible utilisateur : les deux Frame : “Findings”, “écarts”, “non-conformes”, “open gaps”, “Top Critical Findings” désignent la même chose. “Trigger”, “Lancer”, “Launch”, “Retry” se croisent. “Voir” déclenche tantôt une navigation, tantôt une action serveur (impersonate). Un opérateur qui forme un nouveau collègue passe 10 minutes à expliquer le vocabulaire — un client PME se demande si “Findings” et “Écarts” sont deux choses différentes. Solution proposée :
  • Produire un fichier docs/conventions/glossary.md qui fixe le vocabulaire produit en FR et EN, avec un terme canonique par concept :
    • Finding / Écart (canonique en FR : “Écart”)
    • Trigger audit / Lancer un audit (canonique en FR : “Lancer un audit”)
    • Sign out / Se déconnecter (canonique partout, supprimer “Logout” du portal et “Quitter” du banner)
    • Severity / Sévérité
    • Compliance score / Score de conformité
    • Remediation / Plan d’action ou Remédiation (à arbitrer)
  • Distinguer “Voir” (navigation) de “Prévisualiser comme ce client” (action impersonate) — le bouton impersonate doit être labellisé “Prévisualiser le portail” pour signaler l’effet de bord.
  • Migrer tous les fichiers messages/fr.json et messages/en.json pour respecter le glossaire.
  • Ajouter une section dans les conventions de PR : tout nouveau string passe par le glossaire ou justifie un ajout.
Pourquoi cette solution :
  • Vocabulaire consistent = produit qui paraît mature et pensé.
  • Coût marginal après REC-CRIT-01 puisqu’on touche déjà tous les fichiers i18n.
  • Le glossaire devient un artefact commercial réutilisable (formation client, doc utilisateur, vidéo démo).
Métriques de succès :
  • 1 seul terme canonique par concept dans messages/fr.json.
  • Aucun “Findings” en FR, aucun “Logout” en EN sur le portal.
Risques / tradeoffs :
  • Le terme “Findings” est très utilisé en cyber FR — certains clients PME peuvent préférer l’anglais. À tester avec 2-3 prospects.
  • Le glossaire fige des choix qui devront être tenus.

REC-CRIT-05 — Unification des points de lancement d’audit et de téléchargement de rapports

Problèmes adressés : PROB-DUP-01, PROB-DUP-02, PROB-DUP-03, PROB-DUP-04, BUG-05 Cible utilisateur : MSSP Operator + Client PME Frame : Aujourd’hui pour télécharger un rapport, il existe <AuditActions> côté MSSP (PDF/Excel/HTML) et <DownloadReportButton> côté portal (probablement seulement PDF). Pour lancer un audit, il y a <TriggerAuditButton> et un lien texte dans le hub documents. Pour télécharger un plan de remédiation, il y a 3 endroits avec 2 endpoints différents. Le risque opérationnel : un client PME qui demande l’Excel ne peut pas l’avoir car il est sur le portail. Solution proposée :
  • Fusionner <DownloadReportButton> et la partie “downloads” de <AuditActions> dans un seul composant <ReportDownloadMenu> qui prend en prop formats: ('pdf' | 'excel' | 'html' | 'json')[] et mode: 'menu' | 'inline'.
  • Côté portal, exposer PDF + Excel (l’opérateur peut décider de masquer Excel si commercial le demande, via flag client).
  • Le lien texte “Lancer un audit” dans /dashboard/clients/[id]/documents empty state doit ouvrir la même modale <TriggerAuditButton> (pas un redirect).
  • Pour le plan de remédiation : un seul endpoint canonique /api/v1/clients/[id]/documents/remediation-plan qui agrège milestones + actions. Supprimer milestones-plan ou le marquer deprecated.
  • <GuideFilterDialog> reste mais devient une variante du flow “Generate report” plutôt qu’un bouton header isolé — il s’intègre dans la tab Remediation (cf. REC-CRIT-02).
Pourquoi cette solution :
  • Réduire la surface UI réduit les risques de divergence (un nouveau format ajouté ne sera plus à propager dans 2 endroits).
  • Cohérence MSSP/portal : le client voit les mêmes options de download que son MSSP, ça simplifie le support.
  • Élimine 4 doublons d’un coup.
Métriques de succès :
  • 1 seul composant React pour les downloads de rapport.
  • 1 seul endpoint canonique pour le plan de remédiation.
  • Test : un MSSP qui ajoute le format CSV à <ReportDownloadMenu> n’a qu’un fichier à modifier.
Risques / tradeoffs :
  • Refactor qui touche du code utilisé partout — risque de régression.
  • Si le portail expose Excel par défaut, il faut s’assurer que le RBAC CLIENT_USER autorise bien le download Excel (à vérifier sur les routes API).
  • Le merge nécessite des tests manuels exhaustifs avant V1 puisque la couverture Vitest est à 0%.

REC-CRIT-06 — Banner “Audit en cours” enrichi avec progression live

Problèmes adressés : PROB-CTX-04 Cible utilisateur : MSSP Operator Frame : Quand le MSSP a lancé un audit et navigue ailleurs (un autre client, un rapport), il revient régulièrement sur la page du client pour vérifier où en est le pipeline. Aujourd’hui le banner “Audit en cours” est juste un lien — il faut cliquer pour aller voir la progression. C’est une friction quotidienne forte pour qui gère 10+ tenants en parallèle. Solution proposée :
  • Étendre le banner sur /dashboard/clients/[id] pour afficher : progress bar + stage actuel (“Importing Graph data”, “Running checks 47/220”, etc.) + ETA si disponible.
  • Réutiliser les données du SSE stream du <AuditLiveMonitor> côté serveur (ou l’inliner en mode “compact”).
  • Garder le lien “Voir le détail complet” qui mène à /dashboard/audits/[id].
  • Sur /dashboard (hub MSSP), exposer une card “Audits en cours” qui liste tous les audits PENDING/RUNNING/IMPORTING avec leur stage et le lien client. Vue cross-tenant qui manque aujourd’hui.
Pourquoi cette solution :
  • Un MSSP solo qui surveille 5-10 audits simultanés a besoin de scanner l’état global en 5 secondes, pas de cliquer sur chaque client.
  • Réutilise une infra existante (SSE stream déjà en place).
  • Différencie SnakySec face à des outils GRC moins temps-réel.
Métriques de succès :
  • Sur la page client, l’opérateur voit le stage du pipeline sans cliquer.
  • Sur le dashboard MSSP, une card listante les audits en cours est présente quand au moins 1 audit tourne.
Risques / tradeoffs :
  • Multiplier les SSE connexions (1 par card) peut peser côté serveur si beaucoup d’audits parallèles. Prévoir une stratégie de mutualisation ou de polling court (5s) en fallback.
  • Plus de “chrome” UI sur la page client — vérifier que ça ne pousse pas le contenu utile sous le fold.

REC-CRIT-07 — Modales destructives standardisées avec preview

Problèmes adressés : PROB-CTX-05, en partie PROB-INC-10 Cible utilisateur : MSSP Operator Frame : Aujourd’hui, supprimer un audit ou un secret déclenche un confirm() natif du navigateur. C’est laid, ça ne dit pas ce qui sera supprimé exactement, et c’est dangereux : un audit supprimé ce sont des findings perdus, des reports certifiés invalidés, des kpiSnapshots détachés. Pareil pour le bouton “CSV + before/after” qui peut générer un export très lourd sans avertissement. Solution proposée :
  • Adopter le pattern shadcn <AlertDialog> (déjà disponible via Radix) pour toutes les actions destructives ou coûteuses.
  • Composant maison <DestructiveActionDialog> standardisant : titre, description (préfilled “Vous êtes sur le point de supprimer X. Cette action est irréversible.”), preview des entités impactées (ex: “3 findings, 1 rapport certifié, 12 lignes de log seront supprimés”), input de confirmation pour les actions critiques (taper le nom de l’audit pour valider), bouton primary destructif.
  • Appliquer à : delete audit, delete secret, revoke API key, “CSV + before/after” (qui doit prévenir “export potentiellement volumineux”), suppression de milestones avec dépendances.
  • Le banner impersonation “Quitter” peut rester simple (pas destructif).
Pourquoi cette solution :
  • Un audit supprimé par erreur est un incident sérieux dans un contexte opposable juridiquement (audit log scellé).
  • Le pattern est simple à standardiser et donne un effet visuel de pro immédiat.
  • Préventif vs curatif : éviter le clic accidentel coûte moins cher qu’un restore.
Métriques de succès :
  • 0 window.confirm() dans le code.
  • Pour delete audit/secret/api-key : preview obligatoire des entités liées.
Risques / tradeoffs :
  • L’opérateur expert peut trouver les confirmations “lourdes” — proposer un raccourci “Skip confirmation for 24h” est tentant mais déconseillé pour V1 (sécurité produit).

Section 3 — Recommandations importantes (REC-IMP-NN)

REC-IMP-01 — Convention de breadcrumb stricte et application aux 7 routes manquantes

Problèmes adressés : PROB-INC-05, PROB-CTX-03 Cible utilisateur : les deux Frame : Certaines pages ont un breadcrumb 4-niveaux, d’autres juste un back arrow, d’autres rien. L’utilisateur ne sait jamais à quoi s’attendre. Solution proposée : Imposer la règle “toute page de profondeur ≥ 2 (i.e. accessible par >1 clic depuis le hub) DOIT avoir un breadcrumb shadcn complet”. Appliquer à : /dashboard/clients/new, /dashboard/settings/users, /dashboard/settings/api-keys, /dashboard/settings/audit-log, /dashboard/settings/notifications, /dashboard/help/dr/[...slug], et /dashboard/clients/[id]/onboarding/[step]. Le back arrow seul est interdit comme nav primaire. Pourquoi : Cohérence = confiance produit. Coût implémentation faible (1 import + 1 composant par page). Métriques : 100% des pages dashboard ont un breadcrumb. Aucun back arrow seul résiduel. Risques : Le breadcrumb du wizard onboarding doit cohabiter avec le stepper sidebar — risque de redondance visuelle.

REC-IMP-02 — Documents GRC : taxonomie repensée par cas d’usage

Problèmes adressés : indirect (couvre la confusion “8 docs / 7 catégories” mentionnée dans la dette) Cible utilisateur : MSSP Operator Frame : Aujourd’hui les 8 documents sont éparpillés sur 7 catégories (Governance, Compliance, Access, Incidents, Monitoring, Onboarding, Technical). Le MSSP qui cherche “le doc à donner à l’assureur” doit chercher. Solution proposée : Reclasser en 3 catégories orientées cas d’usage : Documents de gouvernance (PSSI, Charte IT, Politique d’Accès, Politique de Classification, Registre des Traitements) · Documents d’incident (Procédure Incident, Notification de Violation) · Documents de pilotage (Plan de Remédiation). Les catégories techniques (Monitoring, Onboarding, Technical) deviennent une page de docs internes/runbooks séparée. Pourquoi : Les catégories actuelles sont des labels MSSP-internes — les renommer côté usage facilite la formation d’analystes tiers et la lecture client. Métriques : Test interview MSSP “où trouvez-vous le doc PSSI” → réponse en <5 secondes. Risques : Casse les bookmarks existants. À faire avant V1, pas après.

REC-IMP-03 — Onboarding banner intelligente

Problèmes adressés : couvre le point de friction “banner persistant après audit réussi” (mentionné dans dette) Cible utilisateur : MSSP Operator Frame : La banner amber reste tant que onboardingStatus !== "COMPLETED", même quand l’audit a tourné, créant un “bruit visuel” permanent. Solution proposée : Auto-marquer onboarding comme COMPLETED dès que le premier audit COMPLETED a été importé pour le client (sans devoir cliquer manuellement “Done”). Conserver le step Done comme étape consultable mais retirer son blocage. Si reactivation du wizard nécessaire, exposer un bouton dans le menu “Plus” (cf. REC-CRIT-02). Pourquoi : Le wizard a rempli sa fonction quand l’audit tourne — pas besoin d’un clic supplémentaire. Métriques : Aucune banner d’onboarding visible une fois le 1er audit COMPLETED. Risques : Si l’utilisateur n’a pas validé le step Users (step 6), il peut ne pas avoir invité ses CLIENT_USER. Prévoir une banner légère “Pensez à inviter les utilisateurs côté client” sur la tab Overview tant que ClientUserAccess.count === 0.

REC-IMP-04 — /dashboard/audits/[id]/diff : entrée explicite dans la liste audits

Problèmes adressés : PROB-ORPH-04 Cible utilisateur : MSSP Operator Frame : Le diff entre 2 audits est une fonctionnalité forte (régressions, améliorations, status changes). Elle est cachée derrière un <CompareSelector> dans le header de la page audit detail. Solution proposée : Dans la liste /dashboard/audits et dans la History tab du client, ajouter une icône “Comparer au précédent” sur chaque ligne qui pointe directement vers /audits/[id]/diff?vs=[id-1] (le run immédiatement antérieur du même client). Garder le <CompareSelector> pour les comparaisons non-adjacentes. Ajouter un lien “Voir les régressions de la semaine” sur le dashboard MSSP qui agrège les diffs récents cross-tenant. Pourquoi : Le diff est exactement le type de feature que le MSSP veut pousser au client en réunion mensuelle (“voici ce qui s’est dégradé”) — la cacher est un manque à gagner commercial. Métriques : >50% des accès au diff passent par la liste audits (pas par CompareSelector) après 1 mois d’usage. Risques : Il faut gérer le cas du 1er audit (pas de précédent) — désactiver l’icône ou la masquer.

REC-IMP-05 — Convention “card-as-link” tranchée

Problèmes adressés : PROB-INC-09 Cible utilisateur : MSSP Operator Frame : Sur certaines pages (clients, settings, documents) la card entière est cliquable, sur d’autres seul un bouton dans la card l’est. L’utilisateur clique parfois “à côté” et rien ne se passe. Solution proposée : Adopter la règle “une card ne doit jamais être un lien si elle contient elle-même un bouton d’action”. Pour les cards listings (clients, audits cards), la card complète devient le lien et les actions secondaires (Impersonate) deviennent un menu kebab dans le coin haut-droit, qui stoppe la propagation du clic. Pour les cards hub (settings, documents), card-as-link OK car elles n’ont pas d’actions internes. Pourquoi : Règle simple, applicable mécaniquement, évite les confusions de zone cliquable. Métriques : Aucune card avec à la fois <Link> wrapper ET <Button> interne sur la même surface. Risques : Le menu kebab demande un design discret pour ne pas surcharger les cards client.

REC-IMP-06 — Filtres URL-synced : composant unique <UrlFilters>

Problèmes adressés : PROB-INC-07, BUG-03 Cible utilisateur : MSSP Operator Frame : <DashboardFilters> et <AuditFilters> font la même chose (sync URL params, ouvrir router.push) mais avec un design et une API différents. Et <DashboardFilters> redirige toujours vers /dashboard même si on est ailleurs (BUG-03). Solution proposée : Créer un composant générique <UrlFilters> qui prend une définition déclarative [{ key: 'client', type: 'select', options: [...] }, { key: 'period', type: 'toggleGroup', options: [...] }] et synchronise tout via usePathname() + searchParams. Remplacer les deux composants existants par cette version. Documenter le pattern. Pourquoi : Filtres = pattern récurrent qui apparaitra sur les futures pages (alerts, findings, integrations). Capitaliser maintenant évite la dette future. Métriques : 1 seul composant filter, pas de hardcoded route push. Risques : Sur-ingénierie possible si on n’a que 2 cas d’usage. Acceptable car déjà 2 et 3-4 prévus.

REC-IMP-07 — DR Runbook : layout 2-niveaux au lieu de 3

Problèmes adressés : PROB-CTX-08, PROB-CTX-07 Cible utilisateur : MSSP Operator Frame : Sur /dashboard/help/dr/*, on empile sidebar dashboard + sidebar DR + header — sur 1280px le contenu doc tient sur ~600px. Et le viewer markdown n’a pas de breadcrumb (juste un slug brut en footer). Solution proposée : Quand on est sous /dashboard/help/dr/*, masquer la sidebar dashboard principale et la remplacer par la sidebar DR (avec un bouton ”← Retour au dashboard” en haut). Une seule sidebar à la fois. Ajouter un breadcrumb 3-niveaux dans le viewer : “DR Runbook > [Section] > [Doc]” extrait du slug. Pourquoi : Lisibilité de la doc DR critique en cas d’incident — c’est le moment où l’opérateur a le moins envie de chercher. Métriques : Largeur utile contenu doc >900px sur écran 1280px. Risques : Casse la convention “sidebar dashboard toujours visible”. Justifié par le contexte spécifique de la doc DR.

REC-IMP-08 — error=forbidden géré par un toast global

Problèmes adressés : PROB-ORPH-08, BUG-06 Cible utilisateur : MSSP Operator Frame : Plusieurs gardes redirigent vers /dashboard?error=forbidden mais la page ne fait rien du paramètre. L’utilisateur arrive silencieusement et ne sait pas pourquoi il a été renvoyé. Solution proposée : Ajouter un client-side hook dans le dashboard layout qui lit searchParams.error et déclenche un toast destructif “Vous n’avez pas les permissions pour accéder à cette ressource” + nettoie le param de l’URL via router.replace. Standardiser les codes d’erreur (forbidden, not_found, expired_session). Pourquoi : Feedback explicite = trust. Coût trivial (1 hook, 5 lignes). Métriques : Un MSSP_ANALYST qui tente d’accéder à une page MSSP_ADMIN voit un toast clair, pas une redirection silencieuse. Risques : Aucun.

Section 4 — Recommandations mineures (REC-MIN-NN)

  • REC-MIN-01 — adresse PROB-DUP-05 — Extraire la logique documentContexts dans lib/document-contexts.ts partagé entre dashboard et portal (S).
  • REC-MIN-02 — adresse PROB-DUP-06, PROB-INC-04 — Garder un seul “Sign Out” : footer sidebar (dashboard + portal). Retirer celui du dropdown header (redondant).
  • REC-MIN-03 — adresse PROB-DUP-07 — Garder uniquement <PortalClientPicker> comme entrée d’impersonation et déplacer <ImpersonateClientButton> dans un menu kebab discret sur la card client.
  • REC-MIN-04 — adresse PROB-ORPH-03 — Sur /dashboard/clients/[id]/controls/[controlId], ajouter prev/next buttons pour naviguer entre contrôles consécutifs du même audit.
  • REC-MIN-05 — adresse PROB-ORPH-05 — Documenter le comportement /dashboard/clients/[id]/onboarding (resume) dans le code en commentaire JSDoc, comportement intentionnel.
  • REC-MIN-06 — adresse PROB-ORPH-06 — Migrer la card “Notifications email” du settings hub dans la même boucle i18n que les autres (suppression du hardcoded FR).
  • REC-MIN-07 — adresse PROB-ORPH-07 — Ajouter une sidebar collapsible “Settings” avec les 6 sous-pages, sur le même modèle que Documents (S).
  • REC-MIN-08 — adresse PROB-INC-02 — Verbe canonique “Lancer” en FR, “Run” en EN (pas Trigger ni Launch). Migrer les ~5 occurrences.
  • REC-MIN-09 — adresse PROB-INC-08 — Extraire le styling actif sidebar dans une classe utilitaire sidebar-active réutilisée partout, supprimer les 3 variantes.
  • REC-MIN-10 — adresse PROB-INC-10 — Le bouton “CSV + before/after” devient une option dans une dropdown “Export” avec mention explicite “(volumineux)”.
  • REC-MIN-11 — adresse PROB-CTX-06 — Sur tous les empty states filtrés, ajouter un bouton “Réinitialiser les filtres” + icône explicite (réutiliser <EmptyState>).
  • REC-MIN-12 — adresse PROB-CTX-09 — Remplacer router.back() du Cancel sur EditClient par un <Link href={/dashboard/clients/$}> explicite.
  • REC-MIN-13 — adresse PROB-CTX-10 — Le “Quitter” du banner impersonate redirige vers /dashboard/clients au lieu de /dashboard.
  • REC-MIN-14 — adresse PROB-CTX-11 — Sur EditClientPage, mapper les erreurs serveur sur les champs concernés via setError() de react-hook-form + scroll-to-error.
  • REC-MIN-15 — adresse PROB-CTX-12 — Dans la modale TriggerAudit, afficher un récap inline “Vous allez auditer : Entra, Exchange, Teams (3 zones · 110 contrôles estimés)” juste au-dessus du bouton Launch.
  • REC-MIN-16 — adresse BUG-01 — Inclure step 8 (Done) dans STEP_ORDER ou documenter explicitement que la stepper sidebar est volontairement de 7 items.
  • REC-MIN-17 — adresse BUG-02 — Corriger le viewPath du lien “Couverture 220 contrôles” pour pointer vers une vraie page de couverture (ou le supprimer si non implémenté).
  • REC-MIN-18 — adresse BUG-03 — <DashboardFilters> utilise usePathname() au lieu d’un push hardcodé sur /dashboard.
  • REC-MIN-19 — adresse BUG-04 — Migrer les strings “Notifications email” et “DR Runbook” dans messages/fr.json (couvert aussi par REC-CRIT-01).
  • REC-MIN-20 — adresse PROB-INC-11 — “Findings” devient “Écarts” partout en FR, “Findings” reste en EN (covered by REC-CRIT-04).

Section 5 — Patterns transverses recommandés

Pattern P1 — Hiérarchie d’actions de page : 1 primaire, 3 secondaires max, le reste en menu “Plus”

Toute page de détail (client, audit, milestone) doit respecter une hiérarchie visuelle stricte. 1 action primaire (variant default) à droite du header, qui correspond au verbe le plus fréquent du flow utilisateur. 3 actions secondaires max (variant outline) adjacentes, qui sont des navigations vers des sous-vues utilisées au moins 1 fois par session. Toutes les autres vont dans un menu kebab “Plus” (icon Ellipsis). Cette règle évite le syndrome “8 boutons en ligne” de la page client actuelle. Application directe : PROB-INC-06, PROB-CTX-01.

Pattern P2 — Profondeur de navigation 2 niveaux maximum (sidebar > sub-nav, jamais 3)

Le DR Runbook actuel empile 3 niveaux de chrome (sidebar dashboard + sidebar DR + header). Toute page qui nécessite un sous-niveau doit soit le matérialiser en tabs internes, soit remplacer (pas s’ajouter à) la sidebar principale. Cette règle s’applique aussi à l’onboarding wizard qui combine sidebar + stepper sidebar. Application directe : PROB-CTX-08.

Pattern P3 — Tabs synchronisées en URL par défaut

Tout composant <Tabs> shadcn doit synchroniser son state avec un query param dédié (?tab=). Reload, partage de lien, retour navigateur : tout doit conserver l’onglet actif. C’est une convention zéro-coût qui évite mille frustrations. Application directe : PROB-CTX-01, et anticipé pour toutes les futures pages avec tabs.

Pattern P4 — Breadcrumb obligatoire au-delà de la profondeur 2

À partir du moment où une page est accessible par >1 clic depuis un hub (/dashboard, /portal, /dashboard/settings), elle doit afficher un breadcrumb shadcn complet dans son header. Le back arrow seul est interdit comme nav primaire (acceptable en complément). Le 4e niveau du breadcrumb peut refléter la tab active. Application directe : PROB-INC-05, PROB-CTX-02, PROB-CTX-03.

Pattern P5 — i18n strict avec validation CI

Aucun string utilisateur visible ne doit être hardcodé hors messages/fr.json et messages/en.json. Un script CI parse les fichiers .tsx et lève une erreur sur les littéraux >2 mots hors t() / Trans / getTranslations. Une exception list explicite couvre les noms propres et termes techniques (CIS, SCuBA, DICT). Cette règle protège la cible commerciale française et permet de scaler vers d’autres langues. Application directe : PROB-INC-01, PROB-INC-04, BUG-04.

Pattern P6 — Vocabulaire produit centralisé dans un glossaire

Un fichier docs/conventions/glossary.md fixe le terme canonique pour chaque concept (Écart vs Finding, Lancer vs Trigger, Score de conformité vs Compliance score). Toute PR qui introduit un nouveau libellé doit soit utiliser un terme du glossaire, soit l’enrichir via revue produit. Le glossaire est aussi un livrable commercial (formation client, doc utilisateur). Application directe : PROB-INC-02, PROB-INC-03, PROB-INC-04, PROB-INC-11.

Pattern P7 — Actions destructives = AlertDialog avec preview d’impact

Aucune action de suppression/révocation/écrasement ne doit utiliser window.confirm(). Un composant standardisé <DestructiveActionDialog> impose : titre clair, preview des entités impactées en chiffres, input de confirmation pour les actions critiques (taper le nom). Cette règle évite les pertes accidentelles d’audit log certifié, donc protège la valeur produit “traçabilité opposable”. Application directe : PROB-CTX-05, PROB-INC-10. Une card est soit entièrement cliquable (pas de bouton interne), soit elle a des actions internes (et n’est pas wrappée dans un Link). Les actions secondaires sur les cards listing (Impersonate sur card client) passent par un menu kebab dans le coin haut-droit qui stoppe la propagation. Application directe : PROB-INC-09.

Pattern P9 — Composant unique pour les filtres URL-synced

Tout filtre qui modifie un query param URL passe par un composant générique <UrlFilters> à API déclarative. Plus de page-spécifique <DashboardFilters> ou <AuditFilters> qui divergent. Cette règle capitalise les comportements (debounce, URL replace, reset all) et évite les bugs de redirect (BUG-03). Application directe : PROB-INC-07, BUG-03.

Pattern P10 — Réutilisation MSSP/portal forcée par variant prop, pas par duplication

Quand un composant existe en deux versions (<DownloadReportButton> vs <AuditActions>, <RemediationPlanView> mêmes deux contextes), il doit être unifié avec une prop variant ou mode, pas dupliqué. Cette règle évite que les fonctionnalités MSSP ne dépassent celles du portail (ou inversement) sans décision produit explicite. Application directe : PROB-DUP-04, BUG-05, et indirectement PROB-DUP-02.

Section 6 — Questions ouvertes / sujets à creuser

Q1 — Le portail client doit-il être 100% white-label ou conserver l’identité SnakySec ?

Aujourd’hui le header du <PortalSidebar> affiche Shield + “SnakySec” + “Client Portal” hardcodé. Le pitch produit parle de “white-label” mais l’implémentation actuelle ne l’est pas (pas de logo client, pas de couleurs custom, pas de nom dynamique). À arbitrer commercialement : est-ce un argument de vente différenciant qui mérite l’effort de templating, ou un nice-to-have V2 ? Une interview avec 2-3 prospects MSSP partenaires (qui revendraient SnakySec en marque blanche) tranchera.

Q2 — “Findings” ou “Écarts” en français : quel terme parle vraiment aux DSI PME ?

Le glossaire propose “Écart” comme canonique en FR, mais le terme “Finding” est largement utilisé dans le jargon cyber français. Risque de paraître pédagogique (“écart”) ou snob (“finding”) selon le client. Test à faire avec 3-5 DSI PME pilotes non-cyber : leur montrer une capture du portail et demander quelle traduction leur est la plus immédiate.

Q3 — Quel taux de retour réel sur le portail client par mois ?

Le persona Client PME est décrit comme “ponctuel” (2-3 connexions par an quand l’assureur demande). Si les analytics confirment ça, alors REC-CRIT-03 (breadcrumb portal) est vital car chaque visite est une re-découverte. Si au contraire les clients reviennent souvent, alors le manque de breadcrumb est moins critique mais l’apprentissage n’est pas aidé. À mesurer via Sentry/Plausible une fois 3-5 clients en prod.

Q4 — Le MSSP solo (Nicolas) gère-t-il vraiment 5-30 tenants en parallèle, ou surtout 2-3 actifs ?

La densité du header client (8 boutons) et l’absence d’une vue “Audits en cours” cross-tenant ne sont des vrais problèmes que si la multiplication des tenants est réelle. Si en V1 commercial Nicolas a 3 clients et fait des audits manuels mensuels, REC-CRIT-06 (banner enrichi) est moins prioritaire. À auto-mesurer sur les 6 prochains mois.

Q5 — Quels formats de rapport les clients PME demandent réellement ?

5 formats sont exposés (PDF Executive, PDF Technical, HTML, Excel, JSON) mais on ignore lesquels sont effectivement téléchargés. PDF Executive = comité de direction, Excel = comptable/RSSI mutualisé, JSON = jamais sauf intégration custom. Si JSON et HTML ne sont jamais touchés, on peut les masquer en V1 et libérer de l’espace UI. À mesurer via les audit logs (action report.download) une fois 3 clients en prod.

Q6 — Le wizard onboarding 7 étapes est-il un vrai parcours guidé ou un formulaire long déguisé ?

Les 7 steps couvrent 4 préoccupations distinctes (identité, technique/credentials, scope, opérationnel/users-schedule). Pour un MSSP expert, c’est trop pour un onboarding récurrent ; pour un nouvel utilisateur, c’est juste. Test modéré à faire : timer un MSSP expert qui onboarde son 5e tenant. Si >15 minutes, regrouper en 3 phases (Identité+Credentials / Scope+Test / Users+Schedule) sera nécessaire post-V1.

Récap final

7 recos critiques, 8 importantes, 20 mineures, 10 patterns transverses, 6 questions ouvertes. Effort total estimé : ~3 semaines de focus solo founder pour absorber les Top 10 avant la V1 Q2 2026, avec REC-CRIT-01 (i18n + CI) comme seul vrai L et le reste en S/M chainables. Le glossaire (REC-CRIT-04) et la convention header (REC-CRIT-02) sont les leviers à plus haute valeur ajoutée car ils éliminent des classes de problèmes futurs, pas seulement des problèmes individuels.