Agentic Workshop
Módulo 07 · 50 min

Hooks y automatización local controlada

Los hooks insertan controles automáticos en el momento en que un agente usa una herramienta. Son potentes porque actúan siempre; precisamente por eso deben tener condiciones estrictas, timeouts y logs comprensibles.

Lecciones

Qué trabajarás, paso a paso

7.1

El ciclo de vida de un evento

Un hook es una regla automática vinculada a un evento, por ejemplo antes o después del uso de una herramienta. Para diseñarlo debes dibujar la secuencia: qué lo activa, qué información recibe, qué decisión produce y qué ocurre después.

Un evento nace de una acción solicitada. `PreToolUse` puede permitir, bloquear o pedir aprobación antes de la ejecución. Si la herramienta tiene éxito interviene `PostToolUse`; si falla existe el evento distinto `PostToolUseFailure`. Cada fase recibe un payload y debe producir un estado comprensible.

Dibuja primero la timeline. Si no sabes si una regla debe prevenir un daño o verificar un resultado, todavía no sabes en qué fase implementarla.

  1. Nombra el evento y el payload mínimo que lo describe.
  2. Decide si la regla debe prevenir la acción o verificar su resultado.
  3. Define condiciones, decisión y exit code observables.
  4. Ejecuta la herramienta solo si el control preventivo lo permite.
  5. Registra el resultado e impide que el hook se reactive a sí mismo.
Para verlo en la práctica · Controlar una solicitud de deploy en staging

Un agente puede iniciar el deploy de staging. Quieres impedir ramas no aprobadas y registrar el resultado sin automatizar producción.

  1. Define el evento `deploy.requested` con branch, commit, entorno e identidad solicitante; excluye tokens y variables secretas del payload.
  2. En el pre-hook permite solo `staging`, verifica que el commit exista y exige aprobación si la rama no sigue la convención de release.
  3. Si se permite, la herramienta inicia el deploy y devuelve ID, commit desplegado y estado; un error mantiene distinto `tool_failed` de `blocked`.
  4. El post-hook lee ese resultado, consulta una sola vez el control de salud y registra `verified`, `failed` o `timeout`; nunca inicia un deploy de producción.

Resultado: La línea temporal muestra quién pidió qué, por qué se permitió la acción y qué versión se verificó realmente. Un bloqueo no se confunde con un fallo del deploy.

7.2

Pre-hook: proteger el límite

Un pre-hook es una barrera colocada antes de una acción. Funciona bien cuando la regla depende de hechos que puedes medir: ruta resuelta, tipo de operación, entorno o presencia de una aprobación válida.

Un pre-hook es adecuado para bloquear escrituras sobre archivos sensibles, comandos destructivos u operaciones fuera de alcance. La decisión debería depender de propiedades observables como la ruta y el tipo de acción, no de palabras vagas en el prompt.

El mensaje de bloqueo debe explicar qué regla se ha activado y cómo proceder de forma segura. Un bloqueo sin remedio transforma una protección en un obstáculo opaco. El pre-hook añade un control, pero no sustituye ni puede eludir la política de permisos.

Para verlo en la práctica · Bloquear una eliminación fuera de la carpeta temporal

Un workflow puede eliminar artefactos bajo `/workspace/tmp/build-42`, pero no debe tocar fuentes, el directorio home ni rutas derivadas de variables vacías.

  1. El pre-hook recibe operación y objetivo, rechaza entrada vacía o glob no resuelto y calcula la ruta canónica sin ejecutar la eliminación.
  2. Compara el objetivo con la raíz explícita `/workspace/tmp` y prohíbe la propia raíz; permite solo un descendiente no simbólico identificado para esa build.
  3. Muestra una preview de los archivos y exige el ID de aprobación para cantidad o tamaño por encima del umbral; no imprime el contenido de los archivos.
  4. Prueba directorio válido, `../src`, enlace hacia home y variable vacía. Solo el primer caso debe llegar a la herramienta; los demás explican la ruta resuelta y el remedio.

Resultado: La automatización limpia su propio artefacto sin convertir un error de ruta en una eliminación amplia. Los casos denegados son comprensibles y comprobables.

7.3

Post-hook: observar lo que ya ha ocurrido

Un post-hook trabaja sobre lo que realmente ha ocurrido. Puede volver inmediato un control repetitivo, pero no debería ocultar una corrección sustancial ni hacer parecer exitosa una acción que en realidad dejó errores.

Después de una modificación exitosa, un hook puede formatear el archivo, ejecutar una comprobación dirigida, sustituir la salida mostrada al modelo o registrar el evento. El punto crucial es temporal: la herramienta ya ha actuado, por lo que un `PostToolUse` no puede deshacer una escritura ni prevenir una llamada de red que ya se ha producido.

Para impedir una acción usa `PreToolUse`. Para reaccionar a un error de la herramienta usa `PostToolUseFailure`. En ambos casos limita el control a los archivos implicados y devuelve contexto, duración y mensaje sin ocultar al agente la causa real.

Para verlo en la práctica · Verificar un endpoint TypeScript recién modificado

El agente cambia `src/orders/route.ts`. El proyecto quiere formateo coherente y una comprobación de tipos solo del paquete API.

  1. El post-hook se activa solo después de una escritura exitosa sobre archivos `.ts` del paquete API y recibe la lista exacta de los archivos tocados.
  2. Ejecuta el formatter sobre esos archivos, registra si el diff cambió y no abre otros archivos para una limpieza general.
  3. Lanza `npm run typecheck --workspace api` con timeout definido; conserva exit code, duración y primeras líneas diagnósticas sin datos sensibles.
  4. Si la comprobación falla, devuelve `written_but_unverified` y el mensaje; no modifica tipos ni pruebas en silencio y no revierte la escritura del usuario.

Resultado: Tienes un formateo repetible y una prueba pertinente. Un error de tipos sigue siendo visible y queda atribuido al control posterior, sin confundirse con el fallo de la escritura.

7.4

Idempotencia, recursión y fallo

Un hook fiable debe comportarse bien incluso cuando se repite, se solapa con otra ejecución o encuentra un servicio en error. Estos casos no son excepciones remotas: son el coste normal de la automatización.

Un hook idempotente puede repetirse sin multiplicar los efectos. Un formatter lanzado por `PostToolUse` no genera automáticamente un nuevo evento; el ciclo nace si el script provoca precisamente el evento que observa o solicita nuevas llamadas a la misma herramienta. Nombra ese evento y usa un marcador de ejecución o un comando que no lo reactive.

Define timeout y política de error. Para una protección crítica puede ser correcto fallar en cerrado; para una métrica no esencial puede ser preferible registrar el problema y continuar.

Para verlo en la práctica · Regenerar el índice de la documentación sin crear un ciclo

Después de una modificación en `docs/`, un hook actualiza `docs/index.json`. El primer prototipo se reactiva cuando guarda el propio índice y duplica las entradas.

  1. Cambia el generador: lee los documentos fuente, ordena las entradas y reescribe el índice completo solo si el contenido calculado es distinto.
  2. Excluye `docs/index.json` de los archivos que activan el hook y añade un lock breve para impedir que dos guardados simultáneos escriban a la vez.
  3. Ejecuta dos veces el mismo evento: la primera actualización produce el nuevo índice, la segunda no cambia bytes ni añade entradas. Después simula un proceso interrumpido y verifica la liberación del lock.
  4. Si la generación falla, conserva el índice anterior, marca la documentación como no verificada y muestra el comando manual; no publiques un JSON parcial.

Resultado: Los retries y guardados cercanos producen un único índice válido. El archivo generado no reactiva su propio hook y una avería deja disponible la última versión completa.

Ejemplo guiado

Protección y formateo

Task Notes contiene `.env` y archivos Python que se pueden formatear.

PRE write: si path termina con .env -> bloquea y explica
TOOL write: aplica la modificación autorizada
POST write: si path termina con .py -> formatea ese archivo
LOG: evento, decisión, duración, exit code; nunca el contenido del secreto