agentleFS
VincwebVincweb/caudron-plugin/skills/architecture-audit/SKILL.md

architecture-audit

Skill0 starsChanged 3 months agoMITView on GitHub

Audite l'architecture d'un projet web React/Hono contre l'architecture cible Caudron — structure des dossiers, isolation des couches, inférence de types Hono RPC, validation Zod, filtrage par vault, plus la rigueur du tsconfig, la stratégie de tests et l'outillage (ESLint typé, Knip, couverture, hook pre-commit). Utiliser quand on demande un audit d'architecture, une revue de structure, une vérification des conventions, ou un état des lieux de l'outillage et de la configuration du projet.

  • Reads credentials

16 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use prototype, review and 1 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • GoogleSkill

    prototype

    Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.

    Also in 3 projects that use this

  • SentrySkill

    review

    Fixture review skill.

    Also in 3 projects that use this

  • GoogleSkill

    tdd

    Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.

    Also in 3 projects that use this

The file

Files
SKILL.md

Skill: Architecture Audit

Analyse le code source pour détecter les écarts avec l'architecture documentée.

Objectif

Comparer l'implémentation réelle du codebase avec les règles définies dans ${CLAUDE_PLUGIN_ROOT}/blueprints/architecture/ et identifier les violations.

Prérequis

Lire les blueprints de référence embarqués dans le plugin :

  • ${CLAUDE_PLUGIN_ROOT}/blueprints/architecture/frontend.md
  • ${CLAUDE_PLUGIN_ROOT}/blueprints/architecture/backend.md
  • ${CLAUDE_PLUGIN_ROOT}/blueprints/architecture/typing.md
  • ${CLAUDE_PLUGIN_ROOT}/blueprints/architecture/tools.md
  • ${CLAUDE_PLUGIN_ROOT}/blueprints/architecture/tests.md

Les chemins de code audités (src/, netlify/) sont relatifs à la racine du projet analysé.

Exécution

Sur un projet de taille non triviale, déléguer à l'agent architecture-auditor (read-only) plutôt que de tout lire soi-même. Il peut tourner en parallèle de design-auditor et clean-code-auditor si un audit complet est demandé.

Règles d'Audit

Frontend (FE-*)

IDRègleSévéritéPattern de Détection
FE-001Hooks simples (TanStack Query only)HIGHChercher useEffect, multiples useState, logique métier dans src/hooks/
FE-002Contexts sans appels réseauHIGHChercher fetch, honoClient dans src/contexts/
FE-003Services isolésMEDIUMChercher imports depuis components, hooks, contexts, pages dans src/services/
FE-004honoClient pour inférence de typesHIGHChercher honoClient.(get|post|patch|delete)< (types génériques manuels) dans src/hooks/
FE-005Utiliser res.ok, pas json.successMEDIUMChercher json.success, !json.success, .success dans src/hooks/
FE-006Pas d'export de types *FromApiMEDIUMChercher export type \w+FromApi dans src/hooks/
FE-007Composants sans appels réseauHIGHChercher fetch, honoClient dans src/components/
FE-008Layouts sans propsMEDIUMChercher export const \w+Layout = \(.*: avec paramètres dans src/layouts/
FE-009Une page = une routeHIGHDétecter même composant utilisé pour plusieurs routes dans src/routes/
FE-010Pas de navigation dans les hooks dataMEDIUMChercher useNavigate, navigate(, toast dans src/hooks/data/
FE-011Pas d'état local dans hooks dataMEDIUMChercher useState (hors queryFn) dans src/hooks/data/
FE-012Services uniquement dans hooks/dataHIGHImports services/ dans src/components/, src/pages/, src/layouts/ — séparer valeur et import type, seuls les premiers couplent à l'exécution
FE-013Pas de re-export de service dans un hookHIGHexport { … } en tête d'un hooks/data/* qui réexporte un service : déplace l'import sans découpler, et rend FE-012 aveugle

Backend (BE-*)

IDRègleSévéritéPattern de Détection
BE-001Method chaining pour Hono RPCHIGHVérifier .get().post() vs router.get() séparé dans netlify/routes/
BE-002Validation Zod sur chaque route POST/PATCHHIGHChercher .post( ou .patch( sans zValidator dans netlify/routes/
BE-003Pas de champ success dans réponses HTTPMEDIUMChercher c.json.*success dans netlify/routes/
BE-004Pas de return type expliciteLOWChercher : Promise< dans netlify/routes/ et netlify/lib/
BE-005Filtrage par vault ID (JWT)HIGHVérifier que les routes protégées utilisent c.get('auth') pour filtrer
BE-006Bons codes HTTPMEDIUMVérifier 201 pour POST, 204 pour DELETE dans netlify/routes/
BE-007Secret requis sans valeur de repliCRITICALprocess.env.X || '…' ou ?? '…' sur un secret : une variable absente fait signer avec une valeur publique et versionnée, sans rien casser ni alerter

Typage (TY-*)

IDRègleSévéritéPattern de Détection
TY-001Pas de return types explicitesLOWChercher : Promise< dans les fonctions backend
TY-002Types inférés depuis DrizzleMEDIUMInterfaces dupliquant les types du schéma Drizzle
TY-003Frontend sans duplication de types DBHIGHChercher interfaces d'entités DB dans src/types/

Code (CD-*)

IDRègleSévéritéPattern de Détection
CD-001Named exports uniquementLOWChercher export default (exception: pages pour React.lazy)
CD-002Pas d'any expliciteHIGHChercher : any, as any, <any> dans tous les fichiers TS
CD-003Code en anglaisLOWVérifier noms de variables/fonctions en français

Configuration TypeScript (CFG-*)

strict: true n'active que huit options. Les cinq suivantes n'en font pas partie et sont celles qui attrapent ce que « strict » laisse passer. Toutes se lisent dans tsconfig.json.

IDRègleSévéritéPattern de Détection
CFG-001strict: trueCRITICALcompilerOptions.strict absent ou false
CFG-002noUncheckedIndexedAccessHIGHOption absente — arr[0] et map[clé] sont typés comme toujours présents
CFG-003verbatimModuleSyntaxMEDIUMOption absente — les import type ne sont pas imposés
CFG-004exactOptionalPropertyTypesMEDIUMOption absente — prop?: T et T | undefined restent confondus
CFG-005noImplicitOverrideLOWOption absente
CFG-006noPropertyAccessFromIndexSignatureLOWOption absente
CFG-007noUnusedLocals + noUnusedParameters + noImplicitReturns + noFallthroughCasesInSwitchMEDIUML'une des quatre absente
CFG-008Config unique à la racineMEDIUMPrésence de tsconfig.app.json / tsconfig.node.json — deux configs divergent

Tests (TE-*)

IDRègleSévéritéPattern de Détection
TE-001Framework de test unitaireHIGHvitest absent des devDependencies — tout tester en E2E laisse la logique pure sans filet
TE-002test = unitaire, test:e2e = bout en boutHIGHscripts.test qui lance playwright : check embarque alors une suite lente exigeant un serveur
TE-003Outil de couvertureMEDIUM@vitest/coverage-v8 absent des devDependencies
TE-004Seuils de couverture qui échouentMEDIUMcoverage.thresholds absent de la config Vitest — un seuil qui n'échoue pas n'est pas un seuil. ⚠️ Sur une base existante, un seuil sous la cible 80/80/70 est conforme s'il est documenté comme cliquet ; ce qui échoue, c'est l'absence de seuil ou un seuil baissé pour faire passer un check
TE-005Couverture ciblée sur la logique pureLOWcoverage.include absent ou couvrant src/ entier au lieu de services/ et lib/
TE-006Tests de composantsLOW@testing-library/react absent alors que le projet a des composants
TE-007Suite unitaire non videHIGHAucun fichier *.test.ts / *.test.tsx alors que src/services/ existe

Outillage (TL-*)

Un outil manquant ne produit aucune erreur : le projet compile et la règle qu'il aurait fait respecter n'existe simplement pas. C'est le seul domaine où l'audit doit chercher une absence.

IDRègleSévéritéPattern de Détection
TL-001ESLint configuréHIGHAbsence de eslint.config.* à la racine, ou eslint absent des devDependencies
TL-002Linting TypeScript typé actifHIGHeslint.config.* sans typescript-eslint / tseslint — lire la config, pas le package.json : le package installé mais non branché ne lint rien
TL-003Prettier configuréMEDIUMAbsence de .prettierrc* / clé prettier, ou prettier absent des devDependencies
TL-004Knip pour le code mortMEDIUMknip absent des devDependencies, ou absence de knip.json / clé knip
TL-005Script checkHIGHscripts.check absent du package.json
TL-006check couvre lint + types + build + testsMEDIUMscripts.check qui n'enchaîne pas lint, typecheck, build et test
TL-007Script deep-checkMEDIUMscripts.deep-check absent — il ajoute format et knip, que check ne lance pas
TL-008Vérification automatique avant commitMEDIUMAbsence de .husky/pre-commit (ou équivalent), ou scripts.prepare sans husky
TL-009Formateur TailwindLOWprettier-plugin-tailwindcss absent alors que le projet utilise Tailwind
TL-010Preset strictTypeCheckedHIGHtseslint.configs.* étendu autrement qu'en strictTypeChecked. ⚠️ js.configs.recommended fait partie de la config attendue — ne pas le confondre
TL-011Garde-fou de taille outilléMEDIUMAbsence de règle max-lines dans eslint.config.* — sans elle, PR-001 et CS-004 reposent sur la relecture
TL-012max-lines sans exemptionMEDIUMBloc de config qui remet 'max-lines': 'off' sur un dossier — une exemption est une règle qu'on a cessé d'appliquer
TL-013Désactivations groupéesLOWPlus de 3 commentaires eslint-disable pour la même règle : elle se désactive une fois dans la config, avec sa raison
TL-014security hors de toute chaîne bloquante localeMEDIUMscripts.check ou scripts.deep-check qui contient security, ou .husky/pre-commit qui le lance : une faille non corrigeable rend la chaîne rouge en permanence, et une chaîne toujours rouge finit contournée. C'est un diagnostic — la remédiation est TL-021
TL-015deep-check hors du hook pre-commitLOW.husky/pre-commit qui lance deep-check : bun install dépend du réseau, le hook doit rester rapide et hors ligne
TL-016trailingComma: "all"LOW.prettierrc avec "es5" — diffs plus bruyants pour rien
TL-017Knip en fichier dédié avec $schemaLOWConfig inline dans package.json au lieu de knip.json
TL-018ignore Knip justifiésMEDIUMEntrée dans knip.ignore / ignoreDependencies sans commentaire disant pourquoi Knip se trompe
TL-019.editorconfigLOWFichier absent à la racine
TL-020Version du runtime figéeLOWengines absent du package.json (ou pas de .nvmrc / .node-version)
TL-021Mises à jour de dépendances automatiséesMEDIUMNi .github/dependabot.yml ni config Renovate : c'est le canal de remédiation, pas un confort — sans lui security ne produit qu'une liste que personne ne traite. Vérifier aussi le bloc registries si le projet consomme un registre privé, sans quoi les packages internes sont ignorés en silence
TL-022CI de vérificationLOWAucun workflow lançant deep-check. Facultatif en solo avec build à chaque push ; utile pour valider sur un environnement propre

Commandes de Détection

FE-001: Complexité des Hooks

grep -r "useEffect" src/hooks/ --include="*.ts" --include="*.tsx"
grep -r "useState.*useState" src/hooks/ --include="*.ts" --include="*.tsx"

FE-002: Réseau dans Contexts

grep -rE "(fetch|honoClient)" src/contexts/ --include="*.ts" --include="*.tsx"

FE-003: Isolation Services

grep -rE "from '.*/components|from '.*/hooks|from '.*/contexts|from '.*/pages" src/services/ --include="*.ts" --include="*.tsx"

FE-004: honoClient Type Inférence

grep -rE "honoClient\.(get|post|patch|delete|upload)<" src/hooks/ --include="*.ts" --include="*.tsx"

FE-005: res.ok Check

grep -rE "(json\.success|!json\.success|\.success)" src/hooks/ --include="*.ts"

FE-006: Export FromApi Types

grep -rE "export type \w+FromApi" src/hooks/ --include="*.ts"

FE-007: Composants sans appels réseau

grep -rE "(fetch\(|honoClient\.)" src/components/ --include="*.ts" --include="*.tsx"

FE-008: Layouts sans props

grep -rE "export const \w+Layout = \(\{[^}]*\}:" src/layouts/ --include="*.tsx" | grep -v "children"

FE-009: Une page = une route

grep -rE "<Route.*element=\{<\w+Page" src/routes/ --include="*.tsx"

FE-010: Pas de navigation dans les hooks data

grep -rE "(useNavigate|navigate\(|toast\(|toast\.)" src/hooks/data/ --include="*.ts"

FE-011: Pas d'état local dans hooks data

grep -rE "useState\(" src/hooks/data/ --include="*.ts"

FE-012 / FE-013 : frontière services → composants

# Les imports de VALEUR seuls : ce sont eux qui couplent à l'exécution.
grep -rnE "from '[^']*services/" src/components/ src/pages/ src/layouts/ | grep -v ":import type"

# Le compte total, pour situer la part type-only (admissible).
grep -rnE "from '[^']*services/" src/components/ src/pages/ src/layouts/ | wc -l

# FE-013 : le re-export qui rend le grep ci-dessus aveugle.
grep -rnE "^export \{" src/hooks/

⚠️ Rapporter les deux nombres. Un audit qui additionne import type et imports de valeur surestime la dette et fait rater la vraie question : d'où vient le helper, et où devrait-il vivre (cf. frontend.md, « Le discriminant utils/ ou services/ »).

BE-001: Method chaining Hono RPC

# Chercher routes séparées (perte d'inférence)
grep -rE "^\w+\.(get|post|patch|delete)\(" netlify/routes/ --include="*.ts"

BE-002: Validation Zod manquante

grep -rE "\.(post|patch)\(" netlify/routes/ --include="*.ts" | grep -v "zValidator"

BE-003: Champ success dans réponses HTTP

grep -rE "c\.json\(.*success" netlify/routes/ --include="*.ts"

BE-004: Return types explicites

grep -rE "\): Promise<" netlify/routes/ netlify/lib/ --include="*.ts"

TY-003: Duplication Types Frontend

# Chercher des interfaces qui dupliquent le schéma Drizzle dans src/types/
grep -rE "^export interface" src/types/ --include="*.ts"

CD-001: Named exports uniquement

grep -rE "^export default" src/ netlify/ --include="*.ts" --include="*.tsx" | grep -v "/pages/"

CD-002: Pas d'any explicite

grep -rE "(: any|as any|<any>)" src/ netlify/ --include="*.ts" --include="*.tsx"

TL-001 / TL-003 / TL-004 : présence des outils

ls eslint.config.* .prettierrc* knip.json 2>/dev/null
jq -r '[.devDependencies,.dependencies|objects|keys[]]|map(select(test("eslint|prettier|knip")))|sort|join(" ")' package.json

TL-002 : linting TypeScript réellement branché

# La config fait foi, pas la dépendance : le package peut être installé sans être utilisé
grep -E "typescript-eslint|tseslint|@typescript-eslint" eslint.config.*

TL-005 / TL-006 / TL-007 / TL-008 : scripts et hook

jq -r '.scripts | to_entries[] | select(.key|test("^(check|deep-check|lint|typecheck|format|knip|test|security|prepare)$")) | "\(.key): \(.value)"' package.json
ls -la .husky/ 2>/dev/null

TL-014 : security hors des chaînes bloquantes

# Doit être défini, et n'apparaître dans AUCUNE des trois.
jq -r '.scripts | .check, .["deep-check"]' package.json | grep -n security
grep -rn "security" .husky/ 2>/dev/null

Défini mais absent des trois = PASS. Présent dans check, deep-check ou le hook = FAIL.

BE-007 : secret avec valeur de repli

grep -rnE "process\.env\.[A-Z_]+ *(\|\||\?\?) *['\"]" netlify/ src/

⚠️ Trier ce que la commande remonte : un repli sur une URL de développement (process.env.URL ?? 'http://localhost:8888') est légitime ; un repli sur un secret (signature, identifiants, clés) ne l'est jamais.

TL-010 / TL-011 / TL-012 : preset et garde-fou de taille

# Cibler tseslint uniquement : `js.configs.recommended` est attendu dans la config canonique
grep -nE "tseslint\.configs\.[a-zA-Z]+" eslint.config.*
grep -nE "max-lines" eslint.config.*        # présence, seuil, et tout 'off'

CFG-001 à CFG-008 : options du compilateur

jq -r '.compilerOptions | to_entries[] | select(.key|test("strict|noUnused|noImplicit|noUnchecked|exactOptional|noFallthrough|verbatim|noProperty")) | "\(.key)=\(.value)"' tsconfig.json
ls tsconfig*.json          # CFG-008 : plus d'un fichier = configs séparées

TE-001 à TE-007 : tests et couverture

jq -r '[(.devDependencies//{})|keys[]|select(test("vitest|playwright|testing-library|coverage"))]|join(" ")' package.json
jq -r '.scripts|to_entries[]|select(.key|test("^test"))|"\(.key): \(.value)"' package.json
grep -nE "thresholds|coverage" vitest.config.* vite.config.* 2>/dev/null
find . -name "*.test.ts" -o -name "*.test.tsx" | grep -v node_modules | wc -l

TL-013 : désactivations répétées

grep -rhoE "eslint-disable[a-z-]* +[@a-z/-]+" src/ netlify/ | sort | uniq -c | sort -rn | head

Fichiers qui dépassent le seuil

find src/ netlify/ -name "*.ts" -o -name "*.tsx" | xargs wc -l | sort -rn | head -20

Classification des Sévérités

SévéritéCritères
CRITICALRisque sécurité, intégrité des données, bloque fonctionnalité core
HIGHViolation architecturale impactant la maintenabilité
MEDIUMDéviation mineure, dette technique
LOWConvention/style, opportunité d'amélioration

Format de Rapport

Le rapport généré doit inclure :

  1. Résumé — Totaux par sévérité
  2. Violations par catégorie — FE-, BE-, TY-, CD-, CFG-, TE-, TL-* avec fichier, ligne, description
  3. Tâches recommandées — Actions de remédiation priorisées
  4. Couverture des règles — Status PASS/FAIL par règle

Principes

  • Non-destructif : Ne jamais modifier le code source
  • Basé sur preuves : Chaque violation cite fichier et ligne
  • Actionnable : Chaque violation a une action de remédiation claire
  • Priorisé : Focus sur violations à fort impact
  • Déterministe : Même codebase = même rapport

Exceptions Connues

Certains patterns sont intentionnels :

  • Types *FormData, *WithRelations, *Props sont autorisés dans src/types/
  • Les fichiers dans src/pages/ peuvent utiliser export default pour React.lazy()
  • Le binary sync CRDT peut utiliser fetchWithAuth directement (pas TanStack Query)

BE-003: Clarification success interne vs HTTP

Le champ success est interdit dans les réponses HTTP (c.json) mais autorisé en interne entre fonctions comme discriminated union pour la gestion d'erreurs type-safe :

// INTERDIT : success dans la réponse HTTP
return c.json({ success: true, data: user })  // NON!

// AUTORISÉ : success interne (fonction → route)
const result = await authenticate(credentials)
if (!result.success) {
  return c.json({ error: 'Unauthorized' }, 401)
}
return c.json({ data: result.user })  // OK : pas de success dans la réponse

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.