Skip to content

Ejemplo end-to-end — El "golden path"

Para qué sirve. Es la pieza que la gente copia. Una User Story real viajando de idea vaga a desplegada, mostrando el artefacto concreto que produce cada uno de los 10 pasos de SCRUM-CON-IA.md. Si tienes que mostrar la metodología en una sola lectura, es esta.

Dominio del ejemplo: un carrito de compras (checkout). Los nombres (ABC-482, frontend) son ilustrativos.

El mapa: qué artefacto sale de cada paso

PasoSkill / eventoArtefacto que produce
1 Refinamientogrill-user-storygrill-intent (Gate 0)la US (ABC-482) + intent.md
2 Planningopsx:proposeproposal.md + design.md + tasks.md + specs/
3 Ramalink-usbranch + implements.yaml
4 Implementaciónopsx:apply (con TDD)test (RED) → código (GREEN), por el agente
5 Smokeskill de smokeescenario end-to-end verde
6 Code reviewreview propio del dev → dai pr → partner (con /dai-review)PR (código + spec) + aprobación
7 Mergedai stampcobertura estampada en el tracker
8 Daily— (humano)sincronización verbal
9 Review/Democriterios GherkinUS aceptada por el PO
10 Retro— (humano)1–2 mejoras

Paso 1 — Refinamiento: de la idea vaga a la US testeable

1a · Cómo empieza (el ticket vago)

El PO crea un ticket en el gestor. Nace con identidad (ABC-482) y poco más:

ABC-482 · "Arreglar lo del carrito que a veces deja comprar de más"

Impresentable para implementar. Acá entra la IA.

1b · grill-user-story produce la US

Acá entra la IA: grill-user-story interroga al PO hasta que la US es testeable por construcción (formato-us.md) y la publica en el gestor:

markdown
# 🔗 Metadata de trazabilidad
| Campo        | Valor                        |
|--------------|------------------------------|
| ID           | ABC-482                      |
| spec_version | v1                           |
| Autor        | J. Pérez (PO)                |
| Estado       | pulida                       |
| Repos esperados | frontend                  |

# Finalizar la compra del carrito

## Historia
Como **comprador**
quiero **finalizar la compra de mi carrito**
para **recibir los productos que elijo sin sorpresas**.

## Casos de uso
- **Happy path** — el comprador finaliza un carrito con productos con stock → se crea la orden.
- **Alternativo** — intenta finalizar con el carrito vacío → el sistema lo frena.
- **Excepción** — un producto del carrito no tiene stock → se rechaza indicando cuál.

## 🔗 Criterios de aceptación
- [ ] **AC-1**
  - **Dado** un carrito con productos que tienen stock
  - **Cuando** el comprador finaliza la compra
  - **Entonces** se crea la orden y el carrito queda vacío
- [ ] **AC-2**
  - **Dado** un carrito vacío
  - **Cuando** se intenta finalizar la compra
  - **Entonces** el sistema lo rechaza y NO crea ninguna orden
- [ ] **AC-3**
  - **Dado** un carrito con un producto sin stock
  - **Cuando** se intenta finalizar la compra
  - **Entonces** se rechaza indicando qué producto no tiene stock

## Fuera de scope
- El pago (pasarela, tarjetas) es otra US.

## Reglas de negocio
- Una orden creada descuenta el stock de cada producto.

Nota que la US no dice tablas, endpoints ni framework — solo el QUÉ. Y cada AC es un test en potencia (Art. 3 del manifiesto).

1c · Gate 0 — grill-intent desafía el problema

Con la US ya formada, antes de invertir en el spec, el Gate 0 cuestiona el problema detrás (contra el MANIFIESTO.md):

IA:  ¿Qué duele HOY, concretamente? "Comprar de más" es un síntoma.
PO:  Un cliente finalizó la compra de un producto que ya no tenía stock, y quedó
     una orden que no podemos cumplir. También pasa que finalizan con el carrito
     vacío y se genera una orden fantasma.
IA:  ¿Quién siente el dolor? "El sistema" no es un usuario.
PO:  El comprador (recibe un error después de comprar) y depósito, que recibe
     órdenes que no puede despachar.
IA:  ¿Y si no lo hacemos? (costo de no actuar)
PO:  Órdenes que se cancelan a mano, clientes enojados. Pasó bastante el último mes.
IA:  La solución implícita es "validar el carrito al finalizar". ¿Es el salto correcto,
     o el problema real es que el stock que muestra el carrito no es confiable?
PO:  No, validar al finalizar es lo correcto: el stock ya existe, falta chequearlo
     en el momento de la compra.

Veredicto: a-spec  ✅  (problema real, usuario concreto, costo claro, sin solution-lock)

→ produce openspec/intents/20260703-checkout-invalido/intent.md con ese veredicto. Si hubiera dado reframe o don't build, la US volvería al PO antes de gastar un solo artefacto de spec — ese es el punto del Gate 0.


Paso 2 — Planning: opsx:propose deriva el CÓMO

El dev corre opsx:exploreopsx:propose sobre la US. OpenSpec genera el design y las tareas. El dev valida y ajusta:

markdown
# design.md (extracto)
## Enfoque
Guard de validación en el caso de uso `FinalizarCompra`. El carrito se valida ANTES
de crear la orden: no vacío, y todos sus productos con stock.

## Reglas de la transición
  carrito con stock   ──finalizar──► orden creada, carrito vacío   (permitido)
  carrito vacío       ──finalizar──► ✗ CarritoVacioError
  producto sin stock  ──finalizar──► ✗ SinStockError(producto)
markdown
# tasks.md (extracto)
- [ ] T1. Test: finalizar carrito con stock → orden creada, carrito vacío (AC-1)
- [ ] T2. Test: finalizar carrito vacío → CarritoVacioError, sin orden (AC-2)
- [ ] T3. Test: producto sin stock → SinStockError con el producto (AC-3)
- [ ] T4. Guard de validación en FinalizarCompra
- [ ] T5. Smoke end-to-end del flujo

Las tareas nacen del cómo, definidas por quien va a implementar — no bajadas desde arriba (Art. 1).


bash
$ dai link-us ABC-482 --us us.md --change finalizar-compra
 branch:  feature/ABC-482-finalizar-la-compra-del-carrito
 archivo: openspec/changes/finalizar-compra/implements.yaml  (ac_hash 7f3a9c2e)
yaml
# implements.yaml — el ÚNICO link AUTORADO: se escribe, no se deriva (schema ADR-0004)
change: finalizar-compra
repo:   frontend

implements:
  - id: ABC-482
    version: v1
    ac_hash: 7f3a9c2e          # lo calculó `dai ac-hash` sobre los criterios de la US v1

introduces:
  - guard-carrito-vacio

autor: D. Force (dev)

El key ABC-482 no se tipeó: salió del argumento. La rama y el link son correctos por construcción (Art. 8, Art. 9).


Paso 4 — Implementación: el agente construye con TDD (/opsx:apply)

/opsx:apply implementa las tareas del change, un test a la vez. Vertical slice del AC-2 (el guard del carrito vacío). Primero el test (RED):

typescript
test("un carrito vacío no se puede finalizar", async () => {
  const carrito = await nuevoCarrito({ items: [] });

  const accion = finalizarCompra(carrito.id);

  await expect(accion).rejects.toThrow(CarritoVacioError);
  expect(await ordenesDe(carrito.id)).toHaveLength(0); // NO se creó ninguna orden
});
// ▶ FALLA: finalizarCompra todavía no valida el carrito.

Después el código mínimo (GREEN):

typescript
export async function finalizarCompra(id: CarritoId) {
  const carrito = await repo.obtener(id);
  if (carrito.items.length === 0) throw new CarritoVacioError(id);
  const sinStock = carrito.items.filter((i) => !hayStock(i));
  if (sinStock.length > 0) throw new SinStockError(sinStock);
  const orden = await crearOrden(carrito);
  return repo.vaciar(carrito, orden);
}
// ▶ VERDE. Repetir el ciclo para AC-1 y AC-3.

El test (que escribió el agente) verifica por la interfaz pública (finalizarCompra, ordenesDe), no espía lo interno. Sobrevive a un refactor (Art. 7 + skill tdd). El dev revisa cada slice: es responsable del código.


Paso 5 — Smoke: el flujo entero, verde

$ smoke checkout
✓ carrito con stock → orden creada, carrito vacío
✓ carrito vacío → rechazado, sin orden
✓ producto sin stock → rechazado indicando el producto
SMOKE OK (3/3)

Paso 6 — Code review: el dev primero, después un partner

Primero el dev revisa la implementación de la IA — minucioso y con criterio (correctitud, casos borde, seguridad, calidad). El dev es responsable del código, no la IA (anti vibe-coding). Ajusta y commitea lo que haga falta.

Con el smoke verde y todo commiteado (lo que quede suelto no entra en la PR), crea la PR con dai pr — precargada con la US, el estado del check y los links (los dos activos: código + spec trazable) — y la asigna a un partner:

$ dai check
✅ ABC-482 al día (v1)
$ dai pr --assignee mgomez \
    --description "Permite comprar sin crear cuenta: el checkout acepta un email de
                   contacto y genera la orden como invitado. Baja el abandono del paso 2."
✓ PR #123 creada → …/pull/123   (base: main · US: ABC-482 @ v1 · dai check ✅)

El partner revisa la PR. Se apoya en la skill /dai-review para un primer pase: deja un review inline (resumen + un comentario anclado por línea, low/medium/high). La skill le muestra el preview y espera su OK antes de postear; después el partner firma aprobación o rechazo:

🤖 /dai-review — resumen
  US: ABC-482 @ v1 · dai check: ✅ al día · DoD: 5/5
  1 comentario en línea: 1 🔵 Low.

🔵 Low — src/checkout/errors.ts:12
  CarritoVacioError y SinStockError podrían extender un DomainError común,
  como el resto del módulo.

👤 M. Gómez (partner): reviso el preview, lo posteo, y apruebo tras el ajuste.

El dev revisa su propio código; un partner distinto revisa la PR y firma (Art. 5). El review sale con el nombre y el token del partner (nunca APPROVE automático): la skill asiste, la persona firma.


Paso 7 — Merge: la trazabilidad se estampa sola

Al mergear, se corre dai stamp (el dev, o el CI si está automatizado — ADR-0003). Deduce que la US de esta rama es ABC-482, lee el implements.yaml y estampa la cobertura inversa en el ticket, con links a la implementación:

ABC-482 · implementado por  (lo estampó dai stamp)
┌──────────┬───────────────────┬─────────┬──────────┬───────────┐
│ repo     │ change            │ versión │ ac_hash  │ estado    │
├──────────┼───────────────────┼─────────┼──────────┼───────────┤
│ frontend │ finalizar-compra  │ v1      │ 7f3a9c2e │ ✅ al día │
└──────────┴───────────────────┴─────────┴──────────┴───────────┘
  branch → …/tree/feature/ABC-482-finalizar-la-compra-del-carrito
  commit → …/commit/abc123   (ancla durable)

El estado se deriva, no se reporta (Art. 10). El link (branch + commit) hace que el ticket sea un router hacia la implementación real (§2.5).


Paso 8 — Daily (humano, a propósito)

"Ayer cerré ABC-482, la validación del checkout con el guard de carrito. Hoy tomo ABC-490. Sin trabas." — La IA no genera esto; el equipo se sincroniza (Art. 6).

Paso 9 — Review / Demo

El PO valida contra los mismos criterios que ya eran tests. Finaliza un carrito, prueba con uno vacío, prueba con un producto sin stock, ve los rechazos. AC-1, AC-2, AC-3 verdes → US aceptada. Cero sorpresas: si el QUÉ hubiera cambiado, el @version lo habría gritado antes.

Paso 10 — Retro (humano)

"El Gate 0 nos ahorró rediseñar el stock, que no hacía falta. Mejora para el próximo sprint: sumar el smoke al pipeline y no correrlo a mano." La matriz de trazabilidad aportó el dato; la decisión la tomó el equipo (Art. 6).


Epílogo — Y cuando el QUÉ cambia (el @version gritando solo)

Dos sprints después, el PO agrega un criterio a ABC-482 (ahora exige avisar al comprador qué productos quedaron sin stock, sin cancelar el resto del carrito).

Lo hace con dai edit-us ABC-482: la baja del tracker, la abre en su editor, valida el formato cuando guarda, y le pregunta si el cambio es material o editorial.

  ── formato de la US (.dai/us/ABC-482.md) ────────────
  ✓ formato válido — 4 criterio(s), 4 en Gherkin completo

  Cambiaron los criterios (ac_hash 7f3a9c2e → b81d40f5).
    s = cambio material: subo spec_version a v2 y los repos con v1 se marcan ATRASADOS
    n = cambio editorial (typo, redacción): se queda en v1
  ¿Subo spec_version a v2? (S/n) s

  ── ABC-482 · qué cambia en jira ────────────
  título:     Finalizar la compra del carrito  (sin cambios)
  criterios:  4
  version:    v1 → v2
  ac_hash:    7f3a9c2e → b81d40f5
  ──────────────────────────────────────
  Esto PISA la US ABC-482 en jira. ¿Guardo? (s/N) s
✓ ABC-482 actualizada en jira  →  https://acme.atlassian.net/browse/ABC-482

La US sube a v2 → cambia el ac_hash.

ABC-482 · implementado por
┌──────────┬───────────────────┬─────────┬──────────┬────────────────────────────┐
│ repo     │ change            │ versión │ ac_hash  │ estado                     │
├──────────┼───────────────────┼─────────┼──────────┼────────────────────────────┤
│ frontend │ finalizar-compra  │ v1      │ 7f3a9c2e │ ⚠️  ATRASADO (la US es v2)  │
└──────────┴───────────────────┴─────────┴──────────┴────────────────────────────┘

Nadie le avisó al dev: dai check lo marcó solo al re-derivar el hash de la US viva y compararlo con el estampado (Art. 11). El dev abre una nueva iteración —mismo ABC-482, ahora contra v2— y el ciclo vuelve a empezar desde el paso 3.


Qué demuestra este recorrido

  • Los 10 pasos son tu Scrum de siempre — solo que en cada uno hay una skill.
  • El link nunca se escribió dos veces: implements.yaml una vez, la cobertura se derivó.
  • Nada llegó a producción sin ser testeable y trazable (Arts. 3, 9, 10).
  • Los rituales humanos siguieron siendo humanos (Art. 6).
  • Y cuando el negocio cambió, el desajuste se hizo visible solo (Art. 11).

Software libre (GPLv3). La IA asiste; la persona firma.