# Endpoints de dry run que mantienen honestos a los agentes de programación con IA

Los agentes de programación con IA no deberían descubrir si un cambio es válido después de ejecutarlo. Ese diseño de API es perezoso, y la autonomía hace que el coste se note enseguida. Un agente puede reintentar, abrir ramas y pasar a la siguiente tarea más rápido de lo que un operador puede reconstruir un cambio accidental de permisos, una migración parcial o un borrado con un alcance mal definido.

Una acción de vista previa solo merece existir cuando predice una ejecución concreta con suficiente fidelidad para que una persona o un agente decida si debe continuar. Una respuesta que dice «válido» no es un plan. Una diferencia que omite una actualización en cascada es peor que no mostrar ninguna, porque crea una falsa sensación de seguridad.

El objetivo de diseño es sencillo: enviar la escritura propuesta, evaluarla contra el estado actual y las reglas normales del negocio, devolver los efectos previstos y los fallos, y después hacer que la ejecución rechace los planes obsoletos o alterados. Esto requiere más cuidado que añadir `dryRun=true`. También permite que un agente corrija una petición inválida antes de pedir aprobación a una persona.

## La vista previa debe describir la escritura exacta

Los endpoints de dry run deben aceptar la misma intención significativa que la ejecución y calcular los efectos de esa intención exacta. Si `POST /memberships` puede conceder un rol, enviar una invitación, añadir al miembro a un grupo de facturación y escribir una entrada de auditoría, la vista previa debe informar de cada efecto que crearía la ejecución.

Los equipos suelen publicar un endpoint de «validación» que comprueba la estructura JSON y los campos obligatorios. Ese endpoint tiene su lugar, pero no muestra una escritura. No puede decirle al cliente que el rol solicitado entra en conflicto con otro existente, que la cuenta de destino está suspendida o que la invitación consumirá una plaza limitada. Llámalo validación si eso es todo lo que hace.

La diferencia importa porque los agentes tratan las llamadas exitosas como pruebas. Una respuesta que solo valida, seguida de una ejecución, deja al agente a ciegas ante las partes de la decisión que dependen del estado. Una vista previa completa evalúa tanto la petición como el estado actual del mundo.

Para cada operación que escriba datos, describe el contrato de ejecución en una frase antes de diseñar la vista previa:

> Con esta entrada y esta revisión observada del objetivo, la ejecución creará, actualizará, eliminará o activará estos efectos concretos.

Esa frase deja al descubierto los comportamientos vagos. «Actualizar la configuración del proyecto» es demasiado amplio. «Cambiar `retention_days` de 30 a 14, recalcular la caducidad de 18 elementos activos y rechazar los elementos sujetos a retención legal» ofrece a la vista previa algo comprobable que devolver.

Una buena vista previa conserva la semántica de la operación. No conviertas un borrado masivo en un recuento impreciso solo porque la lista real resulte incómoda. No uses «podría afectar» cuando tu servicio puede determinar los recursos reales. Si el conjunto es demasiado grande para devolverlo completo, incluye un total, una muestra limitada y un cursor o referencia a un informe que permita inspeccionarlo antes de ejecutar.

## El plan necesita identidad, alcance y consecuencias

Nadie puede valorar «cambiarán 12 registros» sin saber cuáles son ni cómo cambiarán. La acción prevista debe identificar sus entradas, su alcance y sus consecuencias de forma que tanto un programa como una persona puedan inspeccionarlos.

Para actualizar un único recurso, suele funcionar bien una diferencia por campo. En un despliegue, el plan puede necesitar imágenes, entornos, revisiones de configuración, comportamiento de reinicio y comprobaciones de salud. En un cambio de facturación, puede necesitar el cargo anterior, el nuevo, la fecha de entrada en vigor y si el cliente recibirá una notificación. Ajusta la salida al dominio en vez de obligar a todas las operaciones a usar un array de JSON Patch.

Como mínimo, expón estas partes:

- El nombre de la operación y un estado de vista previa explícito.
- Un identificador estable para cada recurso afectado y su revisión cuando el servicio admita revisiones.
- Los valores anteriores y propuestos para cada cambio significativo.
- Los efectos secundarios, como trabajos, notificaciones, cambios de acceso o cargos calculados.
- Las advertencias, los bloqueos de ejecución y los supuestos que podrían cambiar el resultado.

«Significativo» requiere criterio. Una marca de tiempo de base de datos rara vez ayuda a quien aprueba. Un propietario nuevo, una ampliación de la pertenencia a un grupo o un borrado previsto sí importan. Muestra primero el resultado semántico y ofrece detalles de nivel inferior cuando el cliente los necesite.

La vista previa también debe distinguir los efectos directos de los derivados. Supón que un agente reduce la cuota de almacenamiento de un equipo. El cambio directo es un campo de cuota. El resultado derivado podría bloquear las cargas en tres proyectos existentes. Ocultarlo bajo una advertencia genérica hace que la operación parezca más segura de lo que es. Colócalo en un array separado `effects` y nombra la causa.

Sé igual de preciso con la incertidumbre. Una vista previa puede indicar que la ejecución consultará un servicio fiscal externo o programará trabajo para más tarde. No debería afirmar un importe fiscal definitivo si el servicio aún no lo ha resuelto. Usa un registro de supuestos que nombre la dependencia e indique si la ejecución puede continuar sin ella.

## La validación debe separar bloqueos y advertencias

Una vista previa debe decirle al agente qué impide ejecutar, qué merece revisión y qué solo aporta contexto. Mezclar esas categorías garantiza reintentos incorrectos y fatiga de aprobación.

Un bloqueo significa que el servicio rechazará la ejecución bajo las condiciones evaluadas. El agente debe corregir la entrada, obtener la autoridad que falta o detenerse. Una advertencia significa que la ejecución puede continuar, aunque un operador razonable quizá quiera revisar la consecuencia. El contexto aporta información sin insinuar peligro.

Devuelve errores estructurados, no prosa que el agente tenga que interpretar. Esta estructura es deliberadamente sencilla:

```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
    }
  ]
}
```

Los códigos estables permiten que un agente elija una respuesta. Puede obtener la revisión actual después de `version_conflict`; no puede inventar una corrección responsable después de `legal_hold_active`. El `message` existe para la persona que revisa la acción. Conserva ambos.

No etiquetes como advertencia cualquier condición sorprendente. Si una advertencia siempre exige que alguien cambie la petición, debería ser un error. Del mismo modo, no bloquees la ejecución porque la API encuentre una condición inusual pero permitida. Los equipos convierten todas las advertencias en bloqueos por miedo a pasar algo por alto, y después los agentes envían vistas previas que nunca pueden completarse sin limpieza manual. La interfaz se vuelve una representación vacía.

La prueba útil es sencilla: si la ejecución recibiera la misma entrada contra el mismo estado, ¿se ejecutaría? Si la respuesta es sí, informa de una advertencia o del contexto. Si es no, informa de un error. Mantén los fallos de autorización separados de la validación del dominio. Explican problemas distintos y requieren soluciones diferentes.

## Un dry run no puede escribir a espaldas del cliente

Una vista previa debe evitar los efectos externos duraderos, incluidos los que los desarrolladores descartan como tareas de mantenimiento. Crear una fila «temporal», reservar inventario, incrementar una secuencia visible para los usuarios, poner un webhook en cola, enviar un correo o actualizar una marca de último acceso incumple la expectativa de que la petición era segura de inspeccionar.

Este error aparece en servicios maduros porque el código de ejecución creció alrededor de la conveniencia. Un controlador de creación puede asignar un identificador al principio, escribir un registro pendiente antes de validar y llamar a un publicador de eventos antes de confirmar la transacción. Más tarde alguien lo envuelve con `if preview` alrededor del insert final. La vista previa parece inofensiva en una prueba local y aun así consume identificadores, produce tráfico de eventos o deja residuos en producción.

Trata la ejecución de la vista previa como un modo separado en el servicio de aplicación, no como una condición únicamente en el controlador. El modo puede reutilizar las funciones de análisis, autorización, políticas y planificación. Debe dirigir las escrituras y los envíos externos a través de interfaces que produzcan un efecto propuesto o hagan fallar la petición.

Un límite de implementación útil sería:

```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
```

El orden importa. Si tu base de datos admite transacciones, construye el plan a partir de las mismas lecturas que guiarán la ejecución. Si una dependencia no puede participar en la transacción, informa de su interacción pendiente como un efecto explícito y diseña una acción compensatoria para los fallos. Fingir que una llamada externa es transaccional no la convierte en tal.

Los registros de auditoría también requieren una decisión. Quizá quieras registrar que un cliente solicitó una vista previa. Es razonable, pero escribe ese evento en una ruta de auditoría claramente separada y asegúrate de que no active flujos creados para cambios completados. No coloques «vista previa» junto a «permiso concedido» y esperes que los consumidores posteriores deduzcan la diferencia.

Comprueba la ausencia de cambios, no solo la salida. Antes y después de una petición de vista previa, verifica que no hayan cambiado las tablas relevantes, las colas de salida, el almacenamiento de objetos, los buzones de prueba ni los receptores de webhooks. Las pruebas unitarias rara vez detectan esto. Una prueba de integración en un entorno desechable sí lo hará.

## La semántica HTTP necesita un contrato explícito

HTTP no tiene un método universal de dry run, y fingir lo contrario causa problemas de interoperabilidad. RFC 9110 define `GET`, `HEAD`, `OPTIONS` y `TRACE` como métodos seguros en el sentido de que el cliente no solicita un cambio de estado. No dice que un `POST` con un parámetro de consulta sea seguro ni define `dryRun` como control estándar de la petición.

Por eso, quien diseñe el endpoint debe hacer visible el modo tanto en la petición como en la respuesta. Un `POST` suele seguir siendo apropiado porque planificar escrituras complejas necesita un cuerpo y puede requerir una evaluación costosa. Lo importante es que clientes, registros y personas puedan distinguir una vista previa de una ejecución sin adivinar.

Para una operación sencilla, un campo explícito en el cuerpo es fácil de leer y 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"
}
```

Un endpoint dedicado `/plan` funciona cuando la planificación tiene una salida, un ciclo de vida o permisos propios. También evita un fallo recurrente de las opciones de consulta: un cliente generado omite la opción, un proxy la ignora en su configuración de caché o alguien copia mal la URL y ejecuta la escritura. Si eliges un único endpoint con un campo `mode`, rechaza los valores ausentes o desconocidos en las operaciones donde una ejecución accidental sería grave.

Devuelve un tipo de respuesta que no pueda confundirse con el recurso ejecutado. `201 Created` con un cuerpo que parece un recurso es una mala respuesta de vista previa aunque incluya `preview: true`. Usa `200 OK` para un plan inmediato o `202 Accepted` solo cuando la planificación se ejecute de forma asíncrona. Incluye `mode: "preview"` en el cuerpo y establece un tipo de contenido explícito si tu API usa tipos de medios.

Evita almacenar vistas previas en caché salvo que entiendas cada entrada que las afecta, incluida la identidad y la autorización del cliente. El valor predeterminado más seguro es `Cache-Control: no-store`. Un plan obsoleto no es simplemente una página antigua. Puede dirigir a un agente hacia una escritura que ahora afecte a otro conjunto de recursos.

No uses `OPTIONS` para este trabajo. RFC 9110 lo utiliza para describir opciones de comunicación, no para simular una escritura con un cuerpo arbitrario. Un servicio que lo sobrecargue confundirá a las bibliotecas, los controles de seguridad y a cualquiera que espere un comportamiento HTTP normal.

## La ejecución debe demostrar que el plan sigue vigente

Una vista previa puede quedar desactualizada antes de la ejecución. Otro usuario puede editar el registro, un trabajo programado puede ejecutarse, un permiso puede caducar o el agente puede modificar la petición después de leer la respuesta. Es un problema de tiempo entre comprobación y uso, y una vista previa tranquilizadora no lo elimina.

Vincula el plan a la petición evaluada, las revisiones de los recursos leídos, la identidad del cliente y una caducidad breve. El servidor puede devolver un `plan_token` opaco firmado o conservar el plan y devolver un identificador. Los tokens opacos impiden que el cliente trate el plan como una autorización editable. Los planes almacenados facilitan inspeccionar efectos grandes y revocar una aprobación. Cualquiera de los dos enfoques funciona si la ejecución vuelve a comprobar las condiciones adecuadas.

La respuesta podría contener:

```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
}
```

Al ejecutar, el servicio debe verificar el cliente, el resumen, la caducidad y las revisiones. Después debe aplicar atómicamente el plan ya aprobado o regenerarlo dentro de la transacción de escritura y compararlo con el aprobado. Si no puede garantizar la equivalencia, debe rechazar la petición con `plan_stale` y solicitar una nueva vista previa.

No permitas que un agente previsualice una petición para un sujeto y ejecute el identificador del plan con otro sujeto en el cuerpo. Mejor aún, haz que la ejecución acepte solo el identificador del plan y una revisión esperada, para que no exista una segunda copia mutable de la petición que el servidor tenga que reconciliar.

Algunos cambios no pueden recibir una garantía significativa. Un plan para enviar un mensaje puede dejar de ser apropiado porque la dirección del destinatario cambie un instante después. Un plan para llamar a un servicio de terceros puede depender de un precio que varíe antes de la llamada. Explícalo en la salida, vuelve a validar justo antes de la acción irreversible y exige una nueva decisión cuando la diferencia importe.

## Los flujos de agentes necesitan una pausa deliberada antes de ejecutar

Un agente debe tratar una vista previa como evidencia para tomar una decisión, no como permiso para ejecutar automáticamente la escritura. Necesita reglas que indiquen cuándo puede ejecutar, cuándo debe corregir la petición y cuándo tiene que presentar el plan a una persona.

El flujo más fiable tiene cuatro acciones:

1. Envía la escritura prevista en modo de vista previa con una referencia de idempotencia y las revisiones esperadas de los recursos.
2. Detente si la respuesta contiene bloqueos; corrige solo los campos identificados por la respuesta o pide a una persona la intención que falta.
3. Presenta los efectos previstos y las advertencias cuando la operación cruce el límite de aprobación del equipo.
4. Ejecuta únicamente el plan devuelto mientras siga vigente y registra el resultado de la ejecución por separado de la vista previa.

La aprobación debe centrarse en las consecuencias, no en un volcado de JSON. Una persona que decide si concede acceso quiere ver el principal, el rol, los recursos alcanzados mediante la expansión de grupos y la duración. No debería tener que deducir ese impacto de un cuerpo lleno de identificadores.

No hagas que el agente previsualice cada acción inofensiva ni pida aprobación para cada advertencia. Eso crea una cola de tarjetas que nadie lee. Define límites significativos en la aplicación: operaciones irreversibles, cambios de acceso, dinero, comunicación externa, conjuntos amplios de recursos y acciones cuyos efectos el servicio marque como inciertos. El agente puede ejecutar cambios pequeños y bien entendidos dentro de la autoridad que le hayas dado.

Sallyport puede exigir una decisión humana para la llamada HTTP o SSH real de un agente, mientras que la vista previa de la API da contenido concreto a esa decisión. Los dos controles resuelven problemas distintos: uno determina si un proceso puede actuar y el otro explica qué hará el servicio de destino.

## Un cambio masivo fallido demuestra por qué los resúmenes no bastan

Imagina que un agente recibe la orden de eliminar contratistas de un grupo de soporte de producción. Encuentra un filtro que coincide con 37 cuentas y envía una vista previa. El servicio devuelve `count: 37`, `valid: true` y una nota genérica que indica que las pertenencias heredadas podrían cambiar. Un operador aprueba porque el resultado solicitado parece rutinario.

La ejecución elimina la pertenencia directa de esas 37 cuentas. Cuatro conservan el acceso mediante grupos anidados. Otras seis pierden un permiso de guardia independiente porque el servicio también elimina un derecho vinculado. Un trabajo de notificación informa a las 37 personas de que su acceso cambió. Ahora el operador tiene que determinar qué efectos eran intencionados, cuáles estaban ocultos y si la notificación describía el estado real de acceso.

La vista previa era técnicamente veraz en el sentido más estrecho. No prometía que el filtro identificara solo a contratistas. Aun así, era una interfaz deficiente porque devolvía un recuento cuando el usuario necesitaba un grafo de pertenencias y una lista de efectos.

Una respuesta mejor agrupa el resultado por consecuencia:

```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."
    }
  ]
}
```

La respuesta adecuada puede incluir un informe descargable o detalles paginados para lotes grandes. La cuestión no es obligar a una persona a leer miles de filas. Es hacer visibles los resultados excepcionales e irreversibles antes de escribir.

Este ejemplo también revela una recomendación frecuente y deficiente: «usa dry runs solo para acciones destructivas». Los equipos la repiten porque los borrados parecen peligrosos y las vistas previas cuestan tiempo de ingeniería. Pero una concesión de permisos, un cambio de configuración o una notificación pueden tener un radio de impacto mayor que un borrado. Elige el soporte de vista previa según las consecuencias y la reversibilidad, no según el verbo HTTP o la operación de base de datos.

## Las pruebas deben comparar los efectos previstos con los ejecutados

Un endpoint de vista previa se degrada cuando las pruebas solo demuestran que devuelve una respuesta 200. Su promesa central es la equivalencia: cuando el estado y la petición coinciden, los efectos informados deben coincidir con la ejecución.

Construye pruebas emparejadas. Prepara una instalación, solicita la vista previa, captura el plan normalizado, restablece la instalación, ejecuta la misma intención y compara el diario de ejecución con el conjunto de efectos previsto. Ignora los campos que razonablemente no pueden coincidir, como las marcas de tiempo del servidor o los identificadores de correlación generados. No ignores los recursos creados, los valores cambiados, los eventos publicados, las notificaciones ni las llamadas salientes.

Las pruebas basadas en propiedades ayudan con los filtros y las operaciones masivas. Genera una colección de recursos con estados variados, solicita una vista previa contra un predicado, ejecútala en una copia nueva y comprueba que el conjunto seleccionado y el estado final coincidan. Estas pruebas encuentran los casos difíciles en los que la consulta de planificación une una tabla, pero la consulta de escritura une otra.

Conserva una prueba específica para los efectos secundarios de la vista previa. Usa adaptadores falsos para correo, webhooks, colas y proveedores de pagos que hagan fallar la prueba si el modo de vista previa los llama. Después ejecuta al menos una prueba de integración contra la capa de persistencia real, porque un flush del ORM o un trigger puede escribir aunque el código de la aplicación parezca limpio.

Por último, prueba la obsolescencia de forma intencionada. Previsualiza un cambio, modifica un recurso mediante otra petición y después ejecuta el plan antiguo. El servicio debe rechazarlo. Un sistema que aplica el plan anterior porque la diferencia «sigue pareciendo suficientemente cercana» acabará sobrescribiendo el trabajo de otra persona.

## La vista previa es una capacidad de la API, no una excusa para omitir controles

Los endpoints de vista previa reducen las sorpresas. No sustituyen la autorización, las comprobaciones de concurrencia, el diseño transaccional, la idempotencia, los registros de auditoría ni la revisión de las operaciones que la merezcan. Un cliente sin autoridad no debería obtener un mapa detallado de recursos protegidos explorando vistas previas. Un cliente que repita una petición de ejecución no debería crear dos veces el mismo efecto porque el token del plan seguía siendo válido.

Empieza por la escritura que más problemas haya causado a tu equipo durante los ensayos o en producción. Enumera todos sus efectos directos e indirectos, implementa un plan que los informe y haz que la ejecución rechace los planes obsoletos. Después escribe la prueba emparejada que demuestre que la vista previa y la ejecución coinciden. Si no puedes explicar qué hará una escritura antes de ejecutarla, el agente no es la parte arriesgada del sistema. Lo es la API.
