AIDive

Os 4 gates da Shopify no Claude Code: só os testes valem

Por AIDive · Publicado em

Agentes de códigoAutomação e workflows

Introdução: o gate que recusa o pronto

Um gate, no sentido que a Shopify usa, é uma verificação que uma mudança escrita por IA precisa passar antes de seguir adiante. Reconstruído no Claude Code, só o primeiro valeu a pena: os testes de aceitação. A Shopify publicou quatro gates e nenhum número, então este experimento os monta numa repo real, roda cinquenta pedidos com cada gate desligado por vez e conta o que cada um pegou e quanto custou.

O Helix foi aberto ao público em 21 de setembro de 2026. Ele faz cada checkpoint passar por quatro gates em ordem: testes, revisão de design, dois revisores e um engenheiro. O post não dá nenhuma taxa de aprovação. Os revisores são a parte que a maioria copia, e nestas cinquenta execuções a taxa de aprovação só se mexeu quando os testes estavam no loop. Os revisores não acrescentaram nada, por mais ou menos o triplo do custo.

Duas outras descobertas saem do mesmo bench. Um hook que supostamente não pode ser contornado tem três saídas na própria documentação. E quando os testes e os revisores discordaram, o agente fez algo que ninguém tinha planejado. A contagem começa pelo caso sem nenhum gate.

Quatro gates, zero números, um bench

A repo do bench é o tinydb, um banco de documentos de verdade, com 226 testes verdes no commit usado.

O Helix é a ferramenta interna da Shopify para levar os apps mobile de volta ao código nativo. O post enuncia a regra sem rodeios: cada checkpoint passa por quatro gates, em ordem.

Gate O que faz no post da Shopify
Comportamento Roda testes em que todo caso relevante precisa passar
Design Pede a um segundo modelo que atue como um revisor de design perfeccionista
Revisão Dois agentes revisores independentes, com contexto isolado; o loop se repete até os dois aprovarem
Engenheiro Uma pessoa fecha o loop

A frase que vende o desenho é "An attempt is allowed to be wrong. It is not allowed to ship until it isn't." O gate de design sai de cena numa biblioteca, e o post diz que uma mudança só de lógica simplesmente o pula.

O que o post nunca dá é um número. O app Shop foi reconstruído em 12 semanas e o app Shopify tem mais de 300 telas, e esse é todo o balanço. Não há taxa de aprovação, nem custo, nem contagem de defeitos que escaparam.

O bench são cinco pedidos escritos do jeito que um desenvolvedor escreve: operações de push e pull, uma consulta between, a renomeação de uma tabela, uma busca ordenada e o tamanho de um cache de escrita. Por trás de cada pedido há cinco requisitos ocultos que o agente nunca vê, as coisas que um engenheiro sênior cuidadoso esperaria. Isso dá 25 requisitos no total.

O worker é o Sonnet 5.5, rodando em modo headless. Cinco configurações vezes cinco tarefas vezes duas repetições dão 50 execuções. Uma ressalva: as versões explícitas desses pedidos passam de primeira, então os prompts foram encurtados para o que as pessoas realmente escrevem.

Sem nenhum gate, 35 dos 50 requisitos passam. São 70%, com três execuções em dez totalmente limpas.

A tarefa de ordenação mostra como são as falhas. O agente indexou cada documento pelo campo de ordenação, e assim que um documento não tem esse campo a busca inteira quebra. Cinco requisitos, zero aprovados, uma chave ausente.

O hook que recusa o pronto, e suas três saídas

Um Stop hook é um script que o Claude Code executa toda vez que o agente tenta encerrar o turno. O guia da própria Anthropic nomeia o problema que ele resolve: o Claude para quando o trabalho parece pronto. Sem uma verificação que ele possa rodar, "parece pronto" é o único sinal disponível, e você vira o loop de verificação.

O hook fica no arquivo de configurações do projeto, em hooks, Stop, como um comando com um timeout. Aqui é um wrapper de uma linha que chama o script do gate. O contrato é o código de saída: 0 deixa o turno terminar, 2 o bloqueia, e tudo o que o script imprimiu no erro padrão volta ao agente como motivo.

Na prática funciona assim. O agente resume a mudança e para. O hook roda os testes de aceitação, um falha, e o turno não termina. O agente lê a falha como feedback do hook, neste caso um teste que esperava um erro de tipo e não o recebeu, corrige o código e para de novo. Desta vez o gate fica quieto.

Uma regra no prompt é um conselho. Um hook não é. Mesmo assim, a mesma página de documentação lista três maneiras de escapar.

Saída O que a documentação diz
O limite Depois que os stop hooks continuaram o turno 8 vezes seguidas, o Claude Code ignora o próximo bloqueio e encerra o turno. A contagem zera cada vez que o agente chama uma ferramenta, e uma variável de ambiente a aumenta.
O timeout 600 segundos por padrão para um command hook, e um hook que estoura o timeout não produz nenhuma decisão. Uma suíte de testes lenta abre o gate sozinha.
A flag A entrada do hook traz stop_hook_active, verdadeira quando a sessão já está continuando por causa de um stop hook. A documentação manda verificá-la para nunca bloquear por uma condição que não pode se resolver.

O gate aqui registra essa flag e a ignora. Ele desiste sozinho depois de seis bloqueios, para que os loops continuem contáveis.

Existe ainda uma saída que ninguém documenta. Depois de ser bloqueado uma vez, o agente foi procurar os testes: rodou cat no script do hook e vasculhou o disco inteiro atrás do arquivo de testes. Só a lista de permissões o impediu. Por isso os testes de aceitação ficam fora da árvore do repositório, as configurações negam a leitura do hook e dos testes ocultos, e o agente vê a falha mas nunca o arquivo.

Gate 1: os testes de aceitação

O gate de testes de aceitação é um conjunto de testes, escritos de forma independente do implementador, que definem o que significa "pronto". O primeiro gate da Shopify não são os testes do próprio agente. Um subagente gera casos de teste no estilo de integração para cada checkpoint, forçando casos de borda além do caminho feliz, e esses testes definem o que é comprovado.

O escritor aqui é, portanto, um Sonnet headless separado. Ele vê o pedido e a repo antes da mudança, só em leitura, não pode executar código e recebe a instrução de escrever um teste por comportamento que um sênior cuidadoso esperaria: campos ausentes, tipos errados, limites, persistência, o cache e o comportamento existente inalterado. Ele escreve entre 23 e 53 testes, 39 em média, em cerca de 40 segundos. O arquivo cai fora da repo, então, quando um teste falha, o agente lê a asserção e nunca o teste.

Só os testes levam a taxa de aprovação de 70% para 84%: 42 de 50 requisitos, e seis execuções totalmente limpas em vez de três.

Tarefa Sem testes Com testes
Busca ordenada 5 de 10 10 de 10
Tamanho do cache de escrita 8 de 10 10 de 10
Push e pull 4 de 10 4 de 10

A quebra por campo ausente na tarefa de ordenação é exatamente o tipo de caso que o escritor pergunta. Na tarefa do cache, o escritor exigiu um value error para um tamanho de cache abaixo de um.

A tarefa de push e pull não se mexe. Ela erra três requisitos, dois deles sobre um documento sem o campo, e o pedido nunca disse nada sobre isso. O escritor se esquiva exatamente onde o prompt é omisso: o teste dele aceita um raise ou um no-op silencioso, então o gate não consegue recuperar um requisito que ninguém declarou.

Nas 30 execuções com gate de testes, o gate mandou o agente de volta 28 vezes com um requisito real falhando, e cinco vezes com todos passando. Uma dessas cinco foi a tarefa de renomeação: o escritor queria rejeitar nomes que não são strings, ninguém tinha pedido isso, e o agente perdeu uma rodada por causa disso.

A primeira versão deste gate reexecutava os testes do próprio implementador. Ela liberava toda execução na primeira parada enquanto um requisito oculto falhava, porque os testes de um agente testam o que ele construiu.

O preço do gate é $0.20 por execução em vez de $0.085, e 60 segundos em vez de 33.

Gate 3: dois revisores, como a Shopify escreveu

O gate de revisão é um par de agentes revisores, cada um trabalhando num contexto novo. Nas palavras da Shopify: dois agentes revisores independentes, com contexto isolado, checam o código novo, todo achado precisa ser corrigido e o loop se repete até os dois revisores aprovarem. O guia da Anthropic explica por que o isolamento importa: um revisor num contexto novo vê só o diff e os critérios que você dá, não o raciocínio que produziu a mudança.

Cada revisor aqui é um Sonnet headless com ferramentas só de leitura e no máximo 15 turnos. Ele devolve um veredito em JSON estruturado: uma flag approve e achados, cada um com severidade blocking ou minor. Dois revisores rodam em paralelo sobre o pedido e o diff completo, seus achados são combinados, e o gate bloqueia quando qualquer um encontra algo blocking. Um par custa entre 3 e 6 centavos por parada.

Configuração Requisitos aprovados Taxa de aprovação Bloqueios
Sem gate 35 de 50 70% nenhum
Só revisores 34 de 50 68% 1 em 10 execuções
Só testes 42 de 50 84% 6 em 10 execuções (3 certos, 3 falsos)
Revisores depois dos testes 41 de 50 82% 2 em 10 execuções, ambos certos

Só os revisores ficam dois pontos abaixo de nenhum gate, e os revisores depois dos testes ficam dois pontos abaixo dos testes sozinhos.

A tarefa de ordenação explica o número. Numa execução os dois revisores bloquearam: a ordenação indexa o documento, um documento sem esse campo gera um key error, e a busca inteira falha. O agente corrigiu e passou cinco de cinco.

Em outras duas execuções os dois revisores viram exatamente a mesma quebra e a classificaram como minor. Um documento sem o campo de ordenação gera um key error seco, e a docstring documenta isso. Os dois revisores aprovaram, e as duas execuções terminaram em zero de cinco. Mesmo modelo, mesmo diff, mesmo bug, duas severidades diferentes. Os revisores nunca erraram quando bloquearam. O problema é o que eles deixam passar.

Um paper de agosto mediu o mesmo padrão num benchmark público. Zero-shot marcou 77%, um revisor 77%, e dois revisores independentes 75%, tudo dentro do ruído de não ter revisão nenhuma.

Configuração no LiveCodeBench Pontuação
Zero-shot 77%
Um revisor 77%
Dois revisores independentes 75%
Revisor mais crítico (revisão adversarial) 87%

Revisor mais crítico, e o loop que nunca acaba

A revisão adversarial é um revisor emparelhado com um crítico que audita a revisão por meio de discordância estruturada antes de o agente principal editar. O mesmo paper concluiu que esse é o formato que funciona. Ele marcou 87% onde dois revisores marcaram 75%, e superou a melhor baseline de cinco agentes, com 82%.

Benchmark Zero-shot Com revisão adversarial Custo
SWE-bench 71.6 75.2 cerca de 4.5x os tokens

Então o crítico entrou no gate. O revisor A revisa. O crítico B recebe os achados mais o diff e carimba cada um como mantido ou rejeitado. Só um achado blocking mantido fecha o gate.

Nesta repo o resultado foi 39 de 50, ou 78%. O par revisor e crítico bloqueou zero vezes em dez execuções. Durante a validação, ele rejeitou um achado falso que o formato de dois revisores teria bloqueado.

O que o crítico não conseguiu consertar é o loop. Três execuções chegaram a seis bloqueios no mesmo teste de aceitação, e o gate desistiu com o requisito real ainda falhando a cada parada. Em duas delas o teste que bloqueava era erro do próprio escritor, e o agente disse isso: ele não ia modificar o código por causa de um teste que não podia ler.

A terceira é a que vale lembrar. O teste de aceitação queria que um update com falha deixasse todos os documentos intactos, então o agente reescreveu o método de update da tabela. Os revisores bloquearam isso, porque a mudança no método de update não tinha sido pedida e a tarefa eram duas funções novas. O agente reverteu. A última mensagem dele dizia que o teste de aceitação e a revisão de código pediam coisas opostas, então ele tinha parado sem mudar nada. Seis bloqueios depois, o contador o deixou sair.

O ponto de desistência aqui fica em seis. O limite do próprio Claude Code é de oito bloqueios consecutivos e zera a cada chamada de ferramenta, então ele não disparou nenhuma vez em cinquenta execuções.

O gate 4 é você: o que chegou ao humano, a conta, o kit

O quarto gate é o engenheiro, e o post da Shopify promete que os gates não ficam mais frouxos quando ninguém está olhando. O que chega ao engenheiro é o que os outros gates deixaram passar. O balanço abaixo responde qual gate se pagou.

Configuração Aprovados de 50 Taxa de aprovação Custo por execução Sobra para o humano
Sem gate 35 70% $0.085 15
Testes de aceitação 42 84% $0.20 8
Só revisores 34 68% $0.15 16
Testes com revisores 41 82% $0.28 9
Testes com revisor mais crítico 39 78% $0.28 11

Toda linha com gate leva de 54 a 86 segundos por execução, contra 33 sem gate.

Os bloqueios nas cinquenta execuções se dividem assim. Os testes mandaram o agente de volta 28 vezes com razão e cinco vezes sem razão. Os revisores bloquearam três vezes, nunca sem razão, e três loops desistiram. A matriz inteira custou $9.95 de gasto de API a preço de tabela, como a CLI informa, e cerca de 3136 s, ou 52 minutos, de tempo somado dos workers.

O veredito para uma repo como esta: mantenha o gate de testes, com um escritor que não seja o implementador e testes que o agente não possa editar. Espere cerca de um falso bloqueio em seis, e dê ao loop um contador. O gate de dois revisores, como a Shopify o escreveu, não mexeu na taxa de aprovação aqui, em nenhum dos dois formatos. Se você o mantiver, mantenha como conselho no feedback, não como barreira.

Uma armadilha no caminho. A documentação descreve uma flag bare que pula hooks, skills, plugins e memória em chamadas por script. Num login de assinatura ela só lê uma chave de API e devolve um erro, então cada revisor é isolado de outro jeito, com uma fonte de configurações vazia e hooks desativados.

Os limites são reais: uma repo, um modelo worker, cinco tarefas, duas repetições por célula. Uma diferença de dois pontos entre linhas é ruído. Os catorze pontos entre nenhum gate e o gate de testes não são. O gate de design não foi construído, e o engenheiro não foi cronometrado.

Dos quatro gates da Shopify, nesta repo, só os testes se pagaram. O kit inteiro está no pacote grátis: o arquivo de configurações com o hook e a lista de negação, o script de stop, o gate, os comandos do escritor e dos revisores com seus schemas e o modelo do balanço.

Recursos gratuitos

Fontes

Perguntas frequentes

Qual dos quatro gates de IA da Shopify vale a pena reconstruir no Claude Code?
O gate de testes de aceitação. Em 50 execuções no tinydb, ele elevou a taxa de aprovação de 70% para 84%, enquanto o gate de dois revisores não a mexeu (68% sozinho, 82% depois dos testes).
Como impeço o Claude Code de dizer que terminou antes de os testes passarem?
Adicione um Stop hook nas configurações do projeto que rode seus testes de aceitação e saia com código 2 em caso de falha. O Claude Code bloqueia o fim do turno e devolve ao agente, como motivo, tudo o que o script escreveu no erro padrão.
Dá mesmo para obrigar o Claude Code a continuar trabalhando com um Stop hook?
Não totalmente. A documentação dos hooks lista um limite de 8 bloqueios consecutivos que zera a cada chamada de ferramenta, um timeout padrão de 600 segundos que não produz nenhuma decisão e a flag stop_hook_active que você deve verificar. Adicione seu próprio contador de bloqueios para que os loops terminem de forma previsível.
Dois revisores de código de IA independentes melhoram a saída do agente?
Não neste teste. Dois revisores Sonnet marcaram 68% sozinhos e 82% depois dos testes, dois pontos abaixo de cada baseline, porque classificaram uma quebra real como minor em duas execuções. Um paper mediu o mesmo padrão no LiveCodeBench: 77% zero-shot, 75% com dois revisores, 87% com revisor mais crítico.
Quanto cada gate custa por execução no Claude Code?
O custo por execução a preço de tabela da API foi $0.085 sem gate, $0.20 com testes de aceitação, $0.15 só com revisores e $0.28 com testes mais revisores. As execuções com gate levaram de 54 a 86 segundos contra 33.
Como impeço o agente de ler os testes de aceitação ocultos?
Guarde os testes fora da árvore do repositório e negue nas configurações a leitura do script do hook e dos arquivos de teste. O agente então vê a asserção que falhou, mas nunca o arquivo.

Vídeos relacionados