# Endpoints de dry run que mantêm agentes de programação com IA honestos

Agentes de programação com IA não deveriam descobrir se uma alteração é válida fazendo a própria alteração. Esse é um design de API preguiçoso, e a autonomia torna o custo visível rapidamente. Um agente pode tentar de novo, criar ramificações e passar à tarefa seguinte mais rápido do que um operador consegue reconstruir uma alteração acidental de permissão, uma migração parcial ou uma exclusão com escopo errado.

Uma ação de prévia só se justifica quando prevê uma execução específica com fidelidade suficiente para que uma pessoa ou um agente decida se deve prosseguir. Uma resposta que diz "válido" não é um plano. Um diff que omite uma atualização em cascata é pior do que nenhum diff, porque fabrica confiança.

O objetivo de design é simples: enviar a gravação proposta, avaliá-la contra o estado atual e as regras normais de negócio, retornar os efeitos planejados e as falhas, e então fazer a execução recusar planos obsoletos ou alterados. Isso exige mais cuidado do que adicionar `dryRun=true`. Também dá ao agente uma forma de corrigir uma solicitação inválida antes de pedir aprovação a uma pessoa.

## Uma prévia deve descrever a gravação exata

Endpoints de dry run devem aceitar a mesma intenção relevante da execução e calcular os efeitos dessa intenção exata. Se `POST /memberships` pode conceder uma função, enviar um convite, adicionar o membro a um grupo de cobrança e gravar uma entrada de auditoria, a prévia precisa informar cada efeito que a execução criaria.

As equipes costumam lançar um endpoint de "validação" que verifica o formato do JSON e os campos obrigatórios. Esse endpoint tem seu lugar, mas não faz a prévia de uma gravação. Ele não consegue informar que a função solicitada entra em conflito com uma função existente, que a conta de destino está suspensa ou que o convite consumirá uma vaga limitada. Chame-o de validação se é só isso que ele faz.

A diferença importa porque os agentes tratam chamadas bem-sucedidas como evidência. Uma resposta que valida apenas o formato, seguida da execução, deixa o agente sem visibilidade sobre as partes da decisão que dependem do estado. Uma prévia completa avalia tanto a solicitação quanto o estado atual do mundo.

Para cada operação que grava dados, descreva o contrato de execução em uma frase antes de projetar a prévia:

> Dada esta entrada e esta revisão observada do destino, a execução criará, atualizará, excluirá ou acionará estes efeitos identificados.

Essa frase expõe comportamentos vagos. "Atualizar as configurações do projeto" é amplo demais. "Alterar `retention_days` de 30 para 14, recalcular a expiração de 18 itens ativos e rejeitar itens sob retenção legal" dá à prévia algo concreto e testável para retornar.

Uma boa prévia preserva a semântica da operação. Não transforme uma exclusão em massa em uma contagem vaga só porque a lista real parece inconveniente. Não use "pode afetar" quando seu serviço consegue determinar os recursos reais. Se o conjunto de recursos for grande demais para retornar na resposta, informe o total, uma amostra limitada e um cursor ou uma referência de relatório que permita ao chamador inspecionar o conjunto completo antes da execução.

## O plano precisa de identidade, escopo e consequências

Ninguém consegue avaliar "12 registros serão alterados" sem saber quais registros são esses e como serão alterados. A ação planejada deve identificar suas entradas, seu escopo de destino e suas consequências em formatos que tanto um programa quanto uma pessoa consigam inspecionar.

Para uma atualização de um único recurso, um diff no nível dos campos costuma funcionar bem. Para uma implantação, o plano pode precisar de imagens, ambientes, revisões de configuração, comportamento de reinicialização e verificações de saúde. Para uma alteração de cobrança, pode precisar do valor antigo, do novo valor, da data de vigência e da informação sobre o envio de uma notificação ao cliente. Adapte a saída ao domínio, em vez de forçar toda operação a usar um array de JSON Patch.

No mínimo, exponha estas partes:

- O nome da operação e um status explícito da prévia.
- Um identificador estável para cada recurso afetado, além da revisão quando o serviço oferecer revisões.
- Os valores anteriores e propostos para cada alteração relevante.
- Efeitos secundários, como tarefas, notificações, mudanças de acesso ou cobranças calculadas.
- Avisos, bloqueios de execução e premissas que possam alterar o resultado.

"Relevante" exige bom senso. Um timestamp bruto do banco de dados raramente ajuda quem aprova. Um novo responsável, uma ampliação da participação em um grupo ou uma exclusão planejada certamente ajudam. Mostre primeiro o resultado semântico e ofereça detalhes de baixo nível quando o chamador precisar deles.

A prévia também precisa distinguir efeitos diretos de efeitos derivados. Imagine que um agente reduza a cota de armazenamento de uma equipe. A alteração direta é um único campo de cota. O resultado derivado pode ser bloquear uploads em três projetos existentes. Esconder isso sob um aviso genérico faz a operação parecer mais segura do que é. Coloque o efeito em um array `effects` separado e nomeie a causa.

Seja igualmente preciso sobre a incerteza. Uma prévia pode informar que a execução consultará um serviço externo de impostos ou agendará um trabalho para mais tarde. Ela não deve afirmar um valor final de imposto se o serviço ainda não o tiver resolvido. Use um registro de premissa que identifique a dependência e informe se a execução pode prosseguir sem ela.

## A validação deve separar bloqueios de avisos

A prévia deve informar exatamente o que impede a execução, o que merece revisão e o que serve apenas de contexto. Misturar essas categorias garante novas tentativas ruins e cansaço de aprovação.

Um bloqueio significa que o serviço recusará a execução nas condições avaliadas. O agente deve corrigir a entrada, obter a autoridade que falta ou parar. Um aviso significa que a execução pode prosseguir, mas um operador prudente talvez queira inspecionar a consequência. O contexto fornece informação sem sugerir perigo.

Retorne erros estruturados, não prosa que o agente precise interpretar. Este formato é deliberadamente comum:

```json
{
  "mode": "preview",
  "executable": false,
  "validation": [
    {
      "severity": "error",
      "code": "version_conflict",
      "path": "/if_match",
      "message": "Project prj_184 is at revision 73, not revision 71.",
      "blocks_execution": true,
      "repair": "Fetch the current project and create a new preview."
    },
    {
      "severity": "warning",
      "code": "member_count_change",
      "message": "The group will gain 42 members through nested groups.",
      "blocks_execution": false
    }
  ]
}
```

Códigos estáveis permitem que o agente escolha uma resposta. Ele pode buscar a revisão atual depois de `version_conflict`; não pode inventar uma correção de forma responsável depois de `legal_hold_active`. A `message` existe para a pessoa que revisa a ação. Mantenha os dois.

Não classifique toda condição inesperada como aviso. Um aviso que sempre exige que alguém altere a solicitação deveria ser um erro. Da mesma forma, não bloqueie a execução porque a API encontrou uma condição incomum, mas permitida. As equipes transformam todo aviso em bloqueio por medo de deixar algo passar, e então os agentes enviam prévias que nunca conseguem terminar sem limpeza manual. A interface vira encenação.

O teste útil é direto: se a execução receber a mesma entrada contra o mesmo estado, ela rodaria? Se sim, informe um aviso ou contexto. Se não, informe um erro. Mantenha as falhas de autorização separadas da validação de domínio. Elas explicam problemas diferentes e exigem correções diferentes.

## Um dry run não pode gravar às escondidas

Uma prévia deve evitar efeitos externos persistentes, inclusive aqueles que os desenvolvedores tratam como manutenção. Criar uma linha "temporária", reservar inventário, incrementar uma sequência visível para os usuários, enfileirar um webhook, enviar um e-mail ou atualizar um timestamp de último acesso viola a expectativa de que a solicitação era segura para inspeção.

Esse bug aparece em serviços maduros porque o código de execução cresceu em torno da conveniência. Um handler de criação pode alocar um identificador no início, gravar um registro pendente antes da validação e chamar um publicador de eventos antes de a transação ser confirmada. Mais tarde, alguém o envolve com `if preview` antes da inserção final. A prévia parece inofensiva em um teste local, mas ainda consome identificadores, produz tráfego de eventos ou deixa resíduos em produção.

Trate a execução da prévia como um modo separado no serviço de aplicação, não como uma condição apenas no controller. O modo pode chamar funções compartilhadas de análise, autorização, políticas e planejamento. Ele precisa encaminhar gravações e envios externos por interfaces que produzam um efeito proposto ou rejeitem a solicitação.

Um limite de implementação útil é este:

```text
parse request
  -> authorize caller
  -> load consistent current state
  -> validate business rules
  -> build plan
  -> preview: return plan
  -> execute: apply plan in a transaction, then publish committed effects
```

A ordem importa. Se o banco de dados oferecer transações, monte o plano usando as mesmas leituras que orientam a execução. Se uma dependência não puder participar da transação, informe sua interação pendente como um efeito explícito e crie uma ação compensatória para as falhas. Fingir que uma chamada externa é transacional não a torna transacional.

Os registros de auditoria também exigem uma decisão. Você pode querer registrar que um chamador solicitou uma prévia. Isso é razoável, mas grave esse evento em um caminho de auditoria claramente separado e garanta que ele não acione fluxos criados para alterações concluídas. Não coloque "pré-visualizado" ao lado de "permissão concedida" e espere que os consumidores downstream entendam a diferença.

Teste a ausência, não apenas a saída. Antes e depois de uma solicitação de prévia, confirme que as tabelas relevantes, filas de saída, armazenamento de objetos, destinos de teste de e-mail e receptores de webhook downstream não mudaram. Testes unitários raramente detectam isso. Um teste de integração em um ambiente descartável detectará.

## A semântica HTTP precisa de um contrato explícito

O HTTP não tem um método universal de dry run, e fingir o contrário causa problemas de interoperabilidade. A RFC 9110 define `GET`, `HEAD`, `OPTIONS` e `TRACE` como métodos seguros no sentido de que um cliente não solicita uma alteração de estado. Ela não diz que um `POST` com um parâmetro de consulta é seguro, nem define `dryRun` como um controle padrão de solicitação.

Isso significa que quem projeta o endpoint precisa tornar o modo visível tanto na solicitação quanto na resposta. Um `POST` muitas vezes continua sendo apropriado, porque o planejamento de gravações complexas precisa de um corpo de solicitação e pode exigir uma avaliação custosa. O importante é que clientes, logs e pessoas consigam distinguir uma prévia da execução sem adivinhar.

Para uma operação simples, um campo explícito no corpo é fácil de ler e difícil de perder:

```http
POST /v1/projects/prj_184/memberships/plan
Content-Type: application/json

{
  "subject_id": "usr_92",
  "role": "admin",
  "if_match": "73"
}
```

Um endpoint dedicado `/plan` funciona quando o planejamento tem sua própria saída, ciclo de vida ou permissões. Ele também evita uma falha recorrente com sinalizações de consulta: um cliente gerado omite a sinalização, um proxy a ignora na configuração de cache ou um chamador copia a URL incorretamente e executa a gravação. Se você escolher um único endpoint com um campo `mode`, rejeite valores ausentes ou desconhecidos nas operações em que uma execução acidental causaria problemas.

Retorne um tipo de resposta que não possa ser confundido com o recurso executado. `201 Created` com um corpo no formato do recurso é uma resposta ruim para uma prévia, mesmo que você inclua um campo `preview: true`. Use `200 OK` para um plano imediato ou `202 Accepted` apenas quando o próprio planejamento for assíncrono. Inclua `mode: "preview"` no corpo da resposta e defina um tipo de conteúdo explícito se sua API usar tipos de mídia tipados.

Evite armazenar prévias em cache, a menos que você conheça todas as entradas que as afetam, incluindo a identidade e a autorização do chamador. O padrão mais seguro é `Cache-Control: no-store`. Um plano obsoleto não é apenas uma página antiga. Ele pode direcionar um agente para uma gravação que agora afeta um conjunto diferente de recursos.

Não use `OPTIONS` para esse trabalho. A RFC 9110 o utiliza para descrever opções de comunicação, não para simular uma gravação com um corpo arbitrário. Um serviço que o sobrecarrega confundirá bibliotecas, controles de segurança e qualquer pessoa que espere o comportamento HTTP comum.

## A execução precisa provar que o plano ainda está atual

Uma prévia pode ficar errada no intervalo até a execução. Outro usuário pode editar o registro, uma tarefa agendada pode rodar, uma autorização pode expirar ou o agente pode alterar a solicitação depois de ler a resposta. Esse é um problema de tempo entre verificação e uso, e uma prévia tranquilizadora não o elimina.

Vincule um plano à solicitação avaliada, às revisões dos recursos lidos, à identidade do chamador e a uma validade curta. O servidor pode retornar um `plan_token` opaco assinado ou manter o plano e retornar um identificador. Tokens opacos impedem que o cliente trate o plano como uma autorização editável. Planos armazenados facilitam a inspeção de efeitos grandes e a revogação de uma aprovação. As duas abordagens funcionam se a execução verificar as condições corretas.

Uma resposta poderia conter:

```json
{
  "mode": "preview",
  "plan_id": "plan_7f4c",
  "expires_at": "2025-06-18T14:05:00Z",
  "request_digest": "sha256:...",
  "read_revisions": [
    {"resource": "projects/prj_184", "revision": "73"}
  ],
  "executable": true
}
```

Na execução, o serviço precisa verificar o chamador, o digest, a validade e as revisões. Depois, deve aplicar atomicamente o plano já aprovado ou regenerar o plano dentro da transação de gravação e compará-lo ao plano aprovado. Se não puder garantir a equivalência, deve rejeitar a solicitação com `plan_stale` e pedir uma nova prévia.

Não permita que um agente faça a prévia de uma solicitação para um sujeito e execute o ID do plano com um sujeito diferente no corpo. Melhor ainda, faça a execução aceitar apenas o ID do plano e uma revisão esperada, para não haver uma segunda cópia mutável da solicitação que o servidor precise reconciliar.

Algumas alterações não permitem uma garantia significativa. Um plano para enviar uma mensagem pode se tornar inadequado porque o endereço do destinatário mudou um instante depois. Um plano para chamar um serviço de terceiros pode depender de um preço que muda antes da chamada. Informe isso na saída, valide novamente imediatamente antes da ação irreversível e exija uma nova decisão quando a diferença importar.

## Os fluxos de agentes precisam de uma parada deliberada antes da execução

Um agente deve tratar uma prévia como evidência para uma decisão, não como permissão para executar automaticamente a gravação. O agente precisa de regras para saber quando pode executar, quando deve corrigir a solicitação e quando precisa apresentar o plano a uma pessoa.

O fluxo mais confiável tem quatro ações:

1. Enviar a gravação pretendida em modo de prévia, com uma referência de idempotência e as revisões esperadas dos recursos.
2. Parar se a resposta tiver bloqueios, corrigindo apenas os campos identificados pela resposta ou pedindo a uma pessoa a intenção que falta.
3. Apresentar os efeitos planejados e os avisos quando a operação ultrapassar o limite de aprovação da equipe.
4. Executar apenas o plano retornado enquanto ele continuar atual, e então registrar o resultado da execução separadamente da prévia.

A aprovação deve se concentrar na consequência, não em um dump de JSON bruto. Uma pessoa que decide se deve conceder acesso quer ver o principal, a função, os recursos alcançados pela expansão de grupos e a duração. Ela não deveria precisar deduzir esse impacto de um corpo de solicitação cheio de IDs.

Não faça o agente criar uma prévia para toda ação inofensiva e pedir aprovação para todo aviso. Isso produz uma fila de cartões que ninguém lê. Defina limites relevantes na aplicação: operações irreversíveis, alterações de acesso, dinheiro, comunicação externa, grandes conjuntos de recursos e ações cujos efeitos o serviço classifica como incertos. O agente pode executar alterações pequenas e bem compreendidas dentro da autoridade que você lhe conceder.

O Sallyport pode exigir uma decisão humana para a chamada HTTP ou SSH real de um agente, enquanto a prévia da API dá conteúdo concreto a essa decisão. Os dois controles resolvem problemas diferentes: um determina se um processo pode agir, e o outro explica o que o serviço de destino fará.

## Uma alteração em massa malsucedida mostra por que resumos são insuficientes

Imagine um agente encarregado de remover prestadores de serviço de um grupo de suporte de produção. Ele encontra um filtro que corresponde a 37 contas e envia uma prévia. O serviço retorna `count: 37`, `valid: true` e uma observação genérica de que as associações herdadas podem mudar. Um operador aprova porque o resultado solicitado parece rotineiro.

A execução remove a associação direta dessas 37 contas. Quatro mantêm o acesso por meio de grupos aninhados. Outras seis perdem uma permissão separada de plantão porque o serviço também remove uma autorização vinculada. Uma tarefa de notificação informa às 37 pessoas que o acesso mudou. Agora o operador precisa determinar quais efeitos foram intencionais, quais ficaram ocultos e se a notificação descreveu o estado real do acesso.

A prévia era tecnicamente verdadeira no sentido mais restrito. Ela não prometia que o filtro identificava apenas prestadores de serviço. Ainda assim, era uma interface ruim porque retornava uma contagem quando o usuário precisava de um grafo de associações e uma lista de efeitos.

Uma resposta melhor agrupa o resultado por consequência:

```json
{
  "mode": "preview",
  "operation": "remove_group_members",
  "selected": 37,
  "effects": [
    {"type": "direct_membership_removed", "count": 37},
    {"type": "access_retained_via_nested_group", "subjects": ["usr_8", "usr_19", "usr_31", "usr_44"]},
    {"type": "on_call_entitlement_removed", "subjects": ["usr_2", "usr_7", "usr_11", "usr_24", "usr_29", "usr_35"]},
    {"type": "notification_queued", "count": 37}
  ],
  "validation": [
    {
      "severity": "warning",
      "code": "access_outcome_varies",
      "message": "Four selected subjects retain group-derived access."
    }
  ]
}
```

A resposta certa pode incluir um relatório baixável ou detalhes paginados para lotes maiores. O objetivo não é obrigar alguém a ler milhares de linhas. É tornar visíveis os resultados excepcionais e irreversíveis antes da gravação.

Este exemplo também expõe uma recomendação ruim comum: "use dry runs apenas para ações destrutivas". As equipes a repetem porque exclusões parecem perigosas e as prévias exigem tempo de engenharia. Mas uma concessão de permissão, uma alteração de configuração ou uma notificação pode ter um raio de impacto maior que uma exclusão. Escolha o suporte a prévias pela consequência e pela reversibilidade, não pelo verbo HTTP ou pela operação de banco de dados.

## Os testes devem comparar os efeitos previstos com os efeitos executados

Um endpoint de prévia se deteriora quando os testes apenas comprovam que ele retorna uma resposta 200. Sua promessa central é a equivalência: quando o estado e a solicitação coincidem, os efeitos informados devem coincidir com a execução.

Crie testes pareados. Prepare um cenário, faça a prévia, capture o plano normalizado, restaure o cenário, execute a mesma intenção e compare o diário de execução com o conjunto de efeitos previsto. Ignore campos que não possam corresponder razoavelmente, como timestamps do servidor ou IDs de correlação gerados. Não ignore recursos criados, valores alterados, eventos publicados, notificações ou chamadas de saída.

Testes de propriedade ajudam com filtros e operações em massa. Gere uma coleção de recursos com estados variados, peça uma prévia contra um predicado, execute-a em uma cópia nova e confirme que o conjunto selecionado e o estado final coincidem. Esses testes encontram os casos difíceis em que a consulta de planejamento faz join com uma tabela, mas a consulta de gravação faz join com outra.

Mantenha um teste específico para os efeitos colaterais da prévia. Use adaptadores falsos para e-mail, webhooks, filas e provedores de pagamento que façam o teste falhar se o modo de prévia os chamar. Depois, execute pelo menos um teste de integração contra a camada de persistência real, porque um flush do ORM ou um trigger pode gravar mesmo quando o código da aplicação parece correto.

Por fim, teste a obsolescência de propósito. Faça a prévia de uma alteração, modifique um recurso por meio de outra solicitação e tente executar o plano antigo. O serviço deve rejeitá-lo. Um sistema que aplica o plano antigo porque o diff "ainda parece próximo o suficiente" acabará sobrescrevendo o trabalho de outra pessoa.

## A prévia é uma capacidade da API, não uma desculpa para ignorar controles

Endpoints de prévia reduzem surpresas. Eles não substituem autorização, verificações de concorrência, design de transações, idempotência, trilhas de auditoria ou revisão de operações que merecem revisão. Um chamador sem autoridade não deve obter um mapa detalhado de recursos protegidos sondando prévias. Um chamador que repete uma solicitação de execução não deve criar o mesmo efeito duas vezes porque um token de plano era válido.

Comece pela gravação que mais prejudicou sua equipe em ensaios ou em produção. Liste todos os efeitos diretos e indiretos, implemente um plano que os informe e faça a execução rejeitar planos obsoletos. Depois, escreva o teste pareado que comprove que a prévia e a execução concordam. Se você não consegue dizer o que uma gravação fará antes de executá-la, o agente não é a parte arriscada do sistema. A API é.
