Quase todo time que trabalha com agentes já passou por isso: você escreve uma spec que parece razoável, o agente executa, e o que volta não é bem o que você pediu. Não é código quebrado, é código sutilmente errado. Um campo a mais, um fluxo que ninguém autorizou, uma decisão arquitetural tomada no meio do caminho. Aí começa o retrabalho: revisar, apontar, pedir ajuste, e esperar que agora vá.

O reflexo é culpar o modelo. Na maior parte das vezes, não foi ele. Uma spec para agente não é descrição, é restrição. É o menor conjunto de limites que permite executar sem reinterpretação. A maior parte das falhas do tipo “o agente fez coisa estranha” é falha de spec, não de modelo. Este post é sobre o que uma spec precisa ter, o que ela precisa deixar de fora, e como saber que ela está pronta antes de rodar o agente.

Por que a spec narrativa falha

A spec narrativa é o formato mais comum, e o mais perigoso. Ela descreve a feature em parágrafos, conta a história do usuário, explica o contexto de negócio, e em algum lugar no meio disso diz o que precisa ser feito. Em contexto humano, funciona. Um sênior lê, preenche as lacunas com bom senso e segue. Um agente lê tudo como sinal e tenta honrar tudo igualmente.

O problema é que prosa e restrição competem pelo mesmo espaço. Quando você escreve “seria bom ter um feedback visual”, o agente trata como requisito. Quando você escreve “idealmente sem adicionar dependências”, ele trata como preferência opcional. Não há um jeito confiável de dizer “isso é obrigatório, isso é decoração” sem marcar explicitamente.

O outro problema é que narrativa esconde a decisão real. Muitas specs parecem completas até você tentar responder a pergunta: qual decisão concreta essa spec está pedindo?. Se a resposta exige reler três parágrafos e inferir, já está ambígua para um agente.

O que uma spec mínima precisa conter

Vou listar o núcleo, sem cerimônia. Se um item está faltando, a spec vai gerar retrabalho com probabilidade alta.

Decisão concreta. Uma frase, no imperativo, dizendo o que o agente vai entregar ao fim da execução. Não “melhorar o fluxo de login”, mas “adicionar verificação por e-mail no cadastro de novos usuários usando o provedor já configurado”. A diferença é que a segunda versão não admite duas interpretações razoáveis.

Acceptance criteria verificáveis. Cada AC precisa ter um jeito mecânico de checar se foi atendido. Given/When/Then funciona bem, SHALL/DEVE funciona bem, texto solto verificável manualmente também resolve. O que não funciona é AC do tipo “o fluxo deve ficar intuitivo”. Intuitivo para quem, medido como, verificado por qual teste?

Non-goals explícitos. Aqui está a parte que mais gente esquece. Non-goal é o que a spec está conscientemente deixando de fora. “Não adicionar novos endpoints”, “não mexer no schema de users”, “não alterar o fluxo de logout” são non-goals. Sem essa camada, o agente decide o escopo sozinho, e quase sempre decide a mais.

Target files. Os arquivos que vão ser tocados, nomeados. src/auth/email-verification.ts, src/auth/routes.ts, tests/auth/email-verification.test.ts. Não “o módulo de auth”, não “onde fizer sentido”. Quando a lista de arquivos não é óbvia, isso é sinal de que a spec ainda está abstrata demais e precisa voltar um passo.

Restrições técnicas críticas. Tudo que vai levar a revisão se for ignorado: versão de dependência, padrão de erro que o projeto segue, contrato de tipo, limites de performance, requisito de compatibilidade. Não é o lugar para listar o style guide inteiro, que já mora na constituição do projeto. É o lugar para restrições específicas daquela tarefa.

O que deixar de fora

Tão importante quanto o que entra é o que não entra. Uma spec enxuta evita:

  • Histórico de discussão. Debate sobre alternativas vira ruído. Se uma alternativa foi descartada e a razão importa, vira um ADR (architecture decision record) curto. Se não importa, some.
  • Justificativa de produto longa. O agente não precisa da motivação de negócio para executar. Precisa da decisão. Motivação longa dilui o sinal.
  • Exemplos de código tentativos. Snippet “para dar uma ideia” quase sempre vira snippet que o agente copia literalmente, inclusive os erros.
  • Instruções genéricas do projeto. Convenção de nomes, estilo de commit, regra de teste já estão na constituição. Repetir polui o prompt e aumenta chance de contradição silenciosa com o documento canônico.
  • Preferências decorativas. “Seria legal se”, “idealmente”, “se possível”. Ou é requisito, ou não está na spec.

A regra operacional é: cada linha da spec deveria mudar o que o agente faz. Se não muda, não entra. É a mesma lógica de orçamento de contexto do post anterior, aplicada ao artefato da spec em vez do prompt como um todo.

O teste operacional: two agents, same output

Existe um teste mental que vale mais que checklist comprido. Antes de rodar, pergunte:

Dois agentes diferentes, com a mesma spec e o mesmo contexto, produziriam saídas funcionalmente equivalentes?

Se a resposta é não, a spec está subespecificada. Alguma decisão ainda mora na cabeça de quem escreveu. Em vez de mandar o agente executar, vale gastar cinco minutos deixando essa decisão explícita.

Esse teste captura o que checklist tradicional não captura. Uma spec pode ter todos os campos preenchidos e ainda falhar no teste, porque os campos estão vagos. Pode ter muito menos que o formato completo e passar, porque cada campo é preciso.

“Funcionalmente equivalente” aqui quer dizer que o comportamento observável é o mesmo. Nomes internos diferentes, estrutura de diretório diferente, ordem de parâmetros diferente, tudo isso é aceitável. O que não é aceitável é um agente tratar um campo como obrigatório e outro como opcional.

Falhas comuns de spec

Quatro padrões aparecem com frequência suficiente para valer nome próprio. Todos são editáveis em minutos quando você reconhece o padrão.

Spec narrativa em vez de restritiva. A spec conta uma história em vez de definir limites. Correção: extrair a decisão em uma frase imperativa no topo, mover o resto para contexto opcional ou descartar.

AC sem verificabilidade. “Experiência fluida”, “comportamento esperado”, “funciona bem”. Correção: para cada AC, escrever a checagem concreta. Se a checagem não existe, o AC não existe.

Non-goals implícitos. O autor sabe o que está fora de escopo, mas não escreveu. Correção: listar três a cinco non-goals antes de enviar. Quase sempre o primeiro que aparece na cabeça é o mais importante.

Decisão escondida em prosa. A spec parece completa, mas a decisão real está diluída em dois parágrafos. Correção: se você não consegue apontar a frase que contém a decisão, a decisão não está na spec.

Um exemplo antes e depois

Imagine que a tarefa é adicionar verificação de e-mail no cadastro. Uma spec comum ficaria parecida com isso:

Precisamos melhorar a segurança do cadastro. Hoje o usuário consegue
criar uma conta com qualquer e-mail, inclusive de domínios temporários
ou digitado errado, o que tem gerado problemas de suporte. Seria bom
ter uma verificação por e-mail, idealmente usando algo que a gente já
tenha configurado. O fluxo deve ser intuitivo e não atrapalhar a
conversão. Se possível, considerar também o caso do OAuth. Vale
avaliar se faz sentido bloquear funcionalidades antes da verificação.

Parece razoável. É péssima para um agente. A decisão está escondida em “seria bom ter”, tem três coisas opcionais sem marca clara, dois gatilhos para o agente expandir escopo (“considerar OAuth”, “avaliar bloqueio”), nenhum AC verificável, e nenhum arquivo-alvo.

A mesma tarefa reescrita como restrição:

decisao: >
  Adicionar verificação por e-mail ao cadastro de novos usuários
  usando o provedor de e-mail já configurado em src/email/provider.ts.

acceptance_criteria:
  - Dado um cadastro com e-mail válido, quando a conta é criada,
    então um e-mail de verificação com token é enviado.
  - Dado um token de verificação válido, quando o endpoint de
    confirmação é chamado, então o usuário é marcado como verificado.
  - Dado um token expirado ou inválido, quando o endpoint de
    confirmação é chamado, então a resposta é 400 com erro genérico.
  - Usuários existentes na base continuam funcionando sem verificação.

non_goals:
  - Não alterar fluxo de OAuth.
  - Não bloquear acesso a funcionalidades de usuários não verificados.
  - Não adicionar reenvio de verificação nesta story.
  - Não mexer no fluxo de logout ou recuperação de senha.

target_files:
  - src/auth/email-verification.ts (novo)
  - src/auth/routes.ts (adicionar rota de confirmação)
  - src/users/user.model.ts (adicionar campo verified_at)
  - tests/auth/email-verification.test.ts (novo)

restricoes:
  - Token precisa expirar em 24h.
  - Mensagens de erro seguem o padrão de src/errors/http-errors.ts.
  - Sem adicionar dependências novas.

A segunda versão é mais curta, mais chata de ler, e muito menos ambígua. Dois agentes diferentes entregariam resultado equivalente. A primeira versão entregaria duas features diferentes dependendo do humor do modelo.

Repare que esse formato não depende de DSL de nenhum produto específico. É texto estruturado. Pode virar YAML, markdown, campo em uma ferramenta de story, o que funcionar no seu fluxo.

Spec é habilidade de edição, não de escrita

Escrever spec longa é fácil. Escrever spec enxuta é difícil pela mesma razão que um parecer curto é difícil: cada palavra precisa ter função. Na prática, uma boa spec quase sempre passa por duas ou três versões, e cada versão remove coisa.

A primeira versão tende a misturar tudo. A segunda versão separa decisão de contexto e marca os non-goals. A terceira versão corta metade do que não muda o comportamento. Quando você começa a remover e o teste dos dois agentes continua passando, a spec está pronta.

Cinco minutos editando uma spec evitam horas revisando uma PR. O investimento é desproporcionalmente bom.

Fechando

Contexto e spec são duas dimensões diferentes do mesmo problema. Contexto responde “o que o agente vê”. Spec responde “o que o agente precisa decidir”. As duas juntas são a base do trabalho com agentes. Prompt inflado com spec ambígua é a combinação que mais gera retrabalho. Contexto mínimo com spec restritiva é a combinação que mais se paga.

Se der para levar uma coisa deste post: da próxima vez que você for rodar um agente, antes de apertar enter, leia a spec e pergunte em voz alta “dois agentes produziriam a mesma saída?”. Se a resposta não é um sim direto, edite a spec primeiro. É o movimento com melhor retorno por minuto investido que existe no fluxo com agentes.

Referências

Addy Osmani — How to Write a Good Spec for AI Agents.

BMAD Method — referência prática de handoffs guiados por spec.