AIDive

Claude Code no dice «listo» sin los gates de Shopify

Por AIDive · Publicado el

Agentes de programaciónAutomatización y workflows

Intro: el gate que no deja decir «listo»

Un gate, tal como lo usa Shopify, es una comprobación que un cambio escrito por IA debe superar antes de avanzar. Reconstruidos en Claude Code, solo el primero valió la pena: los tests de aceptación. Shopify publicó cuatro gates y ningún número, así que este experimento los construye sobre un repositorio real, ejecuta cincuenta peticiones con cada gate desactivado por turnos y cuenta qué detectó cada uno y cuánto costó.

Helix se hizo público el 21 de septiembre de 2026. Hace pasar cada checkpoint por cuatro gates, en orden: tests, revisión de diseño, dos revisores y un ingeniero. El post no da ninguna tasa de aciertos. Los revisores son la parte que más gente copia, y en estas cincuenta ejecuciones la tasa de aciertos solo se movió cuando los tests formaban parte del bucle. Los revisores no aportaron nada, a un coste de aproximadamente el triple.

Del mismo banco de pruebas salen otros dos hallazgos. Un hook que supuestamente no se puede saltar tiene tres salidas en su propia documentación. Y cuando los tests y los revisores no estaban de acuerdo, el agente hizo algo que nadie había previsto. El recuento empieza por el caso sin ningún gate.

Cuatro gates, cero números, un banco de pruebas

El repositorio del banco de pruebas es tinydb, una base de datos de documentos real, con 226 tests en verde en el commit utilizado.

Helix es la herramienta interna de Shopify para devolver sus apps móviles al código nativo. Su post enuncia la regla sin rodeos: cada checkpoint pasa por cuatro gates, en orden.

Gate Qué hace en el post de Shopify
Comportamiento Ejecuta tests en los que todos los casos relevantes deben pasar
Diseño Pide a un segundo modelo que actúe como un revisor de diseño perfeccionista
Revisión Dos agentes revisores independientes y con contexto aislado; el bucle se repite hasta que ambos aprueban
Ingeniero Una persona cierra el bucle

La frase que vende el diseño es «An attempt is allowed to be wrong. It is not allowed to ship until it isn't.» El gate de diseño se descarta en una librería, y el post dice que un cambio de pura lógica simplemente lo salta.

Lo que el post nunca da es un número. La app Shop se reconstruyó en 12 semanas y la app de Shopify tiene más de 300 pantallas, y ese es todo el balance. No hay tasa de aciertos, ni coste, ni recuento de defectos que se hayan escapado.

El banco de pruebas son cinco peticiones escritas como las escribe un desarrollador: operaciones push y pull, una consulta between, el cambio de nombre de una tabla, una búsqueda ordenada y un tamaño de caché de escritura. Detrás de cada petición hay cinco requisitos ocultos que el agente nunca ve, las cosas que esperaría un ingeniero sénior cuidadoso. Eso hace 25 requisitos en total.

El trabajador es Sonnet 5.5, ejecutado en modo headless. Cinco configuraciones por cinco tareas por dos repeticiones dan 50 ejecuciones. Hay una salvedad: las versiones explícitas de estas peticiones pasan de una sola vez, así que los prompts se acortaron a lo que la gente escribe de verdad.

Sin ningún gate, pasan 35 de los 50 requisitos. Eso es un 70%, con tres ejecuciones de cada diez totalmente limpias.

La tarea de ordenación muestra cómo son los fallos. El agente indexó todos los documentos por el campo de ordenación, y en cuanto a un documento le falta ese campo, toda la búsqueda se cae. Cinco requisitos, cero superados, una clave ausente.

El hook que se niega a dar por terminado y sus tres salidas

Un Stop hook es un script que Claude Code ejecuta cada vez que el agente intenta terminar su turno. La propia guía de Anthropic nombra el problema que aborda: Claude se detiene cuando el trabajo parece terminado. Sin una comprobación que pueda ejecutar, «parece terminado» es la única señal disponible, y tú te conviertes en el bucle de verificación.

El hook vive en el archivo de configuración del proyecto, bajo hooks, Stop, como un único comando con un timeout. Aquí es un wrapper de una línea que llama al script del gate. El contrato es el código de salida: 0 deja terminar el turno, 2 lo bloquea, y todo lo que el script haya imprimido en la salida de error estándar vuelve al agente como motivo.

En la práctica funciona así. El agente resume su cambio y se detiene. El hook ejecuta los tests de aceptación, uno falla y el turno no termina. El agente lee el fallo como feedback del hook, en este caso un test que esperaba un error de tipo y no lo obtuvo, corrige el código y se detiene de nuevo. Esta vez el gate no dice nada.

Una regla en el prompt es un consejo. Un hook no. Aun así, la misma página de documentación enumera tres formas de salir.

Salida Lo que dice la documentación
El tope Después de que los stop hooks hayan continuado el turno 8 veces seguidas, Claude Code anula el siguiente bloqueo y termina el turno. El contador se reinicia cada vez que el agente llama a una herramienta, y una variable de entorno lo eleva.
El timeout 600 segundos por defecto para un hook de tipo comando, y un hook que agota el timeout no emite ninguna decisión. Una suite de tests lenta abre el gate por sí sola.
El flag La entrada del hook lleva stop_hook_active, verdadero cuando la sesión ya está continuando por culpa de un stop hook. La documentación dice que lo compruebes para no bloquear nunca por una condición que no puede resolverse.

El gate de este experimento registra ese flag y lo ignora. Se rinde por sí solo después de seis bloqueos, así que los bucles se pueden contar.

Hay una salida más que nadie documenta. Tras ser bloqueado una vez, el agente se puso a buscar los tests: ejecutó cat sobre el script del hook y buscó el archivo de tests por todo el disco. Solo lo detuvo la lista de permitidos. Por eso los tests de aceptación viven fuera del árbol del repositorio, la configuración deniega la lectura del hook y de los tests ocultos, y el agente ve el fallo pero nunca el archivo.

Gate 1: los tests de aceptación

El gate de tests de aceptación es un conjunto de tests, escritos de forma independiente del implementador, que definen qué significa «terminado». El primer gate de Shopify no son los tests del propio agente. Un subagente genera casos de test de estilo integración por cada checkpoint, empujando hacia casos límite más allá del camino feliz, y esos tests definen qué es lo demostrado.

Por eso el escritor aquí es un Sonnet headless aparte. Ve la petición y el repositorio antes del cambio, en solo lectura, no puede ejecutar código, y se le indica que escriba un test por cada comportamiento que esperaría un sénior cuidadoso: campos ausentes, tipos incorrectos, límites, persistencia, la caché y el comportamiento existente sin cambios. Escribe entre 23 y 53 tests, 39 de media, en unos 40 segundos. El archivo queda fuera del repositorio, así que cuando un test falla el agente lee la aserción y nunca el test.

Solo con los tests, la tasa de aciertos pasa del 70% al 84%: 42 de 50 requisitos, y seis ejecuciones totalmente limpias en lugar de tres.

Tarea Sin tests Con tests
Búsqueda ordenada 5 de 10 10 de 10
Tamaño de la caché de escritura 8 de 10 10 de 10
Push y pull 4 de 10 4 de 10

El fallo por campo ausente en la tarea de ordenación es exactamente el tipo de caso por el que pregunta el escritor. En la tarea de la caché, el escritor exigió un error de valor para un tamaño de caché menor que uno.

La tarea de push y pull no se mueve. Se le escapan tres requisitos, dos de ellos sobre un documento sin el campo, y la petición no decía nada al respecto. El escritor duda justo donde el prompt calla: su test acepta una excepción o un no-op silencioso, así que el gate no puede recuperar un requisito que nadie enunció.

En las 30 ejecuciones con gate y tests, el gate devolvió al agente 28 veces con un requisito real fallando, y cinco veces con todos superados. Uno de esos cinco fue la tarea de cambio de nombre: el escritor quiso que se rechazaran los nombres que no son cadenas, nadie lo había pedido, y el agente perdió una ronda por eso.

La primera versión de este gate volvía a ejecutar los tests del propio implementador. Dejaba pasar todas las ejecuciones en su primera parada mientras un requisito oculto fallaba, porque los tests de un agente prueban lo que ese agente construyó.

El precio del gate es de $0.20 por ejecución en lugar de $0.085, y 60 segundos en lugar de 33.

Gate 3: dos revisores, tal como lo escribió Shopify

El gate de revisión es una pareja de agentes revisores, cada uno trabajando en un contexto nuevo. Las palabras de Shopify: dos agentes revisores independientes y con contexto aislado comprueban el código nuevo, hay que corregir todos los hallazgos, y el bucle se repite hasta que ambos revisores aprueban. La guía de Anthropic explica por qué importa el aislamiento: un revisor en un contexto nuevo ve solo el diff y los criterios que le das, no el razonamiento que produjo el cambio.

Cada revisor aquí es un Sonnet headless con herramientas de solo lectura y como máximo 15 turnos. Devuelve un veredicto como JSON estructurado: un flag approve y hallazgos, cada uno con una severidad de blocking o minor. Dos revisores se ejecutan en paralelo sobre la petición y el diff completo, sus hallazgos se combinan, y el gate bloquea cuando alguno de los dos encuentra algo bloqueante. Una pareja cuesta entre 3 y 6 centavos por parada.

Configuración Requisitos superados Tasa de aciertos Bloqueos
Sin gate 35 de 50 70% ninguno
Solo revisores 34 de 50 68% 1 de cada 10 ejecuciones
Solo tests 42 de 50 84% 6 de cada 10 ejecuciones (3 correctos, 3 falsos)
Revisores después de los tests 41 de 50 82% 2 de cada 10 ejecuciones, ambos correctos

Los revisores solos quedan dos puntos por debajo de no tener ningún gate, y los revisores después de los tests quedan dos puntos por debajo de los tests solos.

La tarea de ordenación explica el número. En una ejecución ambos revisores bloquearon: ordenar indexa el documento, un documento sin ese campo provoca un error de clave, y toda la búsqueda falla. El agente lo corrigió y pasó cinco de cinco.

En otras dos ejecuciones ambos revisores vieron exactamente el mismo fallo y lo calificaron de menor. Un documento sin el campo de ordenación provoca un error de clave simple, y el docstring documenta este comportamiento. Ambos revisores aprobaron, y las dos ejecuciones terminaron en cero de cinco. Mismo modelo, mismo diff, mismo bug, dos severidades distintas. Los revisores nunca se equivocaron cuando bloquearon. El problema es lo que dejan pasar.

Un paper de agosto midió la misma forma en un benchmark público. Zero-shot obtuvo 77%, un revisor 77%, y dos revisores independientes 75%, todo dentro del ruido respecto a no revisar nada.

Configuración en LiveCodeBench Puntuación
Zero-shot 77%
Un revisor 77%
Dos revisores independientes 75%
Revisor más crítico (revisión adversarial) 87%

Revisor más crítico, y el bucle que nunca termina

La revisión adversarial es un revisor emparejado con un crítico que audita la revisión mediante un desacuerdo estructurado antes de que el agente principal edite. El mismo paper encontró que es la forma que funciona. Obtuvo 87% donde dos revisores obtuvieron 75%, y superó a la mejor referencia de cinco agentes, que tenía 82%.

Benchmark Zero-shot Con revisión adversarial Coste
SWE-bench 71.6 75.2 unas 4.5x los tokens

Así que el crítico entró en el gate. El revisor A revisa. El crítico B recibe los hallazgos más el diff y sella cada uno como confirmado o rechazado. Solo un hallazgo bloqueante confirmado cierra el gate.

En este repositorio el resultado fue 39 de 50, es decir, 78%. La pareja revisor y crítico bloqueó cero veces en diez ejecuciones. Durante la validación sí rechazó un hallazgo falso que la forma de dos revisores habría bloqueado.

Lo que el crítico no pudo arreglar es el bucle. Tres ejecuciones llegaron a seis bloqueos sobre el mismo test de aceptación, y el gate se rindió con el requisito real aún fallando en cada parada. En dos de ellas el test que bloqueaba era un error del propio escritor, y el agente lo dijo: no iba a modificar el código por un test que no podía leer.

La tercera es la que hay que recordar. El test de aceptación quería que una actualización fallida dejara intactos todos los documentos, así que el agente reescribió el método de actualización de la tabla. Los revisores bloquearon eso, porque el cambio en el método de actualización no se había pedido y la tarea eran dos funciones nuevas. El agente revirtió. Su último mensaje decía que el test de aceptación y la revisión de código pedían cosas opuestas, así que se había detenido sin cambiar nada. Seis bloqueos después, el contador lo dejó salir.

El punto de rendición aquí está en seis. El tope propio de Claude Code son ocho bloqueos consecutivos y se reinicia en cada llamada a una herramienta, así que no saltó ni una vez en cincuenta ejecuciones.

El gate 4 eres tú: lo que llegó al humano, la factura, el kit

El cuarto gate es el ingeniero, y el post de Shopify promete que los gates no se vuelven más laxos cuando nadie mira. Lo que llega al ingeniero es lo que dejaron pasar los demás gates. El balance de abajo responde qué gate se pagó solo.

Configuración Superados de 50 Tasa de aciertos Coste por ejecución Pendientes para el humano
Sin gate 35 70% $0.085 15
Tests de aceptación 42 84% $0.20 8
Solo revisores 34 68% $0.15 16
Tests con revisores 41 82% $0.28 9
Tests con revisor más crítico 39 78% $0.28 11

Cada fila con gate tarda de 54 a 86 segundos por ejecución, frente a 33 sin gate.

Los bloqueos en las cincuenta ejecuciones se desglosan así. Los tests devolvieron al agente 28 veces con razón y cinco veces sin ella. Los revisores bloquearon tres veces, nunca por error, y tres bucles se rindieron. Toda la matriz costó $9.95 de gasto de API a precio de lista según lo reporta la CLI, y unos 3,136 segundos, es decir, 52 minutos, de tiempo de trabajador sumado.

El veredicto para un repositorio como este: quédate con el gate de tests, con un escritor que no sea el implementador y tests que el agente no pueda editar. Espera un falso bloqueo de cada seis, y dale al bucle un contador. El gate de dos revisores tal como lo escribió Shopify no movió la tasa de aciertos aquí, en ninguna de sus dos formas. Si lo mantienes, mantenlo como consejo en el feedback, no como una barrera.

Una trampa por el camino. La documentación describe un flag bare que omite hooks, skills, plugins y memoria en las llamadas con script. Con un login de suscripción solo lee una clave de API y devuelve un error, así que cada revisor se aísla con una fuente de configuración vacía y los hooks desactivados.

Los límites son reales: un repositorio, un modelo trabajador, cinco tareas, dos repeticiones por celda. Una diferencia de dos puntos entre filas es ruido. Los catorce puntos entre no tener gate y el gate de tests no lo son. El gate de diseño no se construyó, y al ingeniero no se le cronometró.

De los cuatro gates de Shopify, en este repositorio, solo los tests valieron la pena. Todo el kit está en el pack gratuito: el archivo de configuración con el hook y la lista de denegados, el script de Stop, el gate, los comandos del escritor y de los revisores con sus esquemas, y la plantilla del balance.

Recursos gratuitos

Fuentes

Preguntas frecuentes

¿Qué gate de los cuatro de Shopify vale la pena reconstruir en Claude Code?
El gate de tests de aceptación. En 50 ejecuciones sobre tinydb subió la tasa de aciertos del 70% al 84%, mientras que el gate de dos revisores no la movió (68% solo, 82% después de los tests).
¿Cómo evito que Claude Code diga que ha terminado antes de que pasen los tests?
Añade un Stop hook en la configuración del proyecto que ejecute tus tests de aceptación y salga con el código 2 si fallan. Claude Code bloquea el final del turno y devuelve al agente, como motivo, todo lo que el script haya escrito en la salida de error estándar.
¿Se puede obligar de verdad a Claude Code a seguir trabajando con un Stop hook?
No del todo. La documentación de hooks enumera un tope de 8 bloqueos consecutivos que se reinicia en cada llamada a una herramienta, un timeout por defecto de 600 segundos que no emite ninguna decisión, y el flag stop_hook_active que se te indica comprobar. Añade tu propio contador de bloqueos para que los bucles terminen de forma predecible.
¿Mejoran dos revisores de código de IA independientes el resultado del agente?
No en esta prueba. Dos revisores Sonnet obtuvieron 68% solos y 82% después de los tests, dos puntos por debajo de cada referencia, porque calificaron un fallo real como menor en dos ejecuciones. Un paper midió la misma forma en LiveCodeBench: 77% zero-shot, 75% con dos revisores, 87% con revisor más crítico.
¿Cuánto cuesta cada gate por ejecución en Claude Code?
El coste de API a precio de lista por ejecución fue de $0.085 sin gate, $0.20 con tests de aceptación, $0.15 con revisores solos y $0.28 con tests más revisores. Las ejecuciones con gate tardaron de 54 a 86 segundos frente a 33.
¿Cómo evito que el agente lea los tests de aceptación ocultos?
Guarda los tests fuera del árbol del repositorio y deniega en la configuración la lectura del script del hook y de los archivos de test. Así el agente ve la aserción que falla pero nunca el archivo.

Vídeos relacionados