GateGuard — Puerta de Verificación Fáctica Pre-Acción
Un hook PreToolUse que obliga a Claude a investigar antes de editar. En lugar de una autoevaluación ("¿estás seguro?"), exige hechos concretos. El acto de investigar crea conciencia que la autoevaluación nunca logró.
Cuándo activarlo
- Trabajando en cualquier codebase donde las ediciones de archivos afectan a múltiples módulos
- Proyectos con archivos de datos que tienen esquemas o formatos de fecha específicos
- Equipos donde el código generado por IA debe coincidir con patrones existentes
- Cualquier flujo de trabajo donde Claude tiende a adivinar en lugar de investigar
Concepto central
La autoevaluación de LLM no funciona. Preguntar "¿violaste alguna política?" y la respuesta siempre es "no." Esto está verificado experimentalmente.
Pero preguntar "enumera todos los archivos que importan este módulo" obliga al LLM a ejecutar Grep y Read. La investigación en sí misma crea contexto que cambia el resultado.
Puerta de tres etapas:
1. DENEGAR — bloquear el primer intento de Editar/Escribir/Bash
2. FORZAR — indicar al modelo exactamente qué hechos recopilar
3. PERMITIR — permitir reintento después de presentar los hechos
Ningún competidor hace las tres. La mayoría se detiene en denegar.
Evidencia
Dos pruebas A/B independientes, agentes idénticos, misma tarea:
| Tarea | Con puerta | Sin puerta | Diferencia |
|---|---|---|---|
| Módulo de analíticas | 8.0/10 | 6.5/10 | +1.5 |
| Validador de webhook | 10.0/10 | 7.0/10 | +3.0 |
| Promedio | 9.0 | 6.75 | +2.25 |
Ambos agentes producen código que se ejecuta y pasa las pruebas. La diferencia está en la profundidad del diseño.
Tipos de puerta
Puerta de Editar / MultiEdit (primera edición por archivo)
MultiEdit se maneja de forma idéntica: cada archivo del lote se controla individualmente.
Antes de editar {file_path}, presenta estos hechos:
1. Enumera TODOS los archivos que importan/requieren este archivo (busca en el árbol — Glob/Grep, o find/grep mediante Bash)
2. Enumera las funciones/clases públicas afectadas por este cambio
3. Si este archivo lee/escribe archivos de datos, muestra nombres de campos, estructura
y formato de fecha (usa valores redactados o sintéticos, no datos reales de producción)
4. Cita textualmente la instrucción actual del usuario
Puerta de Escribir (primera creación de archivo)
Antes de crear {file_path}, presenta estos hechos:
1. Nombra el/los archivo(s) y línea(s) que llamarán a este nuevo archivo
2. Confirma que ningún archivo existente cumple el mismo propósito (busca en el árbol — Glob/Grep, o find/grep mediante Bash)
3. Si este archivo lee/escribe archivos de datos, muestra nombres de campos, estructura
y formato de fecha (usa valores redactados o sintéticos, no datos reales de producción)
4. Cita textualmente la instrucción actual del usuario
Puerta de Bash destructivo (cada comando destructivo)
Se activa en: rm -rf, git reset --hard, git push --force, drop table, etc.
1. Enumera todos los archivos/datos que este comando modificará o eliminará
2. Escribe un procedimiento de reversión de una línea
3. Cita textualmente la instrucción actual del usuario
Puerta de Bash rutinario (una vez por sesión)
1. La solicitud actual del usuario en una frase
2. Qué verifica o produce este comando específico
Lotes paralelos y aplicación parcial
La puerta de primer toque evalúa cada llamada de herramienta de forma independiente. Cuando se envían varias ediciones a un archivo que aún no se ha tocado en un lote paralelo, la primera llamada es denegada y la denegación marca el archivo como verificado, por lo que las ediciones hermanas en ese lote se aplican. No se revierte nada: el archivo puede terminar conteniendo las ediciones hermanas sin la denegada.
El mensaje de denegación nombra el archivo y advierte que las ediciones hermanas del lote pueden haberse aplicado ya. Trátalo literalmente:
- Envía las ediciones dependientes a un archivo aún no tocado de forma secuencial, no en un lote paralelo. Una definición y su primer uso, o una importación y su sitio de llamada, no deben viajar en el mismo lote.
- Después de una denegación de primer toque, presenta los hechos, reintenta la edición denegada y vuelve a leer el archivo antes de construir sobre cualquier otra cosa del lote.
No es posible un bloqueo a nivel de lote: los hooks ven las llamadas a herramientas una a una, por lo que la puerta no puede saber qué llamadas llegaron juntas.
Inicio rápido
Opción A: Usar el hook ECC (sin instalación)
El hook en scripts/hooks/gateguard-fact-force.js está incluido en este plugin. Actívalo mediante hooks.json.
Si GateGuard bloquea tareas de configuración o reparación, inicia la sesión con
ECC_GATEGUARD=off. Para control a nivel de hook, sigue usando
ECC_DISABLED_HOOKS con el ID del hook GateGuard.
En sesiones largas, solo las primeras GATEGUARD_FACT_FORCE_FULL_DENIALS
denegaciones de verificación fáctica (por defecto 3) emiten el bloque completo de cuatro hechos;
denegaciones posteriores se condensan en una sola línea que lleva el ordinal de denegación, de modo que
bloques casi idénticos no se acumulen en la ventana de contexto y amplifiquen los bucles de repetición del modelo (#2142). Reintentar el mismo archivo o
comando después de presentar hechos nunca vuelve a activar la puerta.
Controles graduados
ECC_GATEGUARD=off (o GATEGUARD_DISABLED=1) desactiva la puerta por completo.
Las variables de esta tabla no — cada una reduce un comportamiento mientras las
verificaciones de Bash destructivo que soportan carga siguen ejecutándose:
| Variable | Valor por defecto | Efecto |
|---|---|---|
GATEGUARD_BASH_ROUTINE_DISABLED | sin definir (puerta activa) | Desactiva la puerta Bash rutinario únicamente. La puerta de Bash destructivo (rm -rf, git reset --hard, drop table, dd if=, …) no se ve afectada. |
GATEGUARD_EXEMPT_GLOBS | sin definir (sin exenciones) | Globs separados por comas; un objetivo de Editar/Escribir/MultiEdit que coincida omite la verificación fáctica de primer toque. Destinado a árboles de bajo valor de importación (pruebas, artefactos generados, directorios temporales) donde "quién importa esto / qué esquema" no aporta señal. |
GATEGUARD_FACT_FORCE_FULL_DENIALS | 3 | Cuántas denegaciones emiten el bloque completo de cuatro hechos antes de que las posteriores se condensen en una sola línea. 0 condensa desde la primera denegación. |
GATEGUARD_BASH_EXTRA_DESTRUCTIVE | sin definir | Patrones adicionales de comandos destructivos, como fuente de regex, añadidos al conjunto integrado. Un regex mal formado se trata como no definido (los integrados aún se aplican) y se registra una vez en stderr. |
GATEGUARD_STATE_DIR | ~/.gateguard | Dónde se mantiene el estado de la puerta por sesión. Si el estado no se puede persistir, la puerta permite la operación en lugar de repetir en bucle, y nombra esta variable en la advertencia. |
GATEGUARD_BASH_ROUTINE_DISABLED acepta 1, true, on, enabled,
enable, o yes (insensible a mayúsculas/minúsculas y espacios en blanco); cualquier otro valor
deja la puerta activa.
Desactivar la puerta por completo
| Variable | Efecto |
|---|---|
ECC_GATEGUARD=off | Desactiva GateGuard para la sesión. Acepta 0, false, off, disabled, o disable. |
GATEGUARD_DISABLED=1 | Mismo efecto. Reconoce solo 1 — las grafías anteriores no aplican aquí. |
Para control a nivel de hook, sigue usando ECC_DISABLED_HOOKS con el ID del hook GateGuard.
Semántica de glob para GATEGUARD_EXEMPT_GLOBS
Los patrones coinciden con la ruta completa del objetivo relativa al proyecto. La raíz del proyecto es
CLAUDE_PROJECT_DIR, con respaldo al cwd del payload del hook, luego al directorio de trabajo del proceso del hook. Los globs relativos nunca eximen objetivos fuera de esa
raíz. Los globs absolutos explícitos coinciden con la ruta absoluta completa del objetivo y pueden
eximir deliberadamente rutas fuera del proyecto.
Tanto los patrones como las rutas usan separadores / y coincidencias en minúsculas. * coincide
dentro de un segmento, ** a través de segmentos, y ? un carácter que no sea separador.
**/ incluye cero directorios, por lo que **/tests/** también coincide con tests/foo.js.
Los patrones mal formados se descartan sin conceder una exención.
Desde 2.2.1, services/** solo cubre el árbol de servicios raíz del proyecto, y
*.md solo cubre sus archivos Markdown raíz. Usa **/*.md para todos los archivos Markdown
dentro del proyecto. Las exenciones existentes sin anclaje pueden necesitar ajuste:
{
"env": {
"GATEGUARD_BASH_ROUTINE_DISABLED": "1",
"GATEGUARD_EXEMPT_GLOBS": "**/tests/**,tests/**,**/*.test.*,**/docs/**,**/dist/**"
}
}
Opción B: Paquete completo con configuración
pip install gateguard-ai
gateguard init
Esto agrega .gateguard.yml para configuración por proyecto (mensajes personalizados, rutas de ignorar, activación/desactivación de puertas).
Antipatrones
- No uses autoevaluación en su lugar. "¿Estás seguro?" siempre obtiene "sí." Esto está verificado experimentalmente.
- No omitas la verificación del esquema de datos. Ambos agentes de la prueba A/B asumieron fechas ISO-8601 cuando los datos reales usaban
%Y/%m/%d %H:%M. Verificar la estructura de datos (con valores redactados) previene toda esta clase de errores. - No controles cada comando Bash. Las puertas de Bash rutinario se activan una vez por sesión. Las puertas de Bash destructivo se activan cada vez. Este equilibrio evita ralentizaciones mientras detecta riesgos reales.
Mejores prácticas
- Deja que la puerta se active de forma natural. No intentes responder de antemano las preguntas de la puerta — la investigación en sí misma es lo que mejora la calidad.
- Personaliza los mensajes de la puerta para tu dominio. Si tu proyecto tiene convenciones específicas, agrégalas a las indicaciones de la puerta.
- Usa
.gateguard.ymlpara ignorar rutas como.venv/,node_modules/,.git/.
Habilidades relacionadas
safety-guard— Verificaciones de seguridad en tiempo de ejecución (complementarias, no superpuestas)code-reviewer— Revisión posterior a la edición (GateGuard es investigación previa a la edición)