O APPROVE do modelo não é o merge
Por que o veredito de um agente reviewer é um sinal de entrada, e como transformar isso em política determinística.
Read in English Versão em Markdown

O problema em uma frase
Um modelo pode escrever "verdict": "APPROVE" e, três linhas abaixo, listar um bug de implementação com severidade alta dentro do escopo da tarefa. As duas coisas cabem no mesmo JSON. Se o pipeline lê só a primeira, o bug vai para o pull request com um carimbo de aprovado.
Não é defeito de um modelo específico. É o que acontece quando o mesmo texto carrega a evidência e a conclusão, e o sistema confia na conclusão.
A régua: evidência entra, veredito sai
A saída estruturada do reviewer no T25 tem dois campos: o veredito e uma lista de findings. Cada finding diz a severidade, a categoria, se está dentro do escopo do plano e o que está errado.
{
"verdict": "APPROVE",
"findings": [
{
"severity": "high",
"category": "implementation",
"inScope": true,
"description": "o retry reusa o token expirado"
}
]
}
O pipeline não usa o verdict que veio do modelo. Ele passa o relatório por evaluateReview(), que refaz a conta:
// src/pipeline/evaluate.ts (simplificado)
export function evaluateReview(report: ReviewReport): ReviewReport {
const blocking = blockingFindings(report.findings);
return {
verdict: blocking.length === 0 ? 'APPROVE' : 'REQUEST_CHANGES',
findings: report.findings,
};
}
blockingFindings() descarta o que está fora do escopo e mantém o resto. No exemplo acima, o resultado é REQUEST_CHANGES e a tarefa volta para implementação com o finding anexado. O modelo continua útil: ele achou o bug. Só não é ele quem decide o que o bug significa.
É exatamente isso que o cartão interativo na página inicial reproduz: você aperta APPROVE num review que já tem um finding em escopo, e a política recusa.
O bug que nos ensinou isso
Durante uma rodada de dogfood em agosto de 2026, um agente de QA terminou o relatório com a palavra "REPROVADO" e listou dois problemas bloqueadores. A tarefa andou para REVIEW como se tivesse passado.
A causa era simples e desconfortável. Reviewer e security tinham parsers de veredito. QA não tinha. O laço de QA verificava só se o processo do agente tinha terminado com sucesso, e um processo que termina bem escrevendo "reprovado" termina bem. Cada vez que uma tarefa passou de QA para review até ali, isso provava que o CLI não travou, não que o QA aprovou.
A correção foi dar ao QA o mesmo tratamento: veredito obrigatório PASS/FAIL em saída estruturada, validado pelo pipeline, com falha devolvendo a tarefa para IMPLEMENTING. A lição que ficou é mais geral que o bug:
Se um estágio não tem um veredito que o código lê, esse estágio não é um gate. É um log.
O que conta como bloqueante
Recalcular o veredito só funciona se a regra for explícita. A do T25 cabe em três linhas:
| Finding | Bloqueia? | Por quê |
|---|---|---|
Em escopo, com causa conhecida (implementation, spec, evidence, policy...) |
Sim | É trabalho que a tarefa prometeu entregar |
Fora do escopo do plano (inScope: false ou out-of-scope) |
Não | Vira registro, não retrabalho; senão o loop de correção vira reescrita |
Em escopo, categoria antiga sem causa (bug, style, other) |
Sim, e falha fechado | Vira unknown: bloqueia, mas a política não autoriza retrabalho no escuro |
A segunda linha importa tanto quanto a primeira. Um reviewer que acha um problema real em outro módulo não deveria gastar o orçamento da tarefa atual consertando o que ninguém pediu. O finding fica registrado; a tarefa segue.
Onde o humano entra
Tirar a decisão do modelo não significa passá-la inteira para o código. O T25 tem três pontos de parada humanos:
- Aprovação da spec. Sempre. A spec só vira plano quando tem critérios de aceite parseáveis, o checklist obrigatório está completo e não há comentário aberto.
- Aprovação do plano. Depende do risco da tarefa. Nos padrões, risco baixo não pede aprovação, risco médio pede aprovação do plano, risco alto pede plano e pull request, e crítico exige humano sempre. Cada projeto pode ligar ou desligar o gate de plano.
- O merge. Sempre humano. A fábrica abre o pull request, verifica os checks obrigatórios e para. Quem responde pela main aperta o botão.
A política decide o que chega até você. Você decide o que entra.
Como aplicar isso no seu pipeline, com ou sem o T25
Se você já roda agentes de review, dá para adotar a ideia hoje:
- Peça saída estruturada. Findings com severidade, categoria e escopo, em JSON, não um parágrafo.
- Ignore o veredito do modelo na decisão. Guarde para auditoria, mas decida com uma função sua sobre os findings.
- Falhe fechado. Saída que não parseia não é aprovação. É erro, com o texto original anexado.
- Dê veredito a todo estágio que você chama de gate. QA incluído. Se o código não lê, não é gate.
- Separe escopo. Finding fora do escopo vira issue, não retrabalho.
Perguntas frequentes
Por que não confiar no APPROVE se o modelo é bom?
Porque o problema não é a qualidade do modelo, é quem assina. Mesmo um reviewer excelente pode listar um problema sério e ainda assim concluir que está tudo bem. Recalcular a partir dos findings custa uma função e elimina essa contradição.
O T25 faz merge automático quando o review aprova?
Não. Um review aprovado leva a tarefa até o pull request. O merge é sempre uma ação humana, depois dos checks obrigatórios do repositório.
Isso não deixa o pipeline mais lento?
Deixa a tarefa voltar mais vezes para implementação quando há finding em escopo, que é o comportamento desejado. O que fica mais rápido é a sua revisão, porque o que chega ao pull request já passou por uma régua que não depende do humor do modelo.
Funciona com qualquer CLI de agente?
Sim. O veredito é recalculado a partir da saída estruturada, então não importa se quem revisou foi Claude Code, Codex ou outro CLI da lista de fallback.