From codebloom
PERMANENT sur tout code source. Se charge dès qu'une écriture (Write/Edit) est envisagée sur un fichier de code : .js .ts .jsx .tsx .vue .svelte .astro .py .php .go .rs .java .kt .swift .rb .cs .c .cpp .dart .ex .elm .sql .sh .lua, ou tout fichier de logique applicative. Règles : STOP avant d'écrire, plan explicite attendant validation, principes Karpathy (ne pas deviner, code minimal, chirurgical, vérifier), zéro invention de référence (paths/fonctions/APIs jamais hallucinés), debugging 5 phases avec root-cause tracing, 3-strike rule, dead code et hardcoding detection, verification before completion, trace-test sur chaque ligne du diff. Ne se charge PAS quand : modifications limitées à .md, .json de config, TODO.md, CHANGELOG.md, TIME.md, ou contenu purement documentaire.
How this skill is triggered — by the user, by Claude, or both
Slash command
/codebloom:code-qualityThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Ce skill s'active automatiquement quand du code est écrit ou modifié, pour maintenir la qualité même sans commande explicite.
Ce skill s'active automatiquement quand du code est écrit ou modifié, pour maintenir la qualité même sans commande explicite.
Tradeoff : ce garde-fou biaise vers la prudence et la vérification. Pour les tâches triviales (typo évidente, one-liner, renommage d'une variable locale), garder du jugement — pas besoin d'un plan formel ni d'une boucle de vérif complète. La règle STOP reste valable : on ne modifie un fichier qu'après instruction explicite ou feu vert.
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. Aucun Write, aucun Edit, aucune modification — même si la réponse semble évidente à implémenter. Expliquer la faisabilité, les options, les tradeoffs. Attendre une instruction explicite ou un feu vert avant de toucher un fichier. Une question ne se transforme jamais en code sans passage par la case validation.
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.
Une instruction vague mène à un code vague. Avant de coder, reformuler en objectif mesurable — une condition qu'un test ou une commande peut prouver comme satisfaite.
| Au lieu de... | Reformuler en... |
|---|---|
| "Ajoute de la validation" | "Écrire des tests pour les entrées invalides, puis les faire passer" |
| "Corrige le bug" | "Écrire un test qui reproduit le bug, puis le faire passer" |
| "Refactore X" | "S'assurer que les tests passent avant ET après" |
| "Rends ça plus rapide" | "Mesurer le temps actuel → définir une cible → vérifier l'écart" |
| "Nettoie le code" | "Lister les éléments morts précisément → supprimer ceux qui tracent à l'intention" |
| "Améliore l'UX" | "Nommer le parcours utilisateur cible → décrire l'état attendu après l'action" |
Critères de succès forts → la boucle peut s'auto-vérifier. Critères faibles ("fais marcher ça") → clarifications en cascade.
step → verifyPour une tâche qui se décompose en plusieurs étapes, utiliser le format couplé action / vérification — chaque étape doit porter sa propre preuve de réussite :
1. [Action atomique] → vérifier : [commande, test, ou observation qui prouve le résultat]
2. [Action atomique] → vérifier : [preuve]
3. [Action atomique] → vérifier : [preuve]
Exemple :
1. Ajouter middleware rate-limit en mémoire → vérifier : curl × 11 → 10 × 200, 1 × 429
2. Extraire en middleware global → vérifier : test hit /users et /posts, les deux limités
3. Brancher Redis pour multi-instance → vérifier : test 2 instances partagent le compteur
Chaque étape est indépendamment déployable et vérifiable. Si une étape échoue sa vérification → stop, on ne passe pas à la suivante.
| 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 |
Exemples avant/après concrets pour chaque principe : voir
reference/karpathy-examples.md. À consulter quand tu doutes ("est-ce que je suis en train d'overcomplexifier / de déborder / de supposer ?").
Formulation reprise de la doc Anthropic (prompting best practices — Overeagerness). Mêmes principes que Karpathy, en wording exécutable :
Avoid over-engineering. Only make changes that are directly requested or clearly necessary. Keep solutions simple and focused:
- Scope — Don't add features, refactor code, or make "improvements" beyond what was asked. A bug fix doesn't need surrounding code cleaned up. A simple feature doesn't need extra configurability.
- Documentation — Don't add docstrings, comments, or type annotations to code you didn't change. Only add comments where the logic isn't self-evident.
- Defensive coding — Don't add error handling, fallbacks, or validation for scenarios that can't happen. Trust internal code and framework guarantees. Only validate at system boundaries (user input, external APIs).
- Abstractions — Don't create helpers, utilities, or abstractions for one-time operations. Don't design for hypothetical future requirements. The right amount of complexity is the minimum needed for the current task.
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.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.
Distinction capitale :
| Origine | Action |
|---|---|
| Orphelins créés par TES changements (import inutilisé après suppression d'un appel, variable qui ne sert plus, fonction rendue obsolète par ton refactor) | Supprime immédiatement — tu dois laisser ta propre zone propre |
| Dead code pré-existant (fonction morte que tu croises, import orphelin qui existait déjà, variable commentée, fichier abandonné) | Signale, ne supprime pas — ce n'est pas ton périmètre. Mentionne-le à l'utilisateur, qu'il décide |
Pourquoi cette distinction : supprimer du dead code pré-existant pendant une autre tâche = drive-by deletion. Ça pollue le diff, masque l'intention du commit, et peut casser des usages cachés (reflection, config externe, plugins) que tu n'as pas vus. Ne supprime jamais ce que ton diff n'a pas rendu orphelin, sauf demande explicite.
| 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. Root-cause tracing : tracer en arrière dans la call stack jusqu'à l'origine ("qu'est-ce qui a appelé cette fonction ? et avant ?"). Ne jamais fixer le symptôme sans remonter à la source.
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. Une seule variable à la fois — ne jamais tenter plusieurs corrections simultanées. 3-strike rule : 3 hypothèses échouées → escalader à l'utilisateur, stop guessing.
Corriger — Fix la cause racine, pas les symptômes. Defense-in-depth : valider aux 4 couches (entrée → logique métier → env → instrumentation debug) pour rendre le bug structurellement impossible. 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].
Remplacer les sleep/délais arbitraires par du polling sur condition réelle. Un sleep(2000) est un aveu de ne pas savoir quand la condition est remplie — il est soit trop long (lent) soit trop court (flaky).
// MAL — délai arbitraire
await sleep(2000)
expect(result).toBeDefined()
// BIEN — polling sur condition
await waitFor(() => result !== undefined, { timeout: 5000, interval: 50 })
expect(result).toBeDefined()
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.
Formulation reprise de la doc Anthropic (prompting best practices — Avoid focusing on passing tests). Le test est là pour vérifier la solution, pas pour la définir :
Please write a high-quality, general-purpose solution using the standard tools available. Do not create helper scripts or workarounds to accomplish the task more efficiently. Implement a solution that works correctly for all valid inputs, not just the test cases. Do not hard-code values or create solutions that only work for specific test inputs. Instead, implement the actual logic that solves the problem generally.
Focus on understanding the problem requirements and implementing the correct algorithm. Tests are there to verify correctness, not to define the solution. Provide a principled implementation that follows best practices and software design principles.
If the task is unreasonable or infeasible, or if any of the tests are incorrect, please inform me rather than working around them. The solution should be robust, maintainable, and extendable.
Concrètement : si un test ne passe pas, corriger la logique, pas le test ; si un test est faux, le signaler au lieu de le contourner.
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) |
Règle : aucune affirmation de travail terminé sans preuve fraîche.
Avant de dire "c'est fait", "ça marche", "les tests passent" :
| Claim | Preuve requise | Insuffisant |
|---|---|---|
| Tests passent | Sortie test : 0 failures | Run précédent, "devrait passer" |
| Build OK | Build : exit 0 | Linter passe (≠ build) |
| Bug corrigé | Symptôme original reproduit → passe | "Code modifié" |
| Requirements OK | Checklist point par point | "Tests passent" seul |
Red flags linguistiques — ces mots trahissent un claim non vérifié :
Ces excuses sont toutes invalides pour contourner les règles ci-dessus :
| Excuse | Réalité |
|---|---|
| "C'est trop simple pour tester" | Le code simple casse aussi. Le test prend 10 secondes. |
| "Je testerai après" | Un test écrit après passe immédiatement — il ne prouve rien |
| "J'ai testé manuellement" | Non reproductible, non automatisable, oublié demain |
| "C'est juste du refactoring" | Le refactoring casse des choses — c'est pour ça qu'on teste avant ET après |
| "Ça marchait avant mon changement" | Ton changement a peut-être cassé autre chose — vérifie |
| "Le fix est évident" | Les bugs les plus sournois ont des fix "évidents" qui masquent la vraie cause |
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.
Si 3+ tentatives de fix échouent sur le même composant → ce n'est plus un bug isolé, c'est un problème d'architecture. Ne pas continuer à patcher. Signaler à l'utilisateur : "Ce composant résiste aux corrections ciblées — il a probablement besoin d'être restructuré. Voici les 3 tentatives et pourquoi elles ont échoué."
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) |
Règle : chaque ligne qui apparaît dans un diff doit tracer directement à la demande utilisateur. Si une ligne ne peut pas s'expliquer par "ceci résout X, que l'utilisateur a explicitement demandé" → elle n'a rien à faire dans le diff.
Avant de finaliser un changement, relire le diff et poser sur chaque ligne modifiée :
"Pourquoi cette ligne est-elle ici ? L'utilisateur a-t-il demandé ça ?"
Si la réponse est "non mais c'était mieux", "au cas où", "tant qu'on y est", "pour la cohérence", ou "par habitude" → revert cette ligne. Ce sont des drive-by changes et ils sont interdits.
Cas typiques à revert :
' ↔ "), d'indentation, de formattingRègle absolue : ne jamais inventer un chemin de fichier, un nom de fonction, une API, un hook, un endpoint, un champ, un numéro de ligne, ou une version. Tout ce qui est cité dans une réponse, un plan, un diff, un commit, ou un rapport doit provenir d'une vérification réelle via outil (Read, Grep, Glob, Bash).
Sample prompt Anthropic (prompting best practices — Minimizing hallucinations) :
Never speculate about code you have not opened. If the user references a specific file, you MUST read the file before answering. Make sure to investigate and read relevant files BEFORE answering questions about the codebase. Never make any claims about code before investigating unless you are certain of the correct answer — give grounded and hallucination-free answers.
Pourquoi : les systèmes et humains en aval cassent sur les valeurs hallucinées. Un path inventé envoie l'utilisateur fouiller un fichier qui n'existe pas. Un nom de hook WordPress inventé compile mais ne se déclenche jamais. Une signature d'API hallucinée génère du code qui plante au runtime. Les LLMs sont brillants pour générer du plausible — c'est précisément ce qui rend l'hallucination dangereuse en code.
node_modules/vendor--help ou doc officielleCes formulations trahissent souvent une invention :
src/utils/..."..._filter pour ça"Si tu te surprends à écrire une de ces phrases → stop, vérifie avec un outil avant de continuer.
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.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub vendeesign/codebloom --plugin codebloom