Ilustración técnica para: Sin check ejecutable, tu agente para cuando parece hecho

Sin check ejecutable, tu agente para cuando parece hecho


Tu agente no sabe cuándo ha terminado. Se detiene cuando el trabajo parece hecho, porque sin una comprobación que pueda ejecutar esa es la única señal de la que dispone. La guía de buenas prácticas de Claude Code lo plantea así y añade la consecuencia operativa: el bucle de verificación pasas a serlo tú, y cada error espera a que lo detectes.

Eso no se arregla escribiendo un prompt más detallado. Se arregla traduciendo la tarea a una señal que el propio agente pueda leer y sobre la que pueda iterar. Lo que cambia entre una tarea y otra no es si hace falta esa señal, sino cuál sirve: un test, un build, una captura comparada o una puntuación con umbral.

Qué cuenta como criterio de verificación

Un criterio de verificación es una comprobación ejecutable que convierte el resultado de una tarea en una señal binaria de paso o fallo: un comando con código de salida, una aserción sobre el estado final del sistema o una puntuación numérica con umbral declarado. Si la comprobación necesita tu juicio para resolverse, no es un criterio de verificación, es una revisión.

De ahí salen tres propiedades que conviene exigir antes de dar por bueno un criterio:

  • Ejecutable sin ti: el agente puede lanzarlo y leer el resultado dentro de su turno.
  • Binario o con umbral explícito: "el test pasa" o "la similitud es mayor que 0,85", no "el resultado es razonable".
  • Con evidencia legible: la salida del comando entra en la conversación. Revisar la evidencia es más rápido que repetir la verificación por tu cuenta.

La diferencia práctica está en cómo formulas la tarea. "Implementa una función que valide emails" no tiene criterio. "Escribe validateEmail. Casos: usuario@ejemplo.com es true, invalido es false, usuario@.com es false. Ejecuta los tests después de implementar" sí lo tiene, y el agente puede cerrar el bucle solo.

Por qué el agente que escribe el código no puede ser el que lo aprueba

El motivo técnico no tiene nada de moral. Cuando un agente evalúa su propio diff, lo hace con el mismo contexto que lo produjo: las mismas suposiciones, la misma lectura del requisito y la misma interpretación del código que acaba de escribir. Puntúa coherencia interna, no corrección. Un comando externo introduce información que no estaba en ese contexto, y por eso desbloquea el bucle.

Es el mismo argumento que sostiene la separación de responsabilidades en arquitectura de software, aplicado al flujo de trabajo: quien produce el artefacto no valida el artefacto. Cuando no existe un check automatizable, el sustituto razonable es un revisor en contexto limpio, que es justo el enfoque que discute el debate sobre code review con IA y revisión humana: un subagente que ve el diff y los criterios, pero no el razonamiento que llevó hasta ahí.

Qué verificador pide cada tipo de cambio

Este es el mapeo que puedes copiar tal cual. La columna importante es la última: el falso verde típico, porque es donde un check mal elegido te da tranquilidad sin darte información.

Tipo de cambioVerificadorSeñalFalso verde típico
Bugfix de lógicaTest que falla antes y pasa despuésCódigo de salida del runnerEl agente ajusta el test al código que escribió
Refactor sin cambio de comportamientoSuite existente + cobertura de las rutas tocadasExit code + delta de coberturaEsa rama no estaba cubierta antes ni después
Contrato de APIValidación de esquema contra la respuesta realValidador OK/KOEl mock devuelve justo lo que el test espera
Cambio de interfazCaptura del resultado comparada con el diseñoDiferencia bajo umbral o lista de diferenciasLa captura se toma antes de que renderice
Migración de datosScript idempotente + recuentos e integridad referencialConteos esperadosEjecución parcial sin transacción
Salida de texto de un LLMGrader por campos + juez para el criterio abiertoPuntuación con umbralEl juez premia la verbosidad, no el acierto
Infra, build, dependenciasBuild + linter + typecheckExit codeWarnings silenciados para que pase

Para cambios de interfaz, la comprobación visual es la que cubre lo que el test no ve: la instrucción útil es pedir la captura del resultado, compararla con el original, listar las diferencias y corregirlas, no "mejora el dashboard".

Cuatro preguntas para elegir el verificador

Si dudas qué poner, recorre el árbol en este orden y párate en la primera respuesta afirmativa:

  1. ¿La salida correcta es única y comparable? Test, assert o diff contra fixture. Es lo más barato y lo que primero deberías intentar.
  2. ¿El cambio es de estado, no de valor devuelto? Verifica el estado final (filas, ficheros, respuesta del endpoint levantado), no el retorno de la función.
  3. ¿El resultado es visual? Captura comparada con una referencia. Sin referencia no hay verificación, solo opinión.
  4. ¿La salida es texto abierto? Grader por campos para lo estructurado y juez por modelo solo para la parte que no admite comparación exacta.

Cuándo no aplica: en tareas exploratorias sin criterio de éxito definido ("qué mejorarías de este módulo") montar un check es trabajo perdido. Ahí el resultado es información, no un artefacto que deba pasar una puerta.

Cuando la salida es texto: graders en lugar de asserts

Para salidas no deterministas, la referencia de Graders de OpenAI define una taxonomía que sirve de vocabulario incluso si no usas esa API: string_check para comparación exacta con operaciones eq, ne, like e ilike; text_similarity con métricas como coseno, fuzzy_match o BLEU; python para reglas propias; score_model y label_model cuando juzga un modelo; y multi, que combina varios con una fórmula de salida.

La decisión práctica es esta: todo lo que sea comparable exactamente se verifica con comparación exacta, y el juez por modelo se reserva para el residuo que no lo admite. Un ejemplo de extracción de facturas, donde el IBAN se compara y el tono se puntúa:

Grader compuesto que pondera un campo exacto y un criterio subjetivo en una sola puntuación:

{
  "type": "multi",
  "name": "extraccion-factura",
  "graders": {
    "iban": {
      "type": "string_check",
      "name": "iban",
      "input": "{{sample.output_json.iban}}",
      "reference": "{{item.iban}}",
      "operation": "eq"
    }
  },
  "calculate_output": "iban"
}

Un juez por modelo tiene coste por llamada y latencia propia. Si lo ejecutas en cada turno de una sesión larga, el gasto deja de ser trivial en cuanto cruza el umbral que te hayas fijado, y ese umbral lo pones tú: como heurística práctica para un proyecto personal, 10-50 €/mes es un rango razonable para marcar la línea, no un dato sobre lo que gasta un desarrollador medio. Aplica el mismo razonamiento que para elegir modelo por coste real y no por benchmark: el juez puede ser un modelo pequeño si la rúbrica es estrecha.

Tres niveles de dureza para el mismo check

Una vez existe la comprobación, queda decidir cuánto bloquea el cierre de la tarea. La documentación de Claude Code ordena las opciones de menos a más setup, y el criterio para elegir es cuánta atención tuya quieres poder retirar:

  • En el prompt: pides ejecutar el check e iterar en el mismo mensaje. Funciona hoy, en cualquier tarea, sin configurar nada.
  • Como condición de sesión: un evaluador vuelve a comprobar la condición después de cada turno y el agente sigue trabajando hasta que se resuelve.
  • Como puerta determinista: un hook de Stop ejecuta tu script y bloquea el fin del turno hasta que pase. Un hook de tipo command que sale con código 2 marca error bloqueante, y ese bloqueo no se puede sobrescribir desde el JSON de salida.

Configuración mínima de un hook de Stop que ejecuta tu script de verificación:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": ".claude/verify.sh", "timeout": 120 }
        ]
      }
    ]
  }
}

Dos detalles que evitan que la puerta se convierta en un bucle infinito. Claude Code anula el hook y termina el turno tras 8 bloqueos consecutivos, según la referencia de hooks a 18/09/2026. Y el hook recibe el campo stop_hook_active, que vale true cuando la ejecución ya viene de un bloqueo previo: comprobarlo es lo que impide bloquear sobre una condición que no se va a resolver nunca. Ese techo de 8 intentos es la misma lógica de presupuestar intentos en lugar de tokens, aplicada por el harness en vez de por ti.

Si necesitas inspeccionar ficheros o salida de comandos para decidir, existe el hook de tipo agent: lanza un subagente que responde {"ok": true} o {"ok": false, "reason": "..."}, con timeout por defecto de 60 segundos y hasta 50 turnos de uso de herramientas, según esa misma referencia de hooks, que marca el tipo agent como experimental. Es más caro que un script, así que reservarlo para lo que un script no puede decidir es la elección sensata.

Plantilla de criterio de verificación

Rellena estos campos antes de lanzar la tarea. Si alguno queda vacío, el agente no tiene con qué cerrar el bucle:

tarea: "refresco de token en src/auth/"
señal_de_paso: "pytest tests/auth/test_refresh.py sale con 0"
señal_de_fallo: "cualquier exit code distinto de 0"
evidencia_requerida: "comando ejecutado y últimas 20 líneas de salida"
estado_previo: "el test falla antes del cambio (reproduce el bug)"
fuera_de_alcance: "no tocar tests existentes ni el cliente HTTP"
dureza: "Stop hook"
max_intentos: 8

El campo estado_previo es el que más falsos verdes evita: exigir que el test falle antes del cambio demuestra que el test verifica algo. El campo fuera_de_alcance es el que evita que el agente haga pasar el check modificando el propio check. Cuánta dureza asignar a cada tarea depende del riesgo del cambio, que es la decisión que hay detrás de ajustar la autonomía según el tipo de cambio en lugar de aplicar el mismo margen a todo.

Cuándo un check verde te engaña

Los criterios de verificación fallan de formas concretas, y conviene reconocerlas antes de confiar en un tablero en verde:

  • El agente optimiza el check, no el problema. Relaja el assert, añade un skip o silencia el warning. La defensa es declarar el test como fuera de alcance y revisar el diff de los ficheros de test, no solo el resultado.
  • El verde de una pasada no es fiabilidad. Con un 70 % de éxito por intento, la probabilidad de que al menos una de tres ejecuciones pase ronda el 97 %, pero la de que pasen las tres es de alrededor del 34 % (cálculo directo sobre esa tasa). Si comunicas la primera cifra, estás midiendo tu mejor día.
  • El juez por modelo sin conjunto etiquetado es una opinión cara. Antes de darle poder de bloqueo, contrasta sus veredictos con un conjunto de ejemplos etiquetados a mano y comprueba que coincide con el criterio humano; si no, ajusta la rúbrica.
  • El test verde sobre un estado sucio. Si la ejecución anterior dejó ficheros o filas a medias, el check puede pasar por motivos equivocados. Aquí ayuda que las acciones del agente sean idempotentes al reanudar, para que repetir la verificación no dependa de lo que quedó del intento previo.
  • El resultado correcto por el camino equivocado. Un agente puede acertar la respuesta final habiendo llamado herramientas innecesarias, repetido pasos o pasado argumentos incorrectos. Evaluar la trayectoria, y no solo el resultado, es lo que expone ese tipo de fallo, como describe el material de Langfuse sobre evaluación de trayectorias y llamadas a herramientas.

Ninguno de estos casos invalida el enfoque: invalidan la lectura ingenua del verde. Un check que puede fallar y que has visto fallar al menos una vez vale más que tres checks que nunca han estado en rojo.

Compartir X LinkedIn