Toutes les ressources
Note #001

Des règles aux hooks : arrêtez de demander à l’IA, imposez

Les instructions disent à un agent de code IA ce qu’il ne doit pas faire. Les hooks garantissent qu’il ne le peut pas. Quoi mettre où, un exemple minimal, et pourquoi tester le garde-fou.

Par Tenovates6 min de lecture

Rédigé en septembre 2026 · Claude Code

Disponible en anglais

Le problème

Toute équipe qui travaille avec un agent de code IA finit par écrire une liste de règles. Ne jamais forcer un push. Ne jamais modifier le fichier d’environnement de production. Toujours lancer les tests avant de dire que c’est fini. L’endroit naturel pour les écrire, c’est le fichier d’instructions que l’agent lit au début de chaque session — CLAUDE.md pour Claude Code, AGENTS.md pour d’autres.

Ça marche. La plupart du temps.

Et « la plupart du temps », c’est justement le problème. Une règle respectée quatre-vingt-dix-neuf fois sur cent finira par être enfreinte le jour où elle compte : une longue session, un contexte résumé, une tâche formulée de telle sorte que la commande interdite ressemble à l’étape évidente. Personne n’a été négligent. La règle n’a simplement jamais été imposée — seulement demandée.

Pourquoi les instructions ne suffisent pas

Un fichier d’instructions, c’est du contexte. Le modèle le lit, le pèse face à tout ce qu’il a lu d’autre, et décide. C’est exactement ce qu’on veut pour des consignes : conventions, préférences, organisation du projet. Ce n’est pas ce qu’on veut pour la poignée d’actions qui ne doivent jamais se produire, quel que soit le contexte.

Pour celles-là, il faut quelque chose qui s’exécute en dehors du jugement du modèle : une vérification qui tourne à chaque fois, et dont la réponse ne se discute pas.

Claude Code en propose deux :

  • Les permissions — des règles dans .claude/settings.json qui font qu’une commande demande d’abord votre accord, ou qui l’interdisent.
  • Les hooks — de petits programmes lancés à des moments précis : avant l’utilisation d’un outil, après son exécution, quand l’agent s’apprête à s’arrêter. Un hook peut bloquer l’action et dire à l’agent pourquoi.

Quoi mettre où

Type de règle Où la placer Pourquoi
« On utilise les tokens Tailwind, pas des couleurs brutes » Instructions Jugement, style, contexte
« Un push passe par un humain » Permission : ask Légitime, mais pas en solo
« Ne jamais réinitialiser la base » Hook (ou permission : deny) Ne doit jamais arriver
« Jamais de --force dans un push » Hook Un motif qu’une permission ne sait pas exprimer
« Les tests passent avant “terminé” » Hook d’arrêt Tourne à chaque fois, pas quand on y pense

Un bon test : si enfreindre la règle une seule fois vous coûtait une journée, elle ne doit pas vivre uniquement dans les instructions.

Un exemple minimal

Les permissions couvrent les cas simples. Les commandes qui commencent par un préfixe donné demanderont une confirmation :

{
  "permissions": {
    "ask": ["Bash(git push:*)", "Bash(npm install:*)"]
  }
}

Un hook couvre ce qu’un préfixe ne sait pas faire — une option qui peut apparaître n’importe où dans la commande. On l’enregistre pour l’outil Bash :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "node .claude/hooks/guard.mjs" }]
      }
    ]
  }
}

Le hook reçoit l’appel d’outil en JSON sur son entrée standard. Le code de sortie 2 bloque l’appel, et ce qu’il écrit sur la sortie d’erreur est montré à l’agent comme explication :

// .claude/hooks/guard.mjs
let input = "";
for await (const chunk of process.stdin) input += chunk;
const command = JSON.parse(input).tool_input?.command ?? "";

const rules = [
  [/\bgit\s+push\b.*\s(--force|-f)\b/, "Force-push is not allowed. Push normally or ask."],
  [/\s--no-verify\b/, "Don't skip the checks: fix what they report."],
];

for (const [pattern, reason] of rules) {
  if (pattern.test(command)) {
    console.error(`Blocked: ${reason}`);
    process.exit(2);
  }
}
process.exit(0);

L’agent ne se contente pas d’échouer : il lit la raison et s’adapte — en général en faisant ce qu’il fallait faire.

La même idée fonctionne en fin de tâche. Un hook sur l’événement Stop peut lancer vos tests et refuser que l’agent termine tant qu’ils échouent. Prévoyez une porte de sortie : l’entrée du hook indique quand l’agent réessaie déjà après un blocage (stop_hook_active). Bloquez une fois, et à la deuxième tentative laissez-le s’arrêter, mais en l’obligeant à dire que le travail n’est pas terminé — sinon un échec impossible à corriger devient une boucle infinie.

Testez le garde-fou, pas seulement ce qu’il protège

Un hook est du code, et le code a des bugs. Le type le plus dangereux, c’est un garde-fou qui échoue en laissant passer : il s’exécute, ne dit rien, et l’action interdite passe. Vous ne le remarquerez pas, car un garde-fou silencieux ressemble exactement à un garde-fou qui n’a rien à signaler.

Deux pièges à connaître :

  • Les chemins ont plusieurs formes. Sous Windows, un même fichier peut arriver à votre hook sous la forme d’un chemin avec lettre de lecteur et barres obliques inverses, ou sous forme Unix, /c/project/.env. Une vérification qui compare avec une seule forme rate l’autre en silence. Vérifiez ce qui compte vraiment — le nom du fichier, le motif du dossier — plutôt qu’un chemin relatif à une racine qui peut s’écrire autrement.
  • Les outils résument. git status --porcelain affiche un dossier tout neuf sur une seule ligne, ?? dossier/, sans les fichiers qu’il contient. Un hook qui cherche des fichiers de code modifiés n’en verra aucun — à moins de les demander avec --untracked-files=all.

La solution est la même dans les deux cas : testez le hook comme n’importe quel programme. Donnez-lui le JSON qu’il recevra, et vérifiez le code de sortie — pour les commandes qui doivent être bloquées et pour celles qui doivent passer :

echo '{"tool_input":{"command":"git push --force origin main"}}' | node .claude/hooks/guard.mjs
echo $?   # attendu : 2

echo '{"tool_input":{"command":"git push origin main"}}' | node .claude/hooks/guard.mjs
echo $?   # attendu : 0

Le deuxième cas compte autant que le premier. Un garde-fou qui bloque tout sera désactivé par la première personne agacée qui le rencontre.

À retenir

Les instructions guident un agent ; les hooks et les permissions l’encadrent. Gardez le fichier d’instructions pour le jugement et les conventions. Placez les quelques règles qui ne doivent jamais être enfreintes là où l’agent ne peut pas les contourner — et testez ces garde-fous avec le même soin que le code qu’ils protègent.

Check-list

Passer une règle des instructions à une garantie
1. L’enfreindre une fois coûterait-il une journée ou plus ?      → l’imposer
2. Est-ce un préfixe de commande (push, install, deploy) ?        → permission : ask / deny
3. Est-ce un motif n’importe où dans une commande ou un chemin ?  → hook PreToolUse
4. Est-ce « doit être vrai avant de terminer » ?                  → hook Stop, avec limite de tentatives
5. Tester chaque garde-fou avec des entrées à bloquer ET des entrées à laisser passer
6. Garder aussi la règle dans les instructions — expliquée — pour que l’agent sache pourquoi