Code Plain
Todos os artigos

Contei o loop do dcode e achei o critério de parada

Dois terços de internal/loop são teste, e o arquivo que decide o que é pronto protege os próprios testes de quem está preso no loop.

Diagrama isométrico: um anel de módulos ligados por setas que circulam de volta ao início, com um módulo verde que se desprende e sai do ciclo.
A volta se fecha sozinha. O que decide o resultado é onde está a saída, e se alguém consegue observar que ela foi alcançada.

Fui contar as linhas de internal/loop no dcode esperando descobrir onde estava a complexidade, e o que encontrei foi uma proporção que eu não tinha reparado enquanto escrevia: são 2.580 linhas de implementação contra 5.541 de teste, seis arquivos de código contra vinte e três de teste, dois para um numa parte do sistema que, no papel, só faz uma coisa muito simples — perceber, agir, observar o resultado e decidir de novo.

A parte difícil, descobri relendo o que eu mesmo tinha escrito, é a que decide que a volta terminou.

O arquivo que decide o que é pronto

O done.go tem 415 linhas e carrega um comentário que resume o problema inteiro melhor do que eu conseguiria reescrever aqui: critério de parada é uma condição que dá para checar, e prosa não qualifica. "Os testes passam" é fato. "O código está limpo" não é, e um critério julgado pelo modelo devolve ao modelo a decisão sobre estar pronto — agora com vinte voltas gastas no caminho até lá.

É a pergunta que quase ninguém responde de propósito, e por isso ela acaba respondida por acidente, com o loop parando quando o modelo acha que terminou. Escrever o teste antes do código resolve isso por uma razão que não é a que costumam dar nos textos sobre TDD: o teste vermelho é uma condição de parada que a máquina consegue checar sozinha, então a volta tem para onde ir e sabe quando chegou. Sem isso, "pronto" é opinião, e é sempre a opinião de quem tem incentivo para terminar.

Por que os testes precisam ser protegidos de quem está no loop

A parte que eu achei mais desconfortável de reler está na definição do conjunto de critérios, onde existe uma lista de caminhos protegidos — os arquivos de teste, tipicamente. O comentário explica por quê, e a explicação é sobre incentivo e não sobre confiança: sem essa proteção o resto é teatro, porque um agente que não consegue sair do loop descobre que o caminho mais curto para fora é enfraquecer aquilo que está medindo.

E aí vem a frase que eu levei a sério o suficiente para implementar: um teste falso é estritamente pior do que um relatório falso, porque o relatório falso é descoberto rodando alguma coisa, enquanto o teste falso fica no repositório fingindo ser cobertura para sempre.

Não é proibição — às vezes consertar o teste é o trabalho. É visibilidade, e é toda a diferença entre um loop que garante qualidade e um que fabrica a aparência dela.

Cada volta custa a conversa inteira

Modelo de linguagem não tem memória entre chamadas, e o que parece diálogo contínuo é o histórico inteiro sendo reenviado do começo a cada volta. A consequência é direta e pouco intuitiva: o que custa é o número de voltas, e não o tamanho da tarefa.

Isso reorganiza o que conta como código bom dentro de um agente. Chamadas independentes vão juntas numa volta só, porque ler três arquivos em três rodadas custa três conversas inteiras enquanto ler os três de uma vez custa uma. Processo longo vai para segundo plano e é esperado uma vez, em vez de consultado de dez em dez segundos — consultar repetidamente é o antipadrão mais caro que existe aqui, já que cada consulta paga o histórico completo só para descobrir que ainda não terminou. E verificação que já passou não se repete até o código mudar.

Não é economia de centavos. É a diferença entre um agente que termina a tarefa e um que consome o orçamento confirmando que está tudo bem.

O que atravessa a volta e o que evapora nela

Tudo que importa e não está escrito em disco vai embora na compactação, no fim da sessão, ou naquele --resume que não restaurou o que você achou que restauraria. Daí a assimetria que vale internalizar: o que está na janela de contexto é volátil e caro, e o que está em arquivo é durável e barato.

Decisão de arquitetura, contrato de módulo, critério de aceite e o motivo de uma escolha pertencem ao disco, onde ficam legíveis para o agente na próxima sessão, para outra pessoa no próximo mês e para você quando esquecer. Uma especificação não é burocracia para agente; é a parte do estado que sobrevive à volta.

O modo de falha do loop de agente é insistir

Uma chamada é bloqueada e o agente tenta de novo, igual. Bloqueia outra vez e ele tenta por um caminho ligeiramente diferente. Um teste falha e ele mexe no teste em vez do código, que é exatamente o comportamento que a lista de caminhos protegidos existe para tornar visível. A busca não acha nada e ele repete com outra palavra, e outra, e outra.

Todo loop precisa de um limite que não dependa de sucesso, porque três tentativas na mesma direção são sintoma de que o caminho está errado, e a saída certa é parar, dizer o que foi tentado e devolver a decisão para quem tem contexto para tomá-la. Um agente que sabe parar vale mais do que um que sabe insistir.

A evidência precisa estar no mesmo nível da afirmação

A última volta é a que mais se falsifica sozinha, porque é tentador conferir uma string no HTML e declarar que a página funciona. O grep encontrando o texto certo prova que o texto está no arquivo, e não prova que a página renderiza, que o CSS carregou ou que o link é clicável.

Abrir no navegador e olhar custa uma volta a mais e é a única que fecha a volta de verdade. O princípio vale em toda parte, e é o mesmo que aparece nas 5.541 linhas de teste: se você afirma que funciona, a prova é funcionando.

O que melhora sozinho e o que não

O modelo escolhe o que fazer dentro da volta, e essa parte melhora a cada versão que alguém lança, sem que você faça nada. O loop decide quantas voltas existem, o que cada uma carrega e quando elas acabam.

Essa segunda parte só melhora se alguém projetar, e é por isso que os dois terços de teste estão ali. A volta é fácil de escrever. Saber que ela terminou é que custa.

Responder