GateGuard — Compuerta de verificación de hechos previa a la 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
- Al trabajar en cualquier código base donde las modificaciones de archivos afecten a múltiples módulos.
- Proyectos con archivos de datos que tengan esquemas o formatos de fecha específicos.
- Equipos donde el código generado por IA deba coincidir con patrones existentes.
- Cualquier flujo de trabajo donde Claude tienda a adivinar en lugar de investigar.
Concepto central
La autoevaluación de los LLM no funciona. Pregunta «¿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 Leer. La investigación en sí misma crea un contexto que cambia el resultado.
Compuerta de tres etapas:
1. DENEGAR — bloquear el primer intento de Editar/Escribir/Bash
2. FORZAR — indicar al modelo exactamente qué hechos debe recopilar
3. PERMITIR — autorizar el reintento después de presentar los hechos
Ningún competidor hace las tres. La mayoría se queda en denegar.
Evidencia
Dos pruebas A/B independientes, agentes idénticos, misma tarea:
| Tarea | Con compuerta | Sin compuerta | Diferencia |
|---|---|---|---|
| Módulo de analíticas | 8.0/10 | 6.5/10 | +1.5 |
| Validador de webhooks | 10.0/10 | 7.0/10 | +3.0 |
| Media | 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 compuerta
Compuerta 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 {ruta_del_archivo}, 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 los nombres de campo, la estructura
y el formato de fecha (usa valores redactados o sintéticos, no datos reales de producción)
4. Cita la instrucción actual del usuario textualmente
Compuerta de Escribir (primera creación de archivo)
Antes de crear {ruta_del_archivo}, presenta estos hechos:
1. Nombra el/los archivo(s) y la(s) 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 los nombres de campo, la estructura
y el formato de fecha (usa valores redactados o sintéticos, no datos reales de producción)
4. Cita la instrucción actual del usuario textualmente
Compuerta de Bash destructivo (cada comando destructivo)
Se activa con: 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 la instrucción actual del usuario textualmente
Compuerta de Bash rutinario (una vez por sesión)
1. La solicitud actual del usuario en una oración
2. Qué verifica o produce este comando específico
Lotes paralelos y aplicación parcial
La compuerta 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 mismo lote paralelo, la primera llamada es denegada y la denegación marca el archivo como verificado, por lo que las ediciones hermanas de 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 los hermanos 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 punto 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 por una, por lo que la compuerta 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 de GateGuard.
En sesiones largas, solo las primeras GATEGUARD_FACT_FORCE_FULL_DENIALS denegaciones de verificación de hechos (por defecto 3) emiten el bloque completo de cuatro hechos; las denegaciones posteriores se condensan en una sola línea que lleva el ordinal de la denegación, para 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 compuerta.
Controles graduados
ECC_GATEGUARD=off (o GATEGUARD_DISABLED=1) desactiva la compuerta por completo. Las variables de esta tabla no — cada una reduce un comportamiento mientras las comprobaciones críticas de Bash destructivo siguen funcionando:
| Variable | Valor por defecto | Efecto |
|---|---|---|
GATEGUARD_BASH_ROUTINE_DISABLED | sin establecer (compuerta activa) | Desactiva solo la compuerta de Bash rutinario. La compuerta de Bash destructivo (rm -rf, git reset --hard, drop table, dd if=, …) no se ve afectada. |
GATEGUARD_EXEMPT_GLOBS | sin establecer (sin exclusiones) | Globs separados por comas; un destino de Editar/Escribir/MultiEdit que coincida omite la verificación de hechos de primer toque. Destinado a árboles con bajo valor de importación (pruebas, artefactos generados, directorios temporales) donde «quién importa esto / qué esquema» no aporta información. |
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 establecer | Patrones adicionales de comandos destructivos, como fuente de regex, añadidos al conjunto incorporado. Un regex mal formado se trata como no establecido (los incorporados siguen aplicándose) y se registra una vez en stderr. |
GATEGUARD_STATE_DIR | ~/.gateguard | Dónde se mantiene el estado de la compuerta por sesión. Si el estado no se puede persistir, la compuerta permite la operación en lugar de entrar en bucle y nombra esta variable en la advertencia. |
GATEGUARD_BASH_ROUTINE_DISABLED acepta 1, true, on, enabled, enable o yes (sin distinción entre mayúsculas, minúsculas y espacios en blanco); cualquier otro valor deja la compuerta activa.
Desactivar la compuerta 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 variantes anteriores no aplican aquí. |
Para control a nivel de hook, sigue usando ECC_DISABLED_HOOKS con el ID del hook de GateGuard.
Semántica de glob para GATEGUARD_EXEMPT_GLOBS
Los patrones coinciden con la ruta de destino completa relativa al proyecto. La raíz del proyecto es CLAUDE_PROJECT_DIR, con fallback al cwd del payload del hook y luego al directorio de trabajo del proceso del hook. Los globs relativos nunca excluyen destinos fuera de esa raíz. Los globs absolutos explícitos coinciden con la ruta de destino absoluta completa y pueden excluir deliberadamente rutas fuera del proyecto.
Tanto los patrones como las rutas usan separadores / y coincidencias en minúsculas. * coincide dentro de un segmento, ** entre segmentos y ? con 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 exclusión.
Desde la versión 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 exclusiones existentes sin anclar pueden necesitar ajustes:
{
"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 añade .gateguard.yml para la configuración por proyecto (mensajes personalizados, rutas de ignorar, activación/desactivación de compuertas).
Antipatrones
- No uses autoevaluación en su lugar. «¿Estás seguro?» siempre obtiene «sí». Esto está verificado experimentalmente.
- No te saltes la comprobació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. Comprobar la estructura de datos (con valores redactados) previene toda esta clase de errores. - No actives la compuerta para cada comando Bash. La compuerta de Bash rutinario se activa una vez por sesión. La compuerta de Bash destructivo se activa cada vez. Este equilibrio evita ralentizaciones mientras detecta riesgos reales.
Buenas prácticas
- Deja que la compuerta se active de forma natural. No intentes responder las preguntas de la compuerta de antemano — la investigación en sí misma es lo que mejora la calidad.
- Personaliza los mensajes de la compuerta para tu dominio. Si tu proyecto tiene convenciones específicas, agrégalas a las indicaciones de la compuerta.
- Usa
.gateguard.ymlpara ignorar rutas como.venv/,node_modules/,.git/.
Habilidades relacionadas
safety-guard— Comprobaciones 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)