Al terminar esta lección
- Explicar por qué construir con un agente desplaza el cuello de botella de escribir a revisar, y diseñar el proceso en función de eso.
- Redactar un
AGENTS.mdcon comandos, límites y fuentes de autoridad que un agente pueda seguir sin conversación previa. - Completar un Specification Bundle de nueve puntos para un slice y probarlo con una consigna adversarial antes de usarlo.
- Pedir y evaluar un plan de implementación: detectar archivos de más, tecnología inventada y decisiones de negocio disfrazadas de detalle técnico.
- Decidir cuándo continuar una sesión y cuándo abrir una nueva sin perder autoridad.
9. Construir con agentes de IA sin hacer vibe coding
Hasta el módulo 8 todo el trabajo fue entender, especificar y decidir. Ahora hay código, y el agente puede escribirlo muy rápido. El problema es qué más decide mientras escribe.
Vibe coding, en este curso, es trabajar con un agente sin fijar de antemano qué se construye, con qué reglas ni cómo se comprueba, y aceptar el resultado porque "funciona" en una demo o porque compila. No es un insulto a la IA: es una descripción del proceso. El agente completa los huecos de la especificación con lo más plausible, y lo plausible es una regla de negocio, una dependencia o un estado que nadie aprobó. Esas decisiones aparecen después, mezcladas en un diff de mil líneas.
Lo contrario no es "desconfiar del agente". Es restringir el espacio de decisión hasta que lo único que le quede por decidir sean detalles de implementación que se pueden verificar con una ejecución. Todo el módulo 9 gira alrededor de ese objetivo, y el módulo 10 se ocupa de la mitad que falta: demostrar que lo construido hace lo prometido.

9.1 El costo se mueve: de escribir a revisar
Problema. Un desarrollador intermedio escribe, con suerte, unas pocas decenas de líneas productivas por hora de trabajo concentrado. Un agente escribe cientos en minutos. Si el proceso no cambia, el equipo recibe más código del que puede entender, y el único control que le queda es "los tests pasan".
Definición operativa. Construir bien con agentes significa que cada cambio cumple tres condiciones: (1) el alcance se acordó antes de escribir, (2) el diff es lo bastante chico como para que una persona lo explique entero, y (3) hay evidencia ejecutada de que se cumple la regla que lo motivó. Si falta una de las tres, el cambio no está listo, por rápido que haya sido producirlo.
Ejemplo en Nexo. Le pedís al agente "implementá CU-01". Vuelve con 1.400 líneas: dominio, use case, adapter, controller, migración, y una función que libera reservas a las 24 horas "para completar el flujo". Typecheck y tests pasan. El problema no es que el código sea malo. Es que ahora tenés que revisar a la vez la regla de crédito, el cálculo de precio, la concurrencia de stock y una política de expiración que Nexo todavía no definió (RN-07 sigue BLOCKED). Cualquier error está escondido entre mil líneas correctas.
Contraejemplo. El mismo trabajo en cinco checkpoints: tipos e invariantes, use case con puertos, adapter con prueba concurrente, boundary, migración. Cada uno tiene su plan aprobado, su test rojo antes del código y un diff de entre 40 y 150 líneas. Los seis checkpoints (A a F) cubren tipos e invariantes, use case con puertos, adapter con prueba concurrente, boundary, migración y E2E selectivo. En total hay más trabajo de coordinación, pero cada pieza se revisa en minutos, y la función de 24 horas no llega a existir porque el plan del checkpoint C no la nombra.
Cómo se verifica. Con una pregunta que podés hacerte antes del merge: ¿puedo explicar, sin releer el chat, por qué cada archivo cambió y qué regla lo exige? Si la respuesta es "no sé por qué tocó ese archivo", el scope falló, no el código.
El mismo pedido al agente, sin y con fronteras
Implementá CU-01 para Nexo. Usá buenas prácticas, que sea
escalable y que tenga tests.
El agente decide qué es "escalable" (¿Redis?, ¿una cola?), qué tests escribir (los que su propia implementación pasa) y qué archivos tocar. No hay forma de saber si terminó.
Slice: SLICE-C-RESERVA (bundle adjunto, versión 3).
Tarea: checkpoint C. Solo el adapter de reserva en
src/adapters/postgres-stock.ts y su test de integración.
Invariante: RN-05. Prohibido: tocar API, dominio, nombres
públicos, agregar dependencias, definir TTL (RN-07 BLOCKED).
Primero: leé el repo y proponé un plan. No escribas código.
Hay un criterio de terminado (RN-05 con test concurrente), archivos permitidos y una lista de lo que requiere aprobación. El agente conserva toda la libertad de ingeniería dentro del adapter.
Un agente entrega 900 líneas que pasan typecheck y la suite completa. ¿Cuál es el primer problema de proceso?
9.2 El flujo: spec, test, código, revisión
El ciclo que usamos tiene un orden deliberado. La especificación va primero porque es la única fuente de autoridad: si la regla no está escrita, el agente la inventa. El test va antes del código porque es la forma ejecutable de la regla, y porque un test que nunca estuvo rojo no demuestra nada. Recién entonces viene el código, que es la parte más barata de producir. La revisión cierra el ciclo, y tiene dos salidas distintas según qué falló.
Las dos flechas que vuelven son la diferencia entre ingeniería y reintento. Si el test concurrente falla porque el UPDATE tiene una condición mal escrita, el defecto es de código y se corrige en implementación. Si el agente propone una expiración de 24 horas porque el bundle no dice nada del TTL, el defecto es de especificación: corregir el código sin tocar el bundle garantiza que la próxima sesión repita el error.
Ordená el ciclo de un checkpoint
Usá las flechas para ordenar los pasos desde el primero hasta el último.
- Posición 1 de 7: Aprobar o corregir el plan (archivos, mecanismo, gates)
- Posición 2 de 7: Escribir el test y verlo fallar por la razón esperada
- Posición 3 de 7: Ejecutar los gates y guardar comando, exit code y resultado
- Posición 4 de 7: Revisar el diff por semántica antes que por estilo
- Posición 5 de 7: Decidir el merge, o volver a la spec si el defecto era conceptual
- Posición 6 de 7: Implementar el cambio mínimo hasta que el test pase
- Posición 7 de 7: Leer el bundle y pedir al agente un plan sin código
9.3 El repositorio habla con el agente: AGENTS.md y CLAUDE.md
Problema. Cada sesión nueva empieza sin memoria. Si los comandos de test, las carpetas prohibidas y la regla de "no inventar reglas de negocio" solo viven en tu cabeza o en un chat, hay que repetirlos en cada prompt, y alguna vez los vas a olvidar.
Definición operativa. AGENTS.md es un archivo en la raíz del repo con las instrucciones permanentes para cualquier agente que trabaje ahí. Distintas herramientas buscan nombres distintos: varias leen AGENTS.md, y Claude Code lee CLAUDE.md. La práctica más simple es mantener una única fuente y que la otra la importe. Contiene lo que no cambia entre slices: cómo se corre y se prueba el proyecto, dónde vive cada cosa, qué está prohibido y dónde está la autoridad. Lo específico de cada tarea va en el bundle.
# Nexo B2B · instrucciones permanentes para agentes
Monolito modular TypeScript (ADR-001) con worker de outbox (ADR-003). PostgreSQL es la fuente transaccional de stock (ADR-002).
Dos depósitos: CBA y CEN. Los campos de dominio se llaman `depositId` /
`preferredDepositId`; en tablas, `warehouse_id`.
## Comandos (los gates)
- npm run typecheck
- npm run test:unit
- npm run test:integration -- <patrón> # necesita PostgreSQL local (docker compose up db)
- npm run lint
Nunca declares "pasa" sin haber ejecutado el comando y mostrar su salida.
Si no podés ejecutarlo (sin Docker, sin red), decilo en el cierre: no lo supongas.
## Dónde vive cada cosa
- src/domain/ reglas puras, sin I/O
- src/application/ casos de uso (CreateOrderUseCase); orquestan puertos
- src/ports/ interfaces (StockPort, PricePolicy, IdempotencyPort)
- src/adapters/ PostgreSQL y servicios externos
- tests/unit | tests/integration | tests/support
- specs/ bundles por slice, RN, AC y ADR. ES LA AUTORIDAD.
## Autoridad de datos
El cliente (frontend) nunca decide precio, crédito, estado ni permisos.
`sellerId` sale de la sesión autenticada, no del body.
Precio: PricePolicy en servidor (RN-03). Stock: PostgreSQL (ADR-002).
## Cómo trabajar
1. Antes de modificar, leé specs/ del slice y el código relevante. Citá archivo y símbolo.
2. Proponé un plan (archivos, mecanismo, tests, gates) y esperá aprobación.
3. Test primero; mostralo fallando por la razón correcta.
4. Cambios mínimos dentro de los archivos autorizados por el bundle.
## Prohibido sin aprobación explícita
- Agregar dependencias o infraestructura (Redis, colas, ORMs nuevos).
- Renombrar, mover o reformatear archivos fuera del alcance del slice.
- Cambiar contratos públicos, esquema de base o ADR.
- Inventar reglas de negocio, constantes comerciales o estados.
- Debilitar o borrar un test para que pase.
## BLOCKED (no decidir, no parametrizar con un default "razonable")
TTL de reserva (RN-07), tolerancia Platino (RN-02), cheques pendientes,
política de sustituciones, división multi-depósito, devoluciones complejas.
Si el trabajo depende de uno de estos, detenete y preguntá.
## Cierre de cada tarea
Archivos modificados · decisiones humanas usadas · supuestos abiertos ·
comandos ejecutados con resultado · tests que NO pudieron ejecutarse ·
cambios de contrato/esquema · diff fuera de alcance (ninguno o lista).
@AGENTS.md
## Notas específicas de Claude Code
- Antes de escribir código, entrá en modo plan y mostrame el plan.
- Para revisar un diff ajeno, usá una sesión nueva: no revises lo que vos mismo escribiste.
La línea @AGENTS.md importa el contenido del otro archivo, de modo que hay una sola fuente. Fijate qué no está en estos archivos: las reglas de negocio. AGENTS.md dice dónde buscarlas y qué hacer si no existen, pero no las copia. Una regla escrita en dos lugares termina con dos redacciones, y el agente seguirá la que haya leído primero.
Antipatrón. Un AGENTS.md de ocho páginas con la historia del proyecto, el estilo de código "ideal" y los valores de la empresa. Es contexto que se paga en cada sesión y que casi nunca cambia una decisión. La prueba para cada línea es: ¿qué haría distinto el agente si la borrara? Si la respuesta es "nada", sobra.
Cómo se verifica. Abrí una sesión nueva, sin explicar nada, y pedí: "resumí qué prohíbe este repo y cómo se prueba un cambio". Si la respuesta omite los gates o la lista de BLOCKED, el archivo no está cumpliendo su trabajo.
¿Dónde vive cada pieza de contexto?
Elegí el lugar donde debería estar cada pieza para que sobreviva entre sesiones sin duplicarse.
El comando para correr los tests de integración y la regla de no declarar “pasa” sin ejecutarlo.
“Una reserva nunca puede hacer disponible < 0 por depósito/SKU” con su definición completa.
La condición disponible >= cantidad, expresada en el UPDATE y comprobada por un test concurrente.
La lista de temas BLOCKED que ningún agente debe resolver con un valor por defecto.
Qué archivos están autorizados para el checkpoint C de esta semana.
La firma de StockPort.reserve y los tipos Reserved / InsufficientStock.
9.4 Anatomía de un Specification Bundle completo
El bundle es el artefacto que convierte el trabajo de discovery y de especificación en algo que un agente puede ejecutar. Sigue la plantilla de nueve puntos del módulo 6: objetivo observable, reglas con definición completa, caso de uso, invariantes, contratos, decisiones, tests y gates, fuera de alcance y unknowns que bloquean.
Este es el bundle real del slice de reserva. Su ID histórico es SLICE-C-RESERVA: es el alias del Slice 2 del backlog por valor (módulo 8), en su checkpoint C (persistencia real y prueba concurrente). La letra no define un slice técnico; nombra una etapa de construcción dentro de uno vertical.
# Specification Bundle · SLICE-C-RESERVA (v3)
Alias histórico del Slice 2 · checkpoint C (persistencia real y concurrencia).
Revisó: el Responsable del Desarrollo. Esta es la única fuente de autoridad del slice.
## 1. Objetivo observable
Al confirmar CU-01, reservar el stock de cada línea en el depósito del pedido
sin sobreventa, y garantizar que reintentar la misma intención no produce un
segundo pedido ni una segunda reserva.
Hecho cuando AC-06, AC-07 y AC-10 pasan en integración contra PostgreSQL real.
## 2. Reglas y requisitos (definición completa)
RN-05 Una reserva nunca puede hacer disponible < 0 por depósito/SKU.
disponible = onHand - reserved.
RN-06 Una misma intención de pedido, identificada por su requestId, no puede
generar más de un pedido: con el mismo requestId y el mismo contenido el
resultado es el mismo pedido; con el mismo requestId y contenido distinto
se informa un conflicto y no se produce ningún efecto nuevo.
RN-03 (solo referencia) El precio de una línea se calcula en servidor desde
lista vigente y acuerdos autorizados. Este slice consume PricePolicy;
ningún campo de precio entra por el comando.
RN-07 (BLOCKED) Una reserva blanda de un pedido pendiente de autorización
expira pasado un tiempo todavía no definido. NO implementar expiración.
NFR-01 Registro de pedido: p95 < 800 ms con 30 usuarios concurrentes (umbral
acordado, se valida con prototipo). Una sentencia por línea; sin locks
explícitos que se sostengan fuera de la transacción.
## 3. Caso de uso (CU-01, tramo que afecta este slice)
Orden: validar → resolver precio → evaluar crédito → reservar stock → registrar
pedido (marcado "pendiente de facturación"), todo en una misma transacción.
A2 Stock insuficiente: no confirma; ningún efecto parcial.
A4 Reutilización conflictiva de requestId: devuelve conflicto, sin segundo pedido.
Garantía mínima: ante cualquier fallo antes del commit no queda pedido parcial,
reserva huérfana ni clave de idempotencia sin pedido.
## 4. Invariantes
- available >= 0 en todo momento y para todo (depósito, SKU)
- una reserva por (pedido, depósito, SKU)
- misma requestId + mismo contenido = mismo resultado
- misma requestId + contenido distinto = Conflict, sin efectos
## 5. Contratos (src/ports/stock.ts, src/application/create-order.ts)
type ReserveInput = { orderId: string; depotId: string; sku: string; quantity: number };
type ReserveResult =
| { kind: "Reserved"; depotId: string; sku: string; quantity: number }
| { kind: "InsufficientStock"; depotId: string; sku: string; available: number };
StockPort.reserve(input: ReserveInput): Promise<ReserveResult>
CreateOrderCommand / CreateOrderResult: sin cambios (ver src/application/create-order.ts)
depositId efectivo = preferredDepositId ?? depósito habitual del cliente.
## 6. Decisiones que restringen el cambio
ADR-002 PostgreSQL es la fuente transaccional de stock. La reserva se hace con
actualización condicional dentro de la transacción del caso de uso.
Alternativa descartada: Redis / colas para el lock (sin ASR que lo pida).
ADR-001 Monolito modular con worker. El slice vive en el proceso de request.
ADR-003 Los efectos posteriores al commit van al worker; nada de este slice
usa el worker.
ADR-005 Idempotencia por requestId, en la misma transacción que pedido y reserva.
ADR-006 El fingerprint (request_hash) se calcula sobre cliente, depósito y líneas
normalizadas por sku/cantidad; ignora metadata de tracing.
## 7. Tests esperados y gates
AC-06 Con una unidad disponible y dos reservas concurrentes, exactamente una confirma.
AC-07 Retry idempotente: mismo requestId y mismo contenido → un solo pedido.
AC-10 Falla de persistencia antes del commit → rollback completo (sin reserva ni pedido).
Los tests de AC-06 y AC-07 corren con dos conexiones reales (pool >= 2).
Gates: npm run typecheck · npm run test:unit ·
npm run test:integration -- stock-reservation
## 8. Fuera de alcance (y archivos autorizados)
No tocar: UI, API HTTP, nombres públicos, dependencias, otros módulos.
No definir: TTL ni liberación automática (RN-07), reserva blanda de pedidos
pendientes de autorización, división de pedidos entre CBA y CEN.
Archivos autorizados:
src/ports/stock.ts · src/adapters/postgres-stock.ts · src/application/create-order.ts
tests/integration/stock-reservation.test.ts · tests/support/barrier.ts
## 9. Unknowns que detienen la implementación si aparecen
- ¿Qué pasa si el depósito pedido no tiene stock pero el otro sí? (BLOCKED: división
multi-depósito). El slice devuelve InsufficientStock.
- Cualquier necesidad de un valor de TTL, tolerancia Platino o política de sustitución.
- Cualquier cambio de esquema: se propone aparte y requiere aprobación.
Leelo como lo leería un agente que no estuvo en discovery. Hay tres cosas que cumplen la regla de oro del módulo 6 ("el agente no necesita adivinar autoridad"). Cada regla trae su definición completa, no solo el ID. Los límites son explícitos, incluidos los archivos permitidos. Y los unknowns tienen una instrucción de conducta: detenerse. Fijate también que las decisiones de arquitectura citan el número de ADR correcto: ADR-002 es PostgreSQL como fuente transaccional de stock, y ADR-003 es el monolito modular con worker. Mezclar los números es un error pequeño que después se propaga a los comentarios y al código.
Probar el bundle antes de usarlo
Un bundle se prueba con una sesión de análisis, sin código. Esta es la consigna adversarial:
Leé specs/slice-2-reserva/BUNDLE.md y AGENTS.md. No escribas código ni
leas el resto del repo todavía.
1. Enumerá cada decisión que tendrías que INVENTAR para implementar este
slice (valores, comportamientos, nombres, estados).
2. Listá las contradicciones entre RN, AC, contratos y ADR, citando IDs.
3. Para cada AC, decime qué setup necesita el test y qué efecto o no-efecto
observa (el oráculo).
Si una sección alcanza para implementar sin adivinar, escribí "sin dudas".
Un agente con el bundle anterior puede devolver, por ejemplo: "Para el depósito efectivo dice preferredDepositId ?? depósito habitual del cliente, pero no encuentro dónde se guarda el habitual. ¿Está en client.habitualDepositId?". Esa pregunta es oro: si la respuesta es "no existe", acabás de descubrir un hueco antes de que el agente invente un campo. Cada pregunta del agente se resuelve en el bundle, nunca solo en el chat, y el ciclo se repite hasta que el agente devuelve "sin dudas" o preguntas que ya son de implementación.
Probando el bundle con la consigna adversarial, el agente pregunta: “¿cuánto debe durar una reserva en espera de autorización?”. ¿Qué hacés?
9.5 Checkpoints A a F: dividir un slice para poder revisarlo
Un slice vertical (por ejemplo el Slice 2) cruza varias capas. Construirlo en un solo movimiento produce el diff gigante que queremos evitar. Lo dividimos en checkpoints de construcción, cada uno con un gate propio. Las letras son etapas técnicas dentro de un slice; no son slices distintos (eso era la confusión que critica el módulo 8: construir "toda la base de datos" antes de integrar nada).
- A. Tests e invariantes y tipos de dominio. Gate:
typechecky tests unitarios de las invariantes puras. - B. Use case y puertos. Gate: tests de aplicación con fakes controlados que pueden fallar.
- C. Adapter, transacción y concurrencia. Gate: tests de integración con PostgreSQL real. Acá vive
SLICE-C-RESERVA. - D. Boundary y contrato. Gate: contract/API tests y autorización contextual.
- E. Migración y observabilidad. Gate: ensayo de migración (expand/contract), lint, suite focalizada.
- F. E2E selectivo y trazabilidad. Gate: un par de recorridos completos y revisión de la matriz RN → AC → test antes del merge.
La regla práctica: un checkpoint se abre con su propio plan y se cierra con su propio informe de evidencia. Si el agente quiere "aprovechar" el checkpoint C para adelantar el D, el plan se rechaza y se anota la idea para después.
9.6 El plan antes del código
Problema. Descubrir que el agente entendió mal una regla después de que escribió 600 líneas cuesta una hora de revisión y un rollback. Descubrirlo en un plan de veinte líneas cuesta treinta segundos.
Definición operativa. El plan es una respuesta estructurada del agente, sin cambios en el repo, que reconstruye la intención y propone el cambio mínimo. Tiene que cubrir: el objetivo y las garantías, las RN/AC que gobiernan, los datos que faltan (marcados, no inventados), los archivos mínimos a tocar con el porqué, los tests y gates, y toda decisión de contrato, esquema o arquitectura como "requiere aprobación".
Leé specs/slice-2-reserva/BUNDLE.md y AGENTS.md. Antes de modificar nada:
1. Resumí el objetivo y las garantías con tus palabras.
2. Listá las RN, AC y ADR que gobiernan este cambio.
3. Marcá los datos que faltan. No inventes valores ni nombres.
4. Proponé los archivos mínimos a tocar y por qué cada uno.
5. Describí el mecanismo para RN-05 y por qué preserva la invariante bajo concurrencia.
6. Indicá qué tests escribirías primero y qué gates ejecutarías.
7. Marcá como "REQUIERE APROBACIÓN" cualquier cambio de contrato, esquema,
dependencia o arquitectura.
No implementes todavía.
Ejemplo en Nexo: un buen plan y uno malo. Con el prompt anterior, dos agentes devuelven planes distintos.
Plan para el checkpoint C
"Agrego Redis para locks distribuidos por SKU, extraigo StockService y ReservationService, renombro OrderRepository a SalesOrderRepository para mayor claridad y agrego un job que libera reservas viejas a las 24 horas."
Cuatro propuestas, cero referencias a RN-05, a un archivo concreto ni a un test. Redis contradice ADR-002, el renombre es un refactor ajeno al slice, el job inventa RN-07, y nada dice cómo se prueba.
"Objetivo: RN-05 por (depósito, SKU) con dos requests concurrentes. Hoy PostgresStockRepository.reserve hace SELECT y después UPDATE por separado (src/adapters/postgres-stock.ts, líneas 18-34): dos transacciones pueden leer available = 1. Propongo un UPDATE condicional (... AND on_hand - reserved >= $3) dentro de la transacción del caso de uso, y devolver InsufficientStock si no actualiza ninguna fila. Archivos: postgres-stock.ts y stock-reservation.test.ts. Test primero: dos transacciones sincronizadas con una barrera sobre stock 1. No toco el contrato, ni agrego dependencias. Pregunta abierta: ¿el depósito está resuelto antes de reservar? REQUIERE APROBACIÓN: nada por ahora."
Menciona el mecanismo, el archivo y línea real, el test y una pregunta legítima.
En el plan bueno hay algo que no se ve a primera vista: cada afirmación se puede verificar contra el repo. La referencia a las líneas 18-34 se comprueba abriendo el archivo. Un plan sin referencias verificables es una opinión.
Reescribí un pedido abierto como pedido acotado
Un compañero le escribe al agente: “Agregá validación de múltiplos de venta (RN-08) en CU-01 y que quede todo prolijo”. Reescribí el prompt como lo harías vos: con objetivo observable, fronteras permitidas y prohibidas, qué se pide primero y qué debe hacer el agente si falta autoridad para el dato.
9.7 Primero leer el repositorio
Cuando trabajás sobre código existente, el prompt no debería empezar con "creá un servicio que...". Primero pedile al agente que localice la implementación actual, los contratos, los tests, las migraciones y las convenciones. Un modelo puede escribir una solución plausible ignorando que el repo ya tiene una abstracción equivalente, que existe una transacción envolvente o que un test de contrato define otro comportamiento.
No modifiques archivos. Localizá dónde se implementan CU-01, RN-05 e
idempotencia. Mostrame: entrypoint, use case, puertos, adapter PostgreSQL,
migraciones y tests, con referencias archivo:línea. Explicá el flujo actual y
marcá contradicciones con el bundle. Si un símbolo no existe, decilo:
no inventes nombres.
Lo importante viene después: verificás la respuesta con búsquedas reales, no con otra pregunta al agente.
# ¿Dónde se abre realmente la transacción del caso de uso?
rg -n "transaction\(|uow.run\(" src/
# ¿Qué adapters implementan StockPort?
rg -n "implements StockPort|StockPort" src/
# ¿Hay algo que ya haga reserva con locks o SELECT FOR UPDATE?
rg -n "FOR UPDATE|reserved = reserved" src/ db/
Si el agente afirmó que CreateOrderUseCase abre la transacción y la búsqueda muestra que la abre el repository, ya tenés una comprensión incorrecta atrapada antes de que cambie una sola línea. Pedirle que cite archivo y símbolo convierte afirmaciones en hipótesis comprobables.
9.8 Contexto pequeño y cuándo abrir una conversación nueva
Optimizar tokens no es resumir las reglas hasta volverlas ambiguas. Es eliminar redundancia y contexto irrelevante. Un bundle usa IDs: el CU no copia veinte líneas de RN, las cita, y el bundle trae la definición completa de las RN que sí gobiernan el cambio. Los logs se recortan alrededor del fallo. El diff se revisa por archivos focales. Ahorrás repetición, no significado: RN-01 puede ocupar una línea en un lugar y estar completa en otro, pero "manejar crédito" no es una regla.
El criterio para abrir una sesión nueva no es "ya escribimos mucho". Es si cambió el objetivo, o si el contexto acumulado contiene supuestos que ya no valen.
- Mismo slice, mismo objetivo y contexto coherente: continuá.
- Nuevo slice o nueva clase de tarea (pasar de implementar a auditar): sesión nueva con el bundle correspondiente.
- El agente insiste con una arquitectura descartada: cerrá, corregí el artefacto si hacía falta y reiniciá desde fuentes limpias.
- Review de un diff importante: usá una sesión que no lo haya producido, para reducir la autojustificación.
Y la contracara: no abras una sesión nueva para esconder una decisión que nunca documentaste. El reset solo es seguro si la fuente de verdad está en el repo. Esto vale también para las sesiones largas: una decisión como "ADR-006 define el fingerprint" tiene que estar en specs/ antes de que termine la conversación en la que se tomó.
9.9 Nexo B2B · De una ambigüedad a un plan aceptable
Marta ya aprobó que reintentar un pedido no debe duplicarlo, y el equipo quiere ir un paso más: que reutilizar un requestId con un pedido distinto sea un conflicto explícito. Es el cambio que hacés vos en el taller (práctica E09). Esta es la primera parte de la conversación, hasta el plan.
El prompt inicial pide análisis, no implementación:
Necesitamos que reutilizar el mismo requestId con contenido distinto devuelva
conflicto (RN-06). No implementes. Leé el bundle y el código actual. Decime
dónde se guarda la clave, cómo se compara la intención hoy y qué tests faltan.
El agente responde algo así:
Encontré `idempotency_keys(request_id, order_id, created_at)` en
db/migrations/0004_idempotency.sql. `PostgresIdempotency.claim` hace
INSERT ... ON CONFLICT DO NOTHING y, si la clave existía, devuelve
{ kind: "replay", orderId } sin comparar contenido: src/adapters/
postgres-idempotency.ts:14-27. No existe ningún fingerprint del payload.
Falta distinguir un retry legítimo de una reutilización conflictiva.
Antes de proponer el cambio necesito saber qué cuenta como "mismo contenido":
¿el orden de las líneas? ¿dos líneas del mismo SKU equivalen a una con la
suma? ¿participan campos que no son de negocio, como sellerId o un trace id?
Acá el agente encontró un hueco real y lo devolvió como pregunta. La respuesta incorrecta del estudiante sería contestar sobre la marcha ("el JSON tal cual") y seguir. La semántica del fingerprint es una decisión de especificación: cambia qué retries se consideran legítimos. Por eso el estudiante vuelve al repo y la escribe como ADR:
# ADR-006 · Fingerprint de idempotencia
Estado: aceptada
Contexto: RN-06 exige distinguir retry legítimo de reutilización conflictiva del
requestId. Comparar el JSON crudo trata como distintos dos pedidos equivalentes
(líneas en otro orden) y como iguales dos que difieren en un campo ignorado.
Decisión: request_hash = SHA-256 de una representación canónica de cliente,
depósito y líneas normalizadas por sku/cantidad (SKU sin espacios y en mayúsculas,
cantidades del mismo SKU sumadas, líneas ordenadas por SKU). Se ignora la
metadata de tracing, requestId y sellerId.
Consecuencias: un retry con líneas reordenadas sigue siendo el mismo pedido.
Cambiar un SKU o una cantidad es conflicto. Las filas legacy no tienen hash.
Disparador de revisión: aparece un campo comercial nuevo en el comando
(por ejemplo deliveryMethod: ver pregunta abierta).
Fijate en la última línea del ADR. CreateOrderCommand ya incluye deliveryMethod: "pickup" | "delivery", y el ADR no lo menciona. ¿Es la misma intención un pedido de retiro y otro de entrega con el mismo requestId? El agente no puede decidirlo, y tampoco hay que tapar la duda: queda como pregunta abierta con dueño y cambia el bundle recién cuando alguien la resuelve. En el taller vas a ver cómo se maneja ese caso.
Con la spec actualizada, el estudiante pide el plan:
ADR-006 está en specs/adr/. Proponé el cambio mínimo: sin tocar el controller
ni nombres públicos. Incluí migración expand/contract y tests de retry igual
y de retry conflictivo.
El plan que vuelve es revisable porque hace visible la compatibilidad: (1) migración aditiva que agrega request_hash nullable; (2) las escrituras nuevas lo completan; (3) claim compara el hash cuando existe; (4) test: mismo requestId y mismo contenido conserva un único pedido; (5) test: mismo requestId y contenido distinto devuelve conflict y no crea efectos; (6) prueba con una fila legacy sin hash, que mantiene el comportamiento anterior y registra una métrica para el backfill. "No haré el campo NOT NULL todavía."
Ahora tu trabajo como revisor: ¿realmente hay que tolerar filas sin hash? ¿Hay una ventana de deploy en la que conviven versiones? ¿Quién hace el backfill? Esas preguntas se hacen antes de habilitar el código. El agente no debería combinar migración destructiva, cambio de lógica y eliminación del fallback en un solo movimiento mientras producción todavía tiene datos antiguos.
Qué puede hacer el agente: explorar el repositorio y citar archivos y símbolos, proponer un plan con alternativas, escribir tests y código dentro de un alcance acotado, resumir diffs, buscar inconsistencias entre el bundle y el código, y ejecutar comandos.
Qué no debe decidir: una regla de negocio nueva, un valor BLOCKED (TTL, tolerancia Platino), un cambio de arquitectura o de contrato, ni aprobar su propio trabajo.
Contexto mínimo útil: AGENTS.md, el bundle del slice, los archivos focales y los comandos de los gates. Nada de "todo el repo" ni de reuniones enteras.
Cómo verificar: cada afirmación del plan con archivo y línea se comprueba con una búsqueda; el cierre trae comandos con su salida; el diff se compara con el plan aprobado.
Prompt de ejemplo (auditoría de plan):
Rol: revisor de planes. No implementes.
Te paso un plan de cambio (adjunto) y el bundle del slice.
1. Para cada archivo del plan, ¿qué regla o AC lo exige? Si ninguna, marcalo.
2. ¿Hay tecnología, estados o constantes que no existen en el bundle?
3. ¿Alguna afirmación sobre el repo que debería poder verificarse con rg?
Dame el comando.
4. ¿Qué decisión del plan es de negocio y no técnica?
Respondé con una lista; si algo está bien, decí por qué.
Cómo revisar su salida: ejecutá los comandos que te propone, tachá los hallazgos que no puedas reproducir y llevá al bundle (no al chat) los que sean reales.
9.10 Invenciones plausibles que vas a ver seguido
Los agentes no mienten por maldad: completan huecos con lo más probable. Estas son las invenciones que más veces aparecen en proyectos como Nexo, con su respuesta.
- "Agregué Redis para evitar carreras." La tecnología no prueba la invariante y suma un sistema distribuido. Respuesta: volver a ASR/invariante (ADR-002) y exigir el test concurrente primero.
- "Permití 5% de sobregiro VIP." Regla de negocio inventada (RN-02 sigue BLOCKED). Respuesta: rechazar y volver a RN/evidencia; el porcentaje, si existe, lo decide Nexo.
- "Tomé
unitPricedel request para respetar lo que vio el cliente." Traslada la autoridad de precio a un input manipulable (RN-03). Respuesta: resolver el precio en servidor. - "Reintento toda la transacción si falla el envío a preparación." Puede duplicar efectos o frenar el negocio por una salida secundaria. Respuesta: separar el efecto post-commit con outbox (ADR-004) e idempotencia.
- "Refactoricé 28 archivos para limpiar la arquitectura." Scope oportunista: el diff no se puede revisar ni revertir. Respuesta: separar el refactor o limitar al slice.
¿Qué tipo de defecto es cada hallazgo?
Decidí si hay que corregir el código o volver a la especificación.
El test concurrente falla: el UPDATE usa > en vez de >= y rechaza la última unidad.
El agente implementó una expiración de reservas a las 24 horas.
El agente duda si el orden de las líneas cambia la intención del pedido (fingerprint).
El mapeo del resultado InsufficientStock a { kind: "rejected", reason: "stock" } devuelve "credit".
Vocabulario del módulo
Construcción con agentes
Frente
Vibe coding
Errores frecuentes
- Síntoma: el agente entrega un diff enorme con "todo lo necesario". Causa: el prompt pedía el slice entero y no fijaba checkpoints ni archivos permitidos. Corrección: dividir en checkpoints A–F y listar archivos autorizados en el bundle.
- Síntoma: en cada sesión hay que repetir cómo se corren los tests. Causa: esa información vive en tu cabeza. Corrección:
AGENTS.mdcon los gates y la regla de mostrar la salida. - Síntoma: dos sesiones implementan la misma regla con valores distintos. Causa: la regla está en el chat o duplicada con redacciones distintas. Corrección: una definición única en el bundle y referencias por ID.
- Síntoma: el agente "resuelve" un unknown con una constante. Causa: el bundle no marca los BLOCKED ni dice qué hacer. Corrección: punto 9 del bundle con la instrucción de detenerse, y lista de BLOCKED en
AGENTS.md. - Síntoma: el plan aprobado no se parece al diff. Causa: nadie comparó. Corrección: el cierre de cada tarea incluye "diff fuera de alcance"; si no es "ninguno", la tarea no se cierra.
- Síntoma: el agente "confirma" que algo existe en el repo y no existe. Causa: el plan no obligaba a citar archivo y símbolo. Corrección: prompt de exploración con referencias
archivo:líneay verificación conrg.
Para llevarte
- Con agentes el cuello de botella es la revisión: diseñá el proceso para que cada diff sea explicable por una persona.
- El orden es spec, plan, test rojo, código, gates, revisión y merge humano. Las dos vueltas (a código o a spec) dependen de qué falló.
AGENTS.mdguarda lo permanente (comandos, límites, autoridad); el bundle guarda lo específico del slice. Las reglas de negocio no se copian: se citan por ID.- Un bundle de nueve puntos con reglas completas, fuera de alcance y unknowns con instrucción de detenerse se prueba con una consigna adversarial antes de usarlo.
- Un plan bueno cita archivos y líneas verificables, nombra el test y marca lo que requiere aprobación. Un plan que agrega tecnología o reglas se rechaza.
- Lo que importa para el próximo agente se escribe en el repo, no en el chat.
¿Cómo valoras el contenido técnico de esta lección?
Tu feedback nos ayuda a mejorar los laboratorios prácticos de Eje Z.