Al terminar esta lección
- Preparar un context bundle de menos de 8.000 palabras que contenga solo las RN, ADR, contratos y archivos que el cambio necesita.
- Escribir un test de RN-06 que falle por la razón correcta antes de tocar el código.
- Ejecutar el cambio en checkpoints, cada uno con su gate y su evidencia.
- Hacer una revisión semántica del diff y clasificar las observaciones de un segundo agente sin aceptarlas por defecto.
- Cerrar el gate con evidencia, dejando visibles los criterios que todavía no podés demostrar.
Práctica E09 · Hacer un cambio completo con agente
Situación. Implementá un cambio pequeño de Nexo: agregar conflicto cuando el mismo requestId se reutiliza con payload diferente.
Entregá. prompt de plan; plan aprobado; diff acotado; tests; registro de decisiones humanas.
Está listo cuando. el agente no toca fuera de alcance; el test demuestra semántica idempotente; hay evidencia de comandos; la RN/ADR se actualiza si cambia comportamiento.
Solución comentada: Solución comentada E09. Comparala después de producir tu entrega.
Elegí un cambio pequeño de Nexo y ejecutá el ciclo completo con un agente. Conservá la conversación como evidencia de proceso, no como especificación. El recorrido de abajo tiene seis pasos. Cada uno tiene un objetivo, un tiempo sugerido, un ejemplo parcial trabajado y un artefacto que tenés que producir. Las soluciones completas están en la lección 14.4, no acá.
Qué puede hacer el agente: leer el bundle y el código, proponer un plan con archivos y mecanismo, escribir el test rojo, implementar dentro de los archivos autorizados, ejecutar los gates y resumir el cambio con sus comandos.
Qué no debe decidir: si una respuesta de negocio cambia (un conflicto, un rechazo, un estado), qué significa “mismo contenido” más allá de lo que dice el ADR, si el depósito es el preferido o el efectivo, ni si el cambio se integra.
Contexto mínimo útil: AGENTS.md, el bundle de E09, los archivos focales y los comandos de los gates. Nada de sesiones anteriores ni de decisiones que vivan solo en el chat.
Cómo verificar: cada referencia del plan a archivo y línea se comprueba con una búsqueda, cada gate se muestra con su salida, y el diff se compara contra el plan aprobado.
Prompt de ejemplo (plan del cambio E09):
Leé el bundle de E09 y AGENTS.md. No modifiques archivos.
1. Resumí el objetivo y las garantías con tus palabras.
2. Listá las RN, ADR y AC que gobiernan el cambio. Citá la definición, no solo el ID.
3. Localizá dónde se compara hoy la clave de idempotencia y citá archivo y línea.
4. Proponé el mínimo de archivos a tocar y por qué cada uno.
5. Indicá qué test escribirías primero y qué debería fallar y por qué.
6. Marcá como REQUIERE APROBACIÓN cualquier cambio de contrato, esquema o dependencia.
7. Marcá como PREGUNTA cualquier decisión de significado que no esté en la spec.
Cómo revisar su salida: los puntos 3 y 4 se verifican con rg antes de aprobar el plan. El punto 7 vuelve a la spec, no al chat.
Paso 1 · Armar el context bundle
Objetivo. Dejar un bundle que un agente pueda ejecutar sin adivinar: el objetivo observable, las reglas con su definición completa, el contrato y los archivos permitidos.
Tiempo sugerido. 20 minutos.
El bundle del slice de reserva que viste en la lección 9.1 tiene nueve puntos. Para E09 no necesitás todo el bundle: necesitás la parte que gobierna la idempotencia. Un bundle mínimo se parece a este ejemplo parcial, con las secciones que suelen faltar marcadas:
## 1. Objetivo observable
Si el mismo requestId llega con contenido distinto, devolver conflict sin efectos.
Hecho cuando el test de conflicto pasa y el de retry igual sigue pasando.
## 2. Reglas (definición completa)
RN-06 (texto completo, copiado de la spec vigente)
## 3. Decisiones que restringen el cambio
ADR-005 Idempotencia por requestId en la misma transacción que pedido y reserva.
ADR-006 Fingerprint: cliente, depósito y líneas normalizadas. Ignora tracing.
## 4. Contratos
CreateOrderResult: (copiar el tipo tal como está en src/application/create-order.ts)
## 5. Archivos permitidos
src/domain/ · src/adapters/postgres-idempotency.ts · tests/integration/
## 6. Fuera de alcance
UI, API HTTP, nombres públicos, dependencias, esquema salvo aprobación.
## 7. Gates
npm run typecheck · npm run test:unit · npm run test:integration -- idempotency
Lo que más cuesta en este paso es la sección 2. Pegar el ID “RN-06” no alcanza: el agente tiene que recibir la definición completa, porque es ahí donde está la diferencia entre retry legítimo y conflicto. Si la copiás de un documento que ya no es vigente, el bundle se contradice con la spec antes de que empiece el trabajo.
Completá el bundle de E09
Tomá el ejemplo anterior y completalo para tu versión del cambio. Las secciones 2, 3 y 5 tienen que ser precisas: si una regla no está en la spec, escribí la pregunta en lugar de inventar la regla. Entregá el bundle en un archivo y una lista de hasta tres preguntas abiertas.
Paso 2 · Pedir el plan antes del código
Objetivo. Obtener un plan de veinte líneas que cite archivos y mecanismo, y revisarlo antes de aprobar nada.
Tiempo sugerido. 20 minutos.
El plan es el control más barato del ciclo. Lo que se busca no es un plan elegante sino uno verificable: cada afirmación sobre el repo apunta a un archivo y una línea que podés abrir. Un plan que dice “la idempotencia se compara en el repositorio” sin citar nada no sirve para aprobar.
Antes de aprobar, verificá dos cosas con búsquedas reales, no con otra pregunta al agente:
# ¿Dónde se compara la clave de idempotencia hoy?
rg -n "request_id|requestId" src/ db/
# ¿Existe ya un campo de fingerprint o hash?
rg -n "request_hash|fingerprint" src/ db/
Un plan de este cambio suele traer propuestas que no pertenecen a E09, y conviene reconocerlas antes de discutirlas. Una tabla nueva de “conflictos” es un cambio de esquema que requiere aprobación. Un lock distribuido con Redis contradice ADR-002. Un renombre de OrderRepository es un refactor fuera de alcance. Un job que libera claves viejas a las 24 horas inventa una política que la spec no tiene (RN-07 sigue BLOCKED). Ninguna de esas propuestas es necesariamente falsa en otro proyecto. En este, todas se rechazan o se llevan a aprobación, y el plan aprobado tiene que decir cuáles quedaron afuera.
Si el agente dice que el campo existe y rg no lo encuentra, el plan tiene una premisa falsa. Si dice que la comparación ocurre en el use case y rg muestra que ocurre en el adapter, tenés una comprensión incorrecta atrapada antes de tocar una línea.
Escribí el prompt de plan del cambio
Escribí el prompt que le das al agente para pedir el plan de E09. Tiene que incluir: el objetivo observable, los archivos permitidos y prohibidos, la pregunta de qué se considera mismo contenido, qué test va primero y qué debe marcar como requiere aprobación. Después escribí, en dos líneas, qué respuesta del agente rechazarías.
Paso 3 · El test rojo primero
Objetivo. Escribir el test de RN-06 y verlo fallar por la razón esperada: el segundo intento con otro contenido se confirma en lugar de devolver conflicto.
Tiempo sugerido. 20 minutos.
El test va antes del código porque es la forma ejecutable de la regla. Un test que nunca falló no demuestra que protege algo. Acá tenés la estructura parcial: el arrange y el act ya están, y las aserciones son el trabajo que tenés que hacer.
// Fixture y runner del proyecto (módulo 10)
describe("RN-06 · requestId reutilizado", () => {
it("con el mismo requestId y otro contenido devuelve conflict y no produce efectos", async () => {
const env = await fixture({ stock: { "SKU-1": 10 }, priceCents: { "SKU-1": 100_000 } });
const base = { requestId: "req-900", sellerId: env.sellerId, clientId: env.clientId };
const first = await env.createOrder({ ...base, items: [{ sku: "SKU-1", quantity: 2 }] });
const second = await env.createOrder({ ...base, items: [{ sku: "SKU-1", quantity: 3 }] });
// TODO: aserciones. ¿Qué devuelve el segundo? ¿Cuántos pedidos hay? ¿Cuánto está reservado?
});
});
Ver las aserciones y la pista de ejecución
Las aserciones que prueban la regla son estas: el primer intento confirma, el segundo devuelve el resultado de conflicto del contrato, y los efectos no cambian respecto del primer intento.
expect(first.kind).toBe("confirmed");
expect(second).toEqual({ kind: "conflict", reason: "request_id_reused" });
expect(await env.orders.count()).toBe(1);
expect(await env.stock.reserved("SKU-1")).toBe(2);
Para que el test falle por la razón correcta, ejecutalo antes de cambiar el código. Con la implementación actual, el segundo intento debería confirmar un pedido nuevo o reutilizar el anterior sin comparar contenido. Si el test falla con un error de compilación o de conexión, no es la falla que querés: arreglá el fixture antes de seguir.
Guardá la salida del comando. Un test rojo sin salida guardada es una afirmación; con salida, es evidencia.
Paso 4 · Implementar en checkpoints
Objetivo. Hacer el cambio mínimo que pone el test en verde, dentro de los archivos autorizados, y dejar el fingerprint donde la spec dice.
Tiempo sugerido. 30 minutos.
El cambio se divide en dos checkpoints. El primero es el dominio: una función pura que calcula el fingerprint de la intención. El segundo es el adapter: la comparación de la clave contra lo guardado. Después de cada uno corrés el gate focal, mirás el diff y recién entonces seguís. Un checkpoint que mezcla dominio y persistencia es difícil de revisar aunque el resultado sea correcto.
import { createHash } from "node:crypto";
export function requestFingerprint(cmd: {
clientId: string;
depositId: string;
items: Array<{ sku: string; quantity: number }>;
}): string {
const lines = normalizeLines(cmd.items); // ADR-006: normalización completa
const canonical = JSON.stringify({ clientId: cmd.clientId, depositId: cmd.depositId, lines });
return createHash("sha256").update(canonical).digest("hex");
}
function normalizeLines(items: Array<{ sku: string; quantity: number }>) {
// TODO: completar según ADR-006 (sku, suma por sku, orden por sku)
throw new Error("pendiente");
}
Ver pistas de normalización y la pregunta de depósito
La normalización que dice ADR-006 tiene tres pasos: el SKU va en mayúsculas y sin espacios, las cantidades de un mismo SKU se suman, y las líneas se ordenan por SKU. Con esos tres pasos, dos pedidos con las mismas líneas en otro orden tienen el mismo fingerprint, y cambiar un SKU o una cantidad produce otro.
La pregunta de depósito es la más delicada. ADR-006 dice “depósito”, pero el comando puede traer preferredDepositId o no traerlo. Si el fingerprint usa el preferido tal como llega, dos requests equivalentes pueden parecer distintos solo porque uno omitió el campo. El depósito efectivo lo resuelve el servidor (bundle, sección 5). Esa elección es una decisión de significado. No la resuelvas en silencio: escribila como pregunta en el bundle y esperá la respuesta antes de implementar esa parte.
En el adapter, el cambio conceptual es que la comparación ya no ocurre solo por requestId. Si la clave existe, se compara el fingerprint guardado con el calculado. Iguales significa retry legítimo y se devuelve el mismo pedido. Distintos significa conflict y ningún efecto nuevo. Las filas anteriores, sin hash, mantienen el comportamiento previo y registran una métrica, como ya se vio en la lección 9.1. No agregues el NOT NULL en este cambio.
El diagrama muestra el mecanismo que el test tiene que demostrar. Es el caso de un timeout después del commit, seguido de dos respuestas posibles según el contenido que llegue con la misma clave.
Después del segundo checkpoint corrés el gate completo y guardás cada salida. Si algún gate falla, el diff se corrige en código, no se relaja el test.
Paso 5 · Evidencia de los gates
Objetivo. Ejecutar los gates y entregar una evidencia que otro revisor pueda reproducir.
Tiempo sugerido. 15 minutos.
npm run typecheck
npm run test:unit
npm run test:integration -- idempotency
El formato de entrega es uno por gate: el comando, el exit code y la línea de resumen. Un ejemplo del formato, no de un resultado real:
$ npm run test:integration -- idempotency
exit code: 0
RN-06 · requestId reutilizado
✓ con el mismo requestId y mismo contenido devuelve el mismo pedido
✓ con el mismo requestId y otro contenido devuelve conflict y no produce efectos
2 passed
Además del gate, pedí una prueba de mutación. Quitá temporalmente la comparación del fingerprint en el adapter, en una rama descartable, y corré el test de conflicto. Tiene que fallar. Si sigue verde, el test no defiende la regla: se reescribe antes de seguir. Después restaurá el código y guardá la salida de ambas corridas. Esa secuencia (falla sin la protección, pasa con ella) es la única evidencia de que el test protege algo concreto.
Si el gate de integración no puede correr porque no tenés PostgreSQL local, decilo en el cierre. No lo declares “pasa”. Esa frase vale más que cualquier captura bonita, porque es la que te protege cuando el revisor pregunta.
¿Qué evidencia cierra el criterio “Los tests relevantes fueron ejecutados y su salida es verificable”?
Paso 6 · Revisión semántica y segundo agente
Objetivo. Revisar el diff por autoridad, invariantes, idempotencia y alcance, y tratar las observaciones de un segundo agente como hipótesis.
Tiempo sugerido. 25 minutos.
Antes de pedir la segunda revisión, revisá vos. Usá el orden de la lección 9.2: alcance, autoridad, invariantes, transacción, idempotencia, tests y estilo. Después pasale al segundo agente solo la spec y el diff, con el prompt de revisión que sigue. Que no reciba tu chat: su utilidad está en no compartir tu sesgo.
PROMPT DE REVIEW
Revisá este diff como si fuera código de otro equipo. Prioridad: 1) contradicciones con CU/RN, 2) autoridad de datos, 3) garantías mínimas y transacciones, 4) concurrencia/idempotencia, 5) autorización/auditoría, 6) cambios fuera de alcance, 7) tests que no ejercitan el riesgo. No evalúes estilo hasta terminar semántica. Citá archivo y comportamiento.
El segundo agente va a devolver observaciones de distinta calidad. Algunas son reales, otras son plausibles y falsas, y algunas son de estilo disfrazadas de defecto. Cada una se trata con una de tres salidas: reproducirla con un comando o test, descartarla con un motivo escrito, o llevarla al bundle como pregunta si es una decisión de significado.
¿Qué hacés con cada observación del revisor?
Elegí la salida correcta para cada observación: reproducir, descartar o llevar al bundle.
El test de conflicto no cubre líneas del mismo SKU en otro orden, y el fingerprint debería darles el mismo resultado.
Deberías usar bcrypt para calcular el fingerprint del pedido.
ADR-006 no dice si el depósito del fingerprint es el preferido o el efectivo.
La función exportada cambió de nombre y otro módulo todavía la importa con el nombre anterior.
Falta un comentario JSDoc en normalizeLines.
Ver cómo se ve un rechazo bien argumentado
Un rechazo sirve cuando deja la razón por escrito. Por ejemplo: “Observación del revisor: falta el índice por request_id. Descartada: la búsqueda es por clave primaria de la tabla de idempotencia, que ya tiene índice. Motivo verificado con el esquema de la migración 0004.” Esa línea muestra qué se revisó, contra qué y por qué se descartó. Un rechazo que dice solo “no aplica” no sirve para la próxima sesión ni para el mentor.
Cuaderno de campo 09 · Diario de decisiones con IA
Entregable: una página de registro por sesión de agente.
- Objetivo y bundle entregado.
- Plan propuesto y correcciones humanas.
- Supuestos detectados y resolución.
- Checkpoints, comandos y resultados.
- Cambios rechazados por fuera de alcance.
- Preguntas devueltas a spec.
Criterio de revisión. Revisión: el diario no debe ser transcripción completa. Debe permitir auditar dónde mantuviste autoridad y por qué aceptaste el diff.
Hoja de entrega 9 · completá antes de pasar el gate
Este bloque no se completa “de memoria”. Volvé a tus artefactos, evidencia y resultados. El objetivo es obligarte a hacer explícito qué vas a defender frente a un mentor o cliente.
Artefacto que entrego y versión:
Tres evidencias que sostienen mis decisiones:
Decisiones que tomé yo y que un agente no podía tomar:
Desconocidos o riesgos que siguen abiertos:
Feedback o propuesta de IA que rechacé, y por qué:
Qué tendría que ocurrir para que yo mismo cambie esta conclusión:
Revisión de calidad: qué corregí antes de enviar o presentar:
Firma del mentor/reviewer o referencia del feedback:
Gate 9 · un cambio generado con IA se puede integrar
Un gate no es una formalidad. Si una condición crítica no se cumple, el trabajo correcto es hacer visible la incertidumbre y volver a buscar evidencia.
Criterios de revisión
Revisá tus artefactos antes de marcar cada criterio. El checklist registra tu autoevaluación; la aprobación requiere revisar la evidencia.
Errores frecuentes
- Síntoma: el test de conflicto pasa en la primera corrida, sin que lo hayas visto fallar. Causa: el test no ejercita el caso (por ejemplo, reutiliza el mismo contenido) o el fixture ya tenía el pedido creado. Corrección: ejecutarlo contra el código anterior y confirmar la falla por la aserción de conflicto, no por error de setup.
- Síntoma: el agente agrega un campo
fingerprinta la tabla y una migraciónNOT NULLen el mismo cambio. Causa: el plan no separó compatibilidad de limpieza. Corrección: migración aditiva y nullable, backfill después,NOT NULLen un cambio posterior con aprobación. - Síntoma: el fingerprint cambia cuando el cliente manda las mismas líneas en otro orden. Causa: la normalización no ordena ni suma por SKU. Corrección: el test de orden invertido y el paso de normalización de ADR-006.
- Síntoma: la revisión del segundo agente produce diez observaciones y todas se aceptan. Causa: nadie las trató como hipótesis. Corrección: reproducir, descartar con motivo o llevar al bundle, una por una.
- Síntoma: el cierre dice “gates en verde” y no hay salida guardada. Causa: el resumen reemplazó a la evidencia. Corrección: pegar el comando, el exit code y la línea de resumen de cada gate.
Para llevarte
- El cambio E09 se apoya en RN-06 con su definición completa, ADR-005 y ADR-006. Si falta la definición, la respuesta es una pregunta, no un valor.
- El test rojo va primero y se verifica por la aserción correcta. Un test que nunca falló no protege nada.
- La normalización del fingerprint sigue ADR-006 al pie de la letra. El depósito del fingerprint es una pregunta de spec mientras no esté respondida.
- Las observaciones de un segundo revisor son hipótesis: se reproducen, se descartan con motivo o vuelven al bundle.
- El gate se cierra con comandos, exit codes y salida guardada. Lo que no pudiste ejecutar se declara.
Guardá tu entrega con el cuaderno de trabajo.
Completá la entrega 09, adjuntá evidencia y registrá los criterios del gate que todavía no podés demostrar.
Cuando termines tu entrega, compará tu razonamiento con la solución comentada E09. No hay una única respuesta correcta: la comparación busca fuentes, decisiones y evidencia.
¿Cómo valoras el contenido técnico de esta lección?
Tu feedback nos ayuda a mejorar los laboratorios prácticos de Eje Z.