GateGuard — Porte factuelle pré-action
Un crochet PreToolUse qui force Claude à enquêter avant d'éditer. Au lieu d'une auto-évaluation (« es-tu sûr ? »), il exige des faits concrets. Le simple fait d'enquêter crée une prise de conscience que l'auto-évaluation n'a jamais permise.
Quand l'activer
- Lorsque vous travaillez sur une base de code où les modifications de fichiers affectent plusieurs modules
- Sur des projets avec des fichiers de données ayant des schémas ou formats de dates spécifiques
- Dans des équipes où le code généré par IA doit correspondre aux modèles existants
- Dans tout flux de travail où Claude a tendance à deviner plutôt qu'à investiguer
Concept central
L'auto-évaluation des LLM ne fonctionne pas. Demandez « as-tu violé des politiques ? » et la réponse est toujours « non ». Ceci est vérifié expérimentalement.
Mais demander « liste chaque fichier qui importe ce module » force le LLM à lancer Grep et Read. L'investigation elle-même crée un contexte qui change le résultat.
Porte en trois étapes :
1. REFUSER — bloquer la première tentative d'Edit/Write/Bash
2. FORCER — dire au modèle exactement quels faits rassembler
3. AUTORISER — permettre la nouvelle tentative après présentation des faits
Aucun concurrent ne fait les trois. La plupart s'arrêtent au refus.
Preuves
Deux tests A/B indépendants, agents identiques, même tâche :
| Tâche | Avec porte | Sans porte | Écart |
|---|---|---|---|
| Module d'analyse | 8,0/10 | 6,5/10 | +1,5 |
| Validateur webhook | 10,0/10 | 7,0/10 | +3,0 |
| Moyenne | 9,0 | 6,75 | +2,25 |
Les deux agents produisent du code qui s'exécute et passe les tests. La différence réside dans la profondeur de conception.
Types de portes
Porte Edit / MultiEdit (première édition par fichier)
MultiEdit est traité de manière identique — chaque fichier du lot est contrôlé individuellement.
Avant d'éditer {chemin_fichier}, présente ces faits :
1. Liste TOUS les fichiers qui importent/requièrent ce fichier (parcours de l'arborescence — Glob/Grep, ou find/grep via Bash)
2. Liste les fonctions/classes publiques affectées par ce changement
3. Si ce fichier lit/écrit des fichiers de données, montre les noms de champs, la structure
et le format de date (utilise des valeurs anonymisées ou synthétiques, pas de données brutes de production)
4. Cite textuellement l'instruction actuelle de l'utilisateur
Porte Write (première création de fichier)
Avant de créer {chemin_fichier}, présente ces faits :
1. Nomme le(s) fichier(s) et ligne(s) qui appelleront ce nouveau fichier
2. Confirme qu'aucun fichier existant ne sert le même usage (parcours de l'arborescence — Glob/Grep, ou find/grep via Bash)
3. Si ce fichier lit/écrit des fichiers de données, montre les noms de champs, la structure
et le format de date (utilise des valeurs anonymisées ou synthétiques, pas de données brutes de production)
4. Cite textuellement l'instruction actuelle de l'utilisateur
Porte Bash destructive (chaque commande destructive)
Déclenché sur : rm -rf, git reset --hard, git push --force, drop table, etc.
1. Liste tous les fichiers/données que cette commande va modifier ou supprimer
2. Écris une procédure d'annulation en une ligne
3. Cite textuellement l'instruction actuelle de l'utilisateur
Porte Bash courante (une fois par session)
1. La demande actuelle de l'utilisateur en une phrase
2. Ce que cette commande spécifique vérifie ou produit
Lots parallèles et application partielle
La porte du premier contact évalue chaque appel d'outil indépendamment. Lorsque plusieurs éditions d'un fichier non encore touché sont envoyées dans un même lot parallèle, le premier appel est refusé et le refus marque le fichier comme vérifié, si bien que les éditions sœurs de ce lot sont appliquées. Rien n'est annulé : le fichier peut se retrouver avec les éditions sœurs sans celle qui a été refusée.
Le message de refus nomme le fichier et prévient que les éditions sœurs du lot ont pu être appliquées. Traitez-le à la lettre :
- Envoyez les modifications dépendantes vers un fichier pas encore touché de manière séquentielle, pas dans un lot parallèle. Une définition et sa première utilisation, ou une importation et son site d'appel, ne doivent pas voyager dans le même lot.
- Après un refus de premier contact, présentez les faits, réessayez l'édition refusée, et relisez le fichier avant de construire sur autre chose du lot.
Un verrou au niveau du lot n'est pas possible : les crochets voient les appels d'outil un par un, donc la porte ne peut pas savoir quels appels sont arrivés ensemble.
Démarrage rapide
Option A : Utiliser le crochet ECC (installation zéro)
Le crochet dans scripts/hooks/gateguard-fact-force.js est inclus dans ce plugin. Activez-le via hooks.json.
Si GateGuard bloque des travaux de configuration ou de réparation, démarrez la session avec
ECC_GATEGUARD=off. Pour un contrôle au niveau du crochet, continuez à utiliser
ECC_DISABLED_HOOKS avec l'ID du crochet GateGuard.
Dans les sessions longues, seuls les premiers GATEGUARD_FACT_FORCE_FULL_DENIALS
refus factuels (par défaut 3) émettent le bloc complet de quatre faits ; les
refus ultérieurs sont condensés en une seule ligne portant le numéro de refus, afin
que des blocs presque identiques ne puissent pas s'accumuler dans la fenêtre de contexte
et amplifier les boucles de répétition du modèle (#2142). Réessayer le même fichier ou
la même commande après avoir présenté les faits ne déclenche plus la porte.
Contrôles gradués
ECC_GATEGUARD=off (ou GATEGUARD_DISABLED=1) désactive complètement la porte.
Les variables de ce tableau ne le font pas — chacune réduit un comportement tandis
que les vérifications Bash destructives porteuses continuent de s'exécuter :
| Variable | Défaut | Effet |
|---|---|---|
GATEGUARD_BASH_ROUTINE_DISABLED | non défini (porte active) | Désactive uniquement la porte Bash courante. La porte Bash destructive (rm -rf, git reset --hard, drop table, dd if=, …) n'est pas affectée. |
GATEGUARD_EXEMPT_GLOBS | non défini (aucune exemption) | Globs séparés par des virgules ; une cible Edit/Write/MultiEdit correspondante saute l'imposition de faits au premier contact. Destiné aux arbres à faible valeur d'importation (tests, artefacts générés, répertoires temporaires) où « qui importe ceci / quel schéma » n'apporte aucun signal. |
GATEGUARD_FACT_FORCE_FULL_DENIALS | 3 | Combien de refus émettent le bloc complet de quatre faits avant que les suivants soient condensés en une seule ligne. 0 condense dès le tout premier refus. |
GATEGUARD_BASH_EXTRA_DESTRUCTIVE | non défini | Motifs destructifs supplémentaires, sous forme de source regex, ajoutés à l'ensemble intégré. Une regex malformée est traitée comme non définie (les motifs intégrés s'appliquent toujours) et est consignée une fois sur stderr. |
GATEGUARD_STATE_DIR | ~/.gateguard | Où l'état de la porte par session est conservé. Si l'état ne peut pas être persisté, la porte autorise l'opération au lieu de boucler et nomme cette variable dans l'avertissement. |
GATEGUARD_BASH_ROUTINE_DISABLED accepte 1, true, on, enabled,
enable, ou yes (insensible à la casse et aux espaces) ; toute autre valeur
laisse la porte active.
Désactiver complètement la porte
| Variable | Effet |
|---|---|
ECC_GATEGUARD=off | Désactive GateGuard pour la session. Accepte 0, false, off, disabled, ou disable. |
GATEGUARD_DISABLED=1 | Même effet. Ne reconnaît que 1 — les orthographes ci-dessus ne s'appliquent pas ici. |
Pour un contrôle au niveau du crochet, continuez à utiliser ECC_DISABLED_HOOKS avec l'ID du crochet GateGuard.
Sémantique des globs pour GATEGUARD_EXEMPT_GLOBS
Les motifs correspondent au chemin cible complet relatif au projet. La racine du projet est
CLAUDE_PROJECT_DIR, avec repli sur le cwd de la charge utile du crochet, puis le répertoire
de travail du processus du crochet. Les globs relatifs n'exemptent jamais de cibles en dehors de
cette racine. Les globs absolus explicites correspondent au chemin cible absolu complet et peuvent
délibérément exempter des chemins en dehors du projet.
Les motifs et chemins utilisent des séparateurs / et une correspondance en minuscules. * correspond
à l'intérieur d'un segment, ** à travers les segments, et ? à un caractère non séparateur.
**/ inclut zéro répertoire, donc **/tests/** correspond aussi à tests/foo.js.
Les motifs malformés sont ignorés sans accorder d'exemption.
Depuis la 2.2.1, services/** ne couvre que l'arborescence racine des services du projet, et
*.md ne couvre que ses fichiers Markdown racine. Utilisez **/*.md pour tous les fichiers Markdown
à l'intérieur du projet. Les exemptions non ancrées existantes peuvent nécessiter un ajustement :
{
"env": {
"GATEGUARD_BASH_ROUTINE_DISABLED": "1",
"GATEGUARD_EXEMPT_GLOBS": "**/tests/**,tests/**,**/*.test.*,**/docs/**,**/dist/**"
}
}
Option B : Paquet complet avec configuration
pip install gateguard-ai
gateguard init
Ceci ajoute .gateguard.yml pour la configuration par projet (messages personnalisés, chemins ignorés, activation/désactivation des portes).
Anti-patrons
- N'utilisez pas l'auto-évaluation à la place. « Es-tu sûr ? » obtient toujours « oui ». Ceci est vérifié expérimentalement.
- Ne sautez pas la vérification du schéma de données. Les deux agents des tests A/B ont supposé des dates ISO-8601 alors que les vraies données utilisaient
%Y/%m/%d %H:%M. Vérifier la structure des données (avec des valeurs anonymisées) évite toute cette classe de bugs. - Ne contrôlez pas chaque commande Bash. La porte Bash courante agit une fois par session. La porte Bash destructive agit à chaque fois. Cet équilibre évite les ralentissements tout en capturant les risques réels.
Bonnes pratiques
- Laissez la porte se déclencher naturellement. N'essayez pas de répondre aux questions de la porte à l'avance — c'est l'investigation elle-même qui améliore la qualité.
- Personnalisez les messages de la porte pour votre domaine. Si votre projet a des conventions spécifiques, ajoutez-les aux invites de la porte.
- Utilisez
.gateguard.ymlpour ignorer des chemins comme.venv/,node_modules/,.git/.
Compétences connexes
safety-guard— Vérifications de sécurité à l'exécution (complémentaires, pas de chevauchement)code-reviewer— Révision post-édition (GateGuard est une investigation pré-édition)