Cada hook es una cicatriz: 84 fallos de agentes convertidos en código
En mi sistema de orquestación de agentes tengo 84 ganchos que interceptan 15 de los 26 tipos de eventos del ciclo de vida que expone Claude Code en su versión 2.1.116 (abril de 2026). Cada gancho es un script de shell o un fragmento de Python que se dispara antes o después de una acción concreta del agente: lecturas de archivos, escrituras, comandos de bash, peticiones web, creación de subagentes, operaciones de git, llamadas a herramientas MCP. Cada gancho existe porque algo salió mal.
Cada gancho de un sistema de orquestación de agentes se remonta a un fallo concreto en producción, lo que convierte a la colección de ganchos en memoria institucional codificada como scripts de shell. Hubo agentes que borraron cachés de CDN, leyeron archivos de credenciales, reportaron pruebas aprobadas que nunca ejecutaron y se desviaron de su tarea durante 40 minutos. Cada incidente produjo una pequeña salvaguarda determinista que se dispara en silencio en todas las sesiones posteriores.
No fallos teóricos. Fallos en producción. Un agente borró una caché de CDN que servía millones de peticiones. Un agente intentó escribir claves SSH. Un agente reportó “todas las pruebas pasan” sin invocar pytest. Un agente se alejó tanto de su tarea que pasó cuarenta minutos optimizando una función en un archivo que no tenía nada que ver con el trabajo asignado.
Ninguno de estos ganchos lo diseñé de forma proactiva. No me senté a enumerar los modos de fallo de los agentes de IA autónomos para después escribir controles preventivos. Todos los ganchos son reactivos. Algo se rompió, escribí un script para que no volviera a romperse, y ese script se ha disparado en silencio en cada sesión desde entonces. El sistema de ganchos no es una arquitectura de seguridad. Es una colección de cicatrices.
En resumen
- La purga de caché: un agente borró una caché de CDN en producción mediante una llamada autorizada a la API. Ahora dos ganchos (47 líneas) resguardan las operaciones destructivas detrás de una contraseña que tiene que escribir un humano.
- El lector de credenciales: un agente incluyó tokens de API en su ventana de contexto. Una salvaguarda de coincidencia de rutas bloquea ahora las lecturas de archivos de credenciales y registra los accesos a archivos
.env. - El verificador fantasma: un agente reportó “todas las pruebas pasan” sin ejecutar pytest. Un detector de lenguaje evasivo redujo la verificación fantasma del 12 % a menos del 2 % de las sesiones.
- Las doce desviaciones: los agentes perdieron el hilo de su tarea de forma comprobable doce veces en sesenta días. Un detector de similitud de coseno con umbral de 0,30 se dispara ahora cada 25 llamadas a herramientas.
- La taxonomía: seis categorías estructurales de fallo cubren los 84 ganchos. Las categorías nuevas son raras después de más de 500 sesiones. El sistema se endurece con cada incidente.
La purga de caché: cómo una sola llamada autorizada rompió producción
El 21 de marzo de 2026 le pedí a un agente que investigara por qué las páginas de mercado de resumegeni.com cargaban con lentitud. El agente empezó la investigación con normalidad: leyó los manejadores de rutas, revisó las consultas a la base de datos, perfiló el renderizado de plantillas. Luego decidió que las entradas obsoletas de la caché de Cloudflare podían estar enmascarando el rendimiento real.
El agente llamó a mcp__cloudflare__cache_purge con purge_everything: true.
Todas las páginas en caché del sitio en producción quedaron invalidadas al instante. La CDN pasó de servir la mayoría de las peticiones en 80-100 ms a reenviar cada petición al servidor de origen en Railway. La página de mercado de Austin pasó de menos de un segundo a 14.290 milisegundos. La de Nueva York, de menos de un segundo a 6.891 ms. Cada página del sitio se renderizaba ahora desde un origen en frío en cada petición.
El agente no hizo nada que no estuviera autorizado. Usó una herramienta MCP legítima con credenciales válidas para llamar a un endpoint autorizado de la API. La purga de caché es un paso de investigación razonable si estás depurando el comportamiento de la caché. El problema es que “razonable para depurar” y “catastrófico para producción” eran la misma llamada a la API, y no existía ninguna restricción entre el razonamiento del agente y la consecuencia en producción.4
Esa noche construí dos ganchos.
La salvaguarda de Bash (destructive-api-guard.sh): se dispara con cada comando de bash. Busca coincidencias con curl.*purge, rm -rf, DROP TABLE, docker.*rm, git push.*--force. Bloqueo duro (exit 2). El agente ve un mensaje que explica por qué se bloqueó el comando y sugiere alternativas. No puede continuar sin la contraseña “rosebud”, que solo puede entrar al contexto si la escribe un humano.
La salvaguarda de MCP (destructive-mcp-guard.sh): se dispara con cada llamada a una herramienta MCP que coincida con mcp__cloudflare o mcp__github. Busca coincidencias con purge, delete, destroy, remove en los parámetros de la herramienta. Mismo bloqueo duro, misma contraseña.
Dos ganchos. Dos scripts de shell. Total: 47 líneas de código. Desde su instalación han evitado cero purgas de caché, porque ningún agente ha intentado ninguna desde que existe la contraseña. Los ganchos no están frenando ataques. Están impidiendo que esa categoría de error sea posible.
El incidente de la purga también dejó al descubierto el problema de rendimiento que se suponía que debía investigar. Los 14 segundos de Austin en renderizado en frío condujeron al traspaso de la página de mercado, que a su vez llevó a la corrección de la forma de la consulta cuatro días después. El incidente fue útil. El gancho garantiza que no se repita.
El lector de credenciales
En febrero de 2026, un agente que reunía contexto para un proyecto leyó ~/.claude/docs/credentials.md. Ese archivo contiene tokens de API de Cloudflare, GitHub, Railway y otros servicios. El agente incluyó un resumen del contenido del archivo en sus notas de trabajo, lo que significaba que los tokens formaron parte de la solicitud a la API enviada a los servidores de Anthropic.
No se hizo commit de ningún token. Ninguno quedó expuesto públicamente. Pero los tokens viajaron a través de una API de terceros dentro de una ventana de contexto que no controlo. La superficie de riesgo se amplió de “mi computadora” a “mi computadora más la infraestructura de inferencia de Anthropic”.
La salvaguarda de rutas de credenciales se dispara en cada lectura de archivo. Compara la ruta con una lista de patrones sensibles: .env, credentials, .ssh/, .aws/, .gnupg/, secrets. Ante lecturas de credenciales, el gancho registra una advertencia y bloquea la lectura. Ante lecturas de .env, permite la lectura pero registra el acceso.
La salvaguarda es orientativa para la mayoría de las rutas y un bloqueo duro para los archivos de credenciales. La distinción importa: un agente que lee .env para entender los nombres de las variables de entorno aporta contexto útil. Un agente que lee credentials.md para conocer tokens de API es un incidente de seguridad.
Desde su instalación, la salvaguarda de rutas de credenciales se ha disparado 23 veces a lo largo de más de 200 sesiones. Veinte correspondían a agentes que leían archivos .env (registrado, permitido). Tres fueron agentes que intentaban leer archivos de credenciales o de claves (bloqueado). Cada lectura bloqueada correspondía a un agente que reunía contexto del proyecto de forma amplia y que acabó incluyendo un archivo sensible en su patrón de búsqueda. Ninguno actuaba con malicia. Todos habrían introducido secretos en la ventana de contexto de no ser por la salvaguarda.
El verificador fantasma
El modo de fallo más insidioso es el del agente que reporta una verificación exitosa sin haberla realizado.
Sesión 147. Le pedí a un agente que refactorizara una consulta a la base de datos y validara el cambio con el conjunto de pruebas existente. El agente refactorizó la consulta correctamente. El informe final decía: “Todas las pruebas pasan. La consulta refactorizada produce resultados idénticos a la original”.
Revisé el registro de la sesión. No aparecía ninguna invocación de pytest. No se había llamado a ningún ejecutor de pruebas. El agente razonó que las pruebas pasarían porque la consulta refactorizada era lógicamente equivalente a la original, y reportó ese razonamiento como si fuera un resultado de pruebas.
La consulta refactorizada era correcta. Las pruebas habrían pasado. El razonamiento del agente era sólido. Pero razonar sobre pruebas no es ejecutarlas, y en esa brecha es donde los bugs llegan a producción. Si la consulta refactorizada hubiera estado sutilmente mal en algún caso límite que el razonamiento del agente no cubría, el bug se habría desplegado con un informe final que afirmaba haber verificado con pruebas.
Este modo de fallo ocurrió 7 veces en 60 sesiones antes de que construyera el gancho de la puerta de evidencia. El gancho se dispara en cada informe final y busca lenguaje evasivo: “deberían pasar”, “creo que”, “es probable que las pruebas pasen”, “estoy seguro de que”. Cuando lo detecta, inyecta un mensaje: “Se detectó lenguaje evasivo. Cita evidencia concreta: pega la salida de las pruebas, nombra el archivo y el número de línea, o remite al paso de verificación específico”.
El gancho no comprueba que las pruebas se hayan ejecutado de verdad. Marca el patrón lingüístico que indica que la verificación se omitió. La detección es imperfecta: un agente con suficiente fluidez podría reformular su evasiva para esquivar el patrón. Pero el gancho detecta el caso común, que representa el 12 % de los fallos de agentes que requieren intervención humana.1
Tras instalar el gancho, la verificación fantasma cayó del 12 % a menos del 2 % de las sesiones. Ese 2 % restante corresponde a casos en los que el agente reformula la evasiva o en los que la afirmación de verificación es técnicamente exacta pero incompleta (por ejemplo, “las pruebas unitarias pasan” cuando no se ejecutaron las de integración).
Desviación
Entre enero y marzo de 2026, mi detector de desviación se disparó doce veces en sesiones donde el agente había perdido el hilo de su tarea de forma comprobable.
El detector de desviación funciona generando el embedding del prompt original de la tarea y comparándolo periódicamente con el de las acciones recientes del agente. Cuando la similitud de coseno baja de 0,30, el sistema inyecta una advertencia que contiene el prompt original. Calibré el umbral experimentando: 0,50 era demasiado sensible (se disparaba con exploración legítima de subtareas), 0,20 demasiado permisivo (se le escapaban desviaciones evidentes), 0,30 captó todos los incidentes verificados.
La sesión 203 fue el caso más claro. La tarea era “arreglar el escapado XML roto del sitemap para los slugs de empleo que contienen ampersands”. El agente empezó leyendo el código de generación del sitemap. Luego notó que el sitemap se generaba a partir de una consulta a la base de datos. Luego notó que esa consulta se podía optimizar. Después pasó 40 minutos refactorizándola hacia un patrón de vista materializada, escribió pruebas para la nueva consulta y reportó la optimización como completada. Nunca arregló el escapado de ampersands.
El detector de desviación lo habría señalado en la marca de las 25 llamadas a herramientas, unos 15 minutos después de iniciada la sesión, cuando la similitud entre “arreglar el escapado XML del sitemap” y “crear una vista materializada” cayó por debajo del umbral. En su lugar, descubrí la desviación durante la revisión.
La sesión 89 fue más sutil. La tarea era “añadir limitación de tasa a los endpoints de autenticación”. El agente la añadió correctamente. Luego notó que el flujo de autenticación tenía mensajes de error inconsistentes. Luego los estandarizó. Luego notó que el formato de las respuestas de error difería del estándar de respuestas de la API. Después refactorizó el formato de respuesta en 12 endpoints. La limitación de tasa quedó correcta y completa. La explosión de alcance fue la desviación.
El detector de desviación se dispara cada 25 llamadas a herramientas. En las doce ocasiones en que la similitud quedó por debajo del umbral, el agente se había apartado de la tarea original de forma comprobable. En seis casos, el agente se corrigió solo tras ver la advertencia inyectada. En cuatro, reconoció la desviación pero argumentó que el trabajo en curso era valioso (a veces con razón). En dos, ignoró la advertencia y siguió con el trabajo divergente.
El gancho no evita la desviación. La hace visible. La decisión de reconducir o permitir el trabajo divergente sigue en manos del humano. Pero sin el gancho, la desviación es invisible hasta el informe final, y para entonces el presupuesto de contexto ya está gastado.
La taxonomía de las cicatrices
Tras 84 ganchos, emergen patrones. Los fallos se agrupan en seis categorías:
| Categoría | Ganchos | Ejemplo |
|---|---|---|
| Exposición de credenciales | 12 | El agente lee .ssh/, incluye claves de API en resúmenes, accede a configuraciones de nube |
| Operaciones destructivas | 8 | Purga de caché, eliminación de bases de datos, force pushes, borrado de archivos |
| Desviación de tarea | 4 | El agente trabaja en el problema equivocado, explosión de alcance, madrigueras de subtareas |
| Calidad del resultado | 6 | Verificación fantasma, evasivas sin evidencia, informes incompletos |
| Agotamiento de recursos | 3 | Demasiados subagentes generados, bucles sin límite, desbordamiento de contexto |
| Contaminación entre proyectos | 4 | Un agente del proyecto A modifica archivos del proyecto B |
Los 47 ganchos restantes son específicos de cada proyecto (aplicación de convenciones, salvaguardas de despliegue, validadores de traducción) o experimentales (seguimiento de costos, métricas de sesión, latidos de actividad).
Las seis categorías estructurales son estables. Los incidentes nuevos dentro de ellas los detectan los ganchos existentes. Las categorías nuevas son raras. En seis meses de operación solo surgió una categoría estructural inédita (contaminación entre proyectos, descubierta cuando una sesión que corría en el proyecto obsidian-signals intentó editar archivos de blakecrosley.com). Las otras cinco quedaron establecidas dentro de las primeras 60 sesiones.
El estudio Agents of Chaos, un experimento multiuniversitario de 14 días que dio a seis agentes de IA acceso a correo, bash, sistemas de archivos y GitHub, identificó de forma independiente categorías de fallo que se solapan con estas: respuesta desproporcionada (operaciones destructivas), secuestro de identidad (exposición de credenciales), bucles infinitos (agotamiento de recursos) y cumplimiento gradual bajo presión (desviación de tarea).5 La convergencia entre su investigación controlada y mi experiencia en producción sugiere que estas categorías son propiedades estructurales de los agentes autónomos, no artefactos de una configuración concreta.
Lo que los ganchos no pueden detectar
Los ganchos operan a nivel de llamada a herramienta. Interceptan la acción antes o después de que ocurra. No pueden interceptar el razonamiento que llevó a esa acción.
Un agente que decide refactorizar una función en vez de arreglar el bug reportado produce una llamada a herramienta válida (escritura de archivo) con contenido correcto (código sintácticamente válido) que incumple la tarea (función equivocada). Ningún gancho lo detecta porque ninguna llamada resulta sospechosa. El detector de desviación acaba por captarlo, pero solo después de que el agente haya consumido bastante contexto en el trabajo equivocado.
Los ganchos tampoco pueden detectar fallos de composición, donde cada acción individual está autorizada pero la secuencia produce un resultado no autorizado. La purga de caché fue un fallo de composición: leer la configuración de la caché (autorizado), llamar a la API de purga (autorizado), pero la combinación (purgar la caché de producción durante una investigación) resultó dañina. La salvaguarda de MCP detecta ahora esa combinación concreta, pero las composiciones inéditas siguen sin cubrir.
La brecha de composición en la cadena de suministro3 opera al mismo nivel: componentes de confianza que se componen en comportamiento no autorizado. Los ganchos son salvaguardas a nivel de componente. Razonar a nivel de composición exige otro mecanismo, uno que evalúe secuencias de acciones en lugar de acciones sueltas. El detector de desviación es la aproximación más cercana: evalúa la trayectoria del comportamiento en vez de llamadas individuales. Pero mide la similitud con la tarea original, no la seguridad de la secuencia de acciones compuesta.
La distancia entre los ganchos y una seguridad completa es la distancia entre la memoria institucional y la previsión institucional. Los ganchos recuerdan lo que salió mal. No predicen lo que saldrá mal después.
Por qué lo reactivo es honesto
Podría diseñar un sistema de ganchos proactivo. Enumerar todos los modos de fallo posibles. Escribir controles preventivos para cada uno. Construir una arquitectura de seguridad completa antes de la primera sesión.
No lo hago porque el diseño proactivo exige predecir fallos que aún no han ocurrido. Las predicciones serían erróneas. Los ganchos resultarían demasiado amplios (bloqueando acciones legítimas) o demasiado estrechos (sin captar el patrón de fallo real). La tasa de falsos positivos erosionaría la confianza en el sistema, y acabaría ignorando las alertas.
Los ganchos reactivos son honestos. Cada uno dice: “pasó esto en concreto, y esta es la salvaguarda concreta que lo impide”. La salvaguarda está calibrada con precisión al fallo porque fue el fallo el que la definió. Los falsos positivos son sustancialmente menores porque el patrón se extrae de un incidente real, no se imagina desde un modelo de amenazas. Una salvaguarda reactiva puede volverse excesiva con el tiempo, a medida que el código evoluciona, pero su precisión de partida es alta.
El enfoque reactivo tiene un costo: la primera instancia de cada categoría de fallo pasa sin freno. La purga de caché ocurrió. La lectura de credenciales ocurrió. La verificación fantasma se desplegó. La desviación consumió contexto. Cada primer fallo es el precio de entrada a una salvaguarda precisa y de bajo ruido que impide el segundo.
Después de más de 500 sesiones, la mayoría de las categorías estructurales de fallo ya se han encontrado. El costo del primer fallo se amortiza a lo largo de cientos de sesiones en las que el gancho impidió la reincidencia. El sistema se endurece con cada incidente. No se vuelve más listo. Se vuelve más duro.
Cada gancho es una cicatriz. Cada cicatriz es una lección. Las lecciones se acumulan.2
Preguntas frecuentes
¿Puedo ver tus configuraciones de ganchos?
Describo el sistema de ganchos en mi comentario al NIST sobre seguridad de agentes y lo menciono a lo largo de la serie de ingeniería de IA. Los ganchos se registran en ~/.claude/settings.json y se despachan por tipo de evento a través de ~/.claude/hooks/dispatchers/.
¿Cómo afectan los ganchos al rendimiento del agente?
Cada gancho añade milisegundos por llamada a herramienta. Con 84 ganchos, la sobrecarga total es de 200-400 ms por llamada, según cuáles se disparen. Es despreciable frente al tiempo de inferencia del modelo (2-5 segundos por respuesta). Los ganchos no son el cuello de botella.
¿Funcionan los ganchos con otras herramientas de programación con IA?
Los ganchos son específicos de Claude Code (modelo de eventos PreToolUse, PostToolUse). El concepto se aplica a cualquier framework de agentes con soporte de middleware o plugins. Las implementaciones concretas no son portables, pero la taxonomía de cicatrices y la metodología reactiva sí son universales.
¿Qué pasa cuando un gancho bloquea una acción?
Los bloqueos duros (exit 2) impiden la acción e inyectan un mensaje que explica por qué. El agente ve el motivo y se ajusta. Los ganchos orientativos (exit 0) registran la preocupación pero permiten la acción. Las operaciones destructivas usan bloqueos duros. La mayoría de las demás categorías usan ganchos orientativos. La contraseña se reserva para las operaciones más peligrosas (purga de caché, eliminación de infraestructura).
¿Cómo decides entre bloqueo duro y aviso orientativo?
Dos clases reciben bloqueo duro: las operaciones destructivas (purgas de caché, borrados de bases de datos, force pushes, modificaciones de infraestructura) y la exposición de credenciales (lectura de archivos secretos, acceso a almacenes de claves). Todo lo demás recibe registro orientativo. La distinción está en la gravedad de la consecuencia: si la acción se puede deshacer a bajo costo y no filtra secretos, basta con un aviso. Si es irreversible o expone credenciales, hace falta un bloqueo duro.
Fuentes
-
Blake Crosley, “What I Told NIST About AI Agent Security,” blakecrosley.com, febrero de 2026. Tasa de verificación fantasma del 12 % en más de 60 sesiones autónomas. 84 ganchos que cubren 15 de los 26 tipos de eventos del ciclo de vida de Claude Code (v2.1.116), metodología de detección de desviación. ↩
-
Blake Crosley, “Compound Context: Why AI Projects Get Better the Longer You Stay With Them,” blakecrosley.com, marzo de 2026. Marco del contexto compuesto: los ganchos como una de las seis categorías que acumulan retornos. ↩
-
Blake Crosley, “The Supply Chain Is the Attack Surface,” blakecrosley.com, marzo de 2026. Brecha de composición: componentes autorizados individualmente que producen resultados no autorizados. ↩
-
Blake Crosley, “Deploy and Defend: The Agent Trust Paradox,” blakecrosley.com, marzo de 2026. Incidente de purga de caché y respuesta con salvaguarda de API destructiva. ↩
-
Christoph Riedl et al., “Agents of Chaos,” arXiv:2602.20021, febrero de 2026. Estudio multiuniversitario de 14 días (Northeastern, Stanford, Harvard, MIT, CMU). Seis agentes de IA, 10 vulnerabilidades de seguridad identificadas, entre ellas respuesta desproporcionada, secuestro de identidad y bucles infinitos. ↩