Garde-fou qualité sur toute création/modification de fichiers. STOP → plan → validation → code. Principes Karpathy, debugging méthodique, code smells, conventions CLAUDE.md.
From codebloomnpx claudepluginhub vendeesign/codebloom --plugin codebloomThis skill uses the workspace's default tool permissions.
edge-cases-checklist.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Ce skill s'active automatiquement quand du code est écrit ou modifié, pour maintenir la qualité même sans commande explicite.
RÈGLE ABSOLUE : Ne PAS appeler Write, Edit ou créer/modifier de fichier tant que l'utilisateur n'a pas explicitement validé le plan.
Cette règle s'applique TOUJOURS — avec ou sans commande codebloom active. Aucune exception.
Question ("c'est possible ?", "on pourrait ?", "comment faire ?") → Répondre uniquement avec du texte. Expliquer la faisabilité, les options, les tradeoffs. Ne proposer aucune modification de fichier. Attendre que l'utilisateur demande d'implémenter.
Instruction ("implémente X", "ajoute Y", "corrige Z") → Présenter le plan structuré, puis STOP :
**Plan :**
[1-3 phrases sur l'approche et le pourquoi]
**Fichiers :** [liste des fichiers touchés]
**Changements :** [action verb-first par fichier — "Ajouter...", "Modifier...", "Supprimer..."]
**Hors scope :** [ce qu'on ne touche PAS, si pertinent]
On y va ?
Validation reçue ("oui", "go", "ok", "on y va", "fais-le") → Implémenter.
Modifier un fichier sans validation part potentiellement dans la mauvaise direction — corriger après coûte plus cher que valider avant. L'utilisateur doit garder le contrôle à chaque étape.
Le plan d'abord, la validation ensuite, le code après.
| Principe | En pratique |
|---|---|
| Ne pas deviner | Ambigu → demande. Deux approches possibles → montre les tradeoffs |
| Code minimal | Minimum viable. Pas d'abstractions prématurées. 10 lignes > 100 lignes si même résultat |
| Chirurgical | Ne touche que les fichiers concernés. Pas de refactoring opportuniste |
| Vérifier | Build/test après chaque changement significatif |
console.log, TODO ou code commentéLes valeurs magiques rendent le code fragile et incohérent. Signaler systématiquement :
| Hardcodé | Mieux |
|---|---|
color: #3B82F6 | Variable/token (--color-primary, colors.primary) |
font-size: 14px | Token de typo (--text-sm, fontSize.sm) |
padding: 24px | Token de spacing (--space-6, spacing.lg) |
width: 1200px | Breakpoint nommé (--breakpoint-lg, screens.xl) |
z-index: 9999 | Échelle définie (--z-modal, zIndex.modal) |
timeout: 3000 | Constante nommée (API_TIMEOUT_MS) |
maxRetries: 3 | Constante nommée (MAX_RETRIES) |
"pending" / "active" | Enum ou constante (Status.PENDING, STATUS_ACTIVE) |
http://localhost:3000 | Variable d'env (process.env.API_URL) |
"sk-..." / "Bearer ..." | Variable d'env — jamais dans le code source |
JavaScript / TypeScript :
if (status === 3)) → constante nomméeprocess.env.X sans fallback ni validation → centraliser dans un config objectCSS / Tailwind :
#3B82F6 vs var(--color-primary))w-[347px], mt-[13px]) → token de spacingz-index arbitraire → échelle fixePython :
class Status(Enum))magic numbers dans la logique métier → constantes nommées en UPPER_CASEpathlib + configPHP :
include/require hardcodés → constantes ou autoloadDESIGN_SYSTEM.md) si existant — garantit la cohérence visuelle et facilite les changements globaux0, 1, "", true/false) ou usages uniques clairement contextuelsLe code mort alourdit le projet, confond les développeurs et masque les vrais problèmes. Le signaler systématiquement.
| Signal | Détection |
|---|---|
| Imports inutilisés | Module importé mais jamais référencé dans le fichier |
| Fonctions non appelées | Définies mais aucune référence dans le projet (grep le nom) |
| Variables assignées jamais lues | const x = ... sans aucune lecture de x |
| Branches impossibles | if (false), if (condition) où condition est toujours fausse |
| Code commenté | Blocs de code en commentaire — git garde l'historique |
| Fichiers orphelins | Fichier non importé/requis nulle part dans le projet |
| Feature flags périmés | Flag toujours true ou false en prod depuis longtemps |
| Dépendances fantômes | Package dans package.json/requirements.txt mais jamais importé |
| TODO/FIXME anciens | Marqueurs de plus de 3 mois sans activité |
| Routes/endpoints morts | Endpoint défini mais jamais appelé côté client |
JavaScript / TypeScript :
eslint avec no-unused-vars, no-unused-importstsc --noUnusedLocals --noUnusedParametersgrep -r "functionName" --include="*.ts"npx depcheck pour trouver les deps inutiliséesPython :
vulture pour détecter le code mortpylint avec unused-import, unused-variablepip-extra-reqs pour les deps fantômesPHP :
phpstan détecte les variables et imports inutiliséscomposer why package-name pour vérifier si une dep est utiliséeCSS :
purgecss pour identifier les classes mortesSignaler quand le code devient inutilement complexe :
| Signal | Mieux |
|---|---|
| Fonction > 5 paramètres | Objet config ou découpage |
| Nesting > 3 niveaux (if/for/if) | Early returns, extraction de fonctions |
| Fichier > 300 lignes | Découper par responsabilité |
| Switch/if-else > 5 branches | Table de lookup, map, ou polymorphisme |
| Booléen en paramètre | Probablement 2 fonctions distinctes |
| Fonction > 50 lignes | Extraire des sous-fonctions nommées |
Ne pas signaler la complexité nécessaire (algorithme intrinsèquement complexe). Signaler la complexité accidentelle (mauvaise structure).
Quand un bug est signalé, ne pas deviner la cause — investiguer méthodiquement.
Règle absolue : PAS DE FIX SANS INVESTIGATION DE LA CAUSE RACINE.
Collecter les preuves — Erreurs, stack traces, logs, git history récent, conditions exactes de reproduction. Collecter AVANT d'émettre une hypothèse.
Pattern matching — Classer le bug par catégorie connue :
| Pattern | Symptôme typique |
|---|---|
| Race condition | Marche parfois, échoue de façon non déterministe |
| Null propagation | Crash sur .property d'un objet qui devrait exister |
| State corruption | L'UI affiche un état impossible |
| Config drift | Marche en local, échoue en staging/prod |
| Stale cache | Les changements ne prennent pas effet |
| Integration failure | Fonctionne en isolation, échoue connecté |
Tester l'hypothèse — Confirmer avec des preuves AVANT de coder. Logging temporaire, assertions aux points suspects. 3-strike rule : 3 hypothèses échouées → escalader à l'utilisateur, stop guessing.
Corriger — Fix la cause racine, pas les symptômes. Correction minimale, pas de refactoring opportuniste. Max 5 fichiers touchés, sinon demander approval.
Vérifier — Écrire un test qui reproduit le bug AVANT le fix (RED), puis vérifier qu'il passe après (GREEN). Deux commits : test: reproduce [bug] puis fix: [bug].
Si le bug semble lié à un pattern connu → recherche web (docs, GitHub Issues) pour confirmer le fix recommandé. Pas sûr → le dire, proposer des hypothèses avec niveau de confiance.
Signaler quand le code présente des problèmes structurels :
| Smell | Symptôme | Mieux |
|---|---|---|
| Feature envy | Fonction qui utilise plus les données d'un autre module que les siennes | Déplacer la logique dans le bon module |
| God object | Classe/module qui fait tout | Découper par responsabilité |
| Primitive obsession | Strings/numbers là où un type/enum clarifierait | Type dédié ou enum |
| Long parameter list | > 4 paramètres | Objet config |
| Données mutables partagées | État mutable accessible par plusieurs modules | Immutabilité ou encapsulation |
| Blocs copiés-collés | Logique dupliquée en 3+ endroits | Extraire une fonction |
| Code commenté | Blocs de code en commentaire | Supprimer (git garde l'historique) |
Après chaque implémentation significative, lancer en parallèle en background :
reviewer — review qualité, sécurité, footprinttester — générer et exécuter les testsSi le code touche auth/secrets/inputs/crypto → lancer aussi security-auditor.
Avant d'implémenter une lib, un pattern ou une API inconnue → lancer researcher et attendre son résultat.
Ne pas attendre que l'utilisateur demande — les agents doivent se lancer automatiquement.
Quand un build échoue, un test casse, ou une approche ne marche pas :
| Tentative | Action |
|---|---|
| Strike 1 | Analyser l'erreur, appliquer un fix ciblé |
| Strike 2 | Changer d'approche — la première ne marche pas |
| Strike 3 | Remettre en question les hypothèses de départ, rechercher plus largement |
| Après 3 strikes | STOP — escalader à l'utilisateur avec le détail des 3 tentatives et les erreurs exactes |
Ne jamais boucler sur la même erreur. Ne jamais retry sans changer quelque chose. Chaque tentative doit être différente de la précédente.
Les erreurs mal gérées deviennent des bugs silencieux — l'application échoue sans trace, rendant le debug impossible. Signaler systématiquement :
// MAL — catch vide (erreur silencieuse)
try { await api.call() } catch (e) {}
// MAL — message générique inutile
catch (e) { throw new Error("Something went wrong") }
// MAL — log sans contexte
catch (e) { console.log(e) }
// BIEN — gestion explicite avec contexte
try {
await api.call()
} catch (error) {
logger.error('API call failed', { endpoint, userId, error: error.message })
throw new AppError('SERVICE_UNAVAILABLE', 'External service is down')
}
throw new Error("fail") — permet de filtrer et traiter par typePour l'aspect sécurité (ne pas exposer stack traces au client), voir la skill
security.
| Anti-pattern | Mieux |
|---|---|
any partout | Types explicites ou unknown + narrowing |
== null vs === null | Toujours === (sauf check null/undefined intentionnel) |
| Callback hell | async/await |
var | const par défaut, let si réassignation |
for (let i = 0; ...) sur un array | .map(), .filter(), .reduce() |
| Import de la lib entière | Import destructuré (import { get } from 'lodash') |
console.log pour debug | Supprimer avant commit |
| Anti-pattern | Mieux |
|---|---|
except: (bare except) | except SpecificError: |
Mutable default args (def f(x=[])) | def f(x=None): x = x or [] |
import * | Imports explicites |
| String concatenation en boucle | f-strings ou join() |
| Pas de type hints | Type hints sur les signatures publiques |
| Anti-pattern | Mieux |
|---|---|
@ (error suppression) | Gestion explicite |
extract() | Accès explicite aux clés |
$$var (variables dynamiques) | Array associatif |
echo dans les classes | Return + template |
| Pas de strict types | declare(strict_types=1) |
Trois lignes similaires valent mieux qu'une abstraction prématurée. Le bon niveau de complexité, c'est le minimum nécessaire pour la tâche en cours.