De ticket a PR con Claude Code: escribe la spec primero
De ticket a PR con Claude Code: convierte un ticket vago en una spec con criterios Given/When/Then, cada uno ligado a un test, y abre un PR que se revisa solo.

Te asignan un ticket. Tres líneas. "Permitir cancelar pedidos desde la API. Ojo con los ya enviados. Avisar al cliente."
Quieres cerrarlo rápido, así que lo pegas tal cual en Claude Code. Veinte minutos después tienes endpoint, tests y PR. Y el revisor empieza el interrogatorio. ¿Qué pasa si cancelo dos veces? ¿Quién dijo que había que reembolsar?
Nadie lo dijo. El agente rellenó los huecos del ticket con suposiciones razonables. Y razonable no es lo mismo que acordado.
El flujo de ticket a PR con Claude Code no se rompe al generar código. Se rompe antes: al traducir el ticket a algo que el agente pueda cerrar y un revisor comprobar.
En corto: para ir de ticket a PR con Claude Code, convierte primero el ticket en una spec de una página con alcance cerrado y criterios Given/When/Then. Cada criterio lleva al lado el test, el comando o la comprobación manual que lo demuestra. El agente implementa contra esa lista y el PR mapea cada criterio a su evidencia.
¿Qué es una spec de ticket?
Una spec de ticket es un documento de una página que traduce un ticket a un alcance cerrado y a criterios de aceptación verificables, cada uno ligado al test, comando o comprobación que demuestra que se cumple.
No es la spec de un proyecto entero: no tiene visión, usuarios ni arquitectura. Vive en un repo que ya existe y su único trabajo es cerrar las preguntas que el ticket deja abiertas.
Por qué el ticket tal cual no sirve para ir de ticket a PR con Claude Code
Un ticket es una conversación a medias. Asume contexto que el agente no tiene: qué significa "enviado" en tu modelo, qué códigos HTTP usa el equipo o si "avisar" es un email o un evento.
En el hilo de Hacker News "Spec-Driven Development: The Waterfall Strikes Back" (noviembre de 2025, 191 comentarios), un usuario lo resume así: el agile no mató la especificación, la dejó "split across hundreds to thousands of randomly filled Jira tickets". Esa es exactamente la materia prima con la que trabajas.
En el mismo hilo, el autor del artículo lo ve de otra forma: dice que SDD no le aporta nada porque ya usa plan mode antes de implementar. Mezcla dos cosas. Plan mode decide cómo se construye. La spec fija qué cuenta como terminado. Un plan sin criterios externos siempre está de acuerdo consigo mismo.
Cómo convertir un ticket en una spec: el ejemplo completo
Un ticket se convierte en spec en cuatro pasos: lista las preguntas que no contesta, resuelve con el código las que son técnicas, lleva las de producto a quien abrió el ticket y escribe el alcance y los criterios Given/When/Then con su verificación al lado.
Vamos con un caso. Es un ejemplo inventado, con la forma de los tickets reales: una API en TypeScript que ya existe, con tests en Vitest y Supertest, un eventBus interno y un servicio de notificaciones que escucha eventos.
El ticket "antes":
SHOP-482 — Cancelar pedidos
Permitir cancelar pedidos desde la API. Ojo con los ya enviados.
Avisar al cliente.
Antes de escribir una línea de spec, lista las preguntas que el ticket no contesta. En este caso salen seis: quién puede cancelar, en qué estados, qué responde si ya está enviado, qué pasa si cancelas dos veces, qué significa "avisar" y si hay reembolso.
Cuatro las resuelve el código: enum de estados, formato común de errores y notificaciones que ya escuchan el bus. Dos son de producto y se preguntan a quien abrió el ticket. Respuesta (inventada): solo cancela el dueño, y el reembolso va en otro ticket.
El agente puede ayudarte a encontrar las preguntas. No puede contestar las de producto.
La spec "después":
# SHOP-482 — Cancelar un pedido desde la API
## Contexto
Ticket: SHOP-482. El cliente puede cancelar un pedido propio que aún no ha salido del almacén.
## Alcance
Dentro:
- Endpoint POST /orders/:id/cancel en src/orders/
- Transición de estado pending | paid -> cancelled
- Publicar el evento order.cancelled en el eventBus existente
Fuera:
- Reembolsos (SHOP-483)
- Plantillas de email (las gestiona notifications, no se tocan)
- Panel de administración
## Decisiones tomadas
- Solo el dueño del pedido puede cancelar (confirmado con producto)
- Pedido de otro usuario: 404, no 403, para no revelar que existe
- Cancelar un pedido ya cancelado no es un error: es idempotente
## Criterios de aceptación
CA-1 Given un pedido "pending" o "paid" del usuario autenticado
When llama a POST /orders/:id/cancel
Then responde 200, status = "cancelled" y cancelledAt tiene fecha
Verificación: test "cancela un pedido pendiente o pagado"
CA-2 Given un pedido "shipped"
When llama a POST /orders/:id/cancel
Then responde 409 con code ORDER_ALREADY_SHIPPED y el estado no cambia
Verificación: test "rechaza cancelar un pedido enviado"
CA-3 Given un pedido de otro usuario
When llama a POST /orders/:id/cancel
Then responde 404 con el mismo cuerpo que un id inexistente
Verificación: test "no revela pedidos ajenos"
CA-4 Given un pedido ya "cancelled"
When llama a POST /orders/:id/cancel de nuevo
Then responde 200 con el mismo cuerpo y no publica un segundo evento
Verificación: test "cancelar es idempotente"
CA-5 Given un pedido "pending" o "paid" del usuario autenticado
When lo cancela
Then se publica exactamente un evento order.cancelled con orderId y userId
Verificación: test "publica order.cancelled una vez" (spy sobre eventBus.publish)
CA-6 Given el resto de la API
When se ejecuta la suite completa
Then ningún contrato existente cambia
Verificación: npm run typecheck && npm test
CA-7 Given staging con el servicio de notificaciones activo
When se cancela un pedido de prueba
Then el cliente recibe el email de cancelación
Verificación: manual, en staging
## Restricciones del repo
- Seguir el patrón de src/orders/routes/pay.ts (validación + servicio + error común)
- No tocar src/notifications/ ni las migraciones
## Preguntas abiertas
(vacío: si hay algo aquí, no se implementa)
"Avisar al cliente" pasó a ser un evento (CA-5) más una comprobación manual (CA-7), y "ojo con los ya enviados", un 409 con código de error. El reembolso, que nadie había mencionado, quedó fuera por escrito.
Si quieres la lista de por qué una spec falla aunque parezca completa, la tienes en por qué tu spec falla con un agente de IA. No la repito aquí.
Plantilla de spec para un ticket
La estructura del ejemplo sirve para casi cualquier ticket acotado en un repo existente:
| Sección | Qué responde | Señal de que está mal |
|---|---|---|
| Contexto | Qué problema resuelve, en una frase, con el id del ticket | Ocupa más de tres líneas |
| Alcance (dentro / fuera) | Qué archivos o módulos se tocan y qué no | El "fuera" está vacío |
| Decisiones tomadas | Las preguntas del ticket ya contestadas, y por quién | Hay decisiones de producto que tomó el agente |
| Criterios de aceptación | Given/When/Then numerados, con su verificación | Un criterio sin test, comando ni paso manual |
| Restricciones del repo | Qué patrón copiar y qué no tocar | No nombra ningún archivo real |
| Preguntas abiertas | Lo que falta por decidir | Tiene algo y aun así se implementa |
Lo que no está escrito como excluido, el agente lo considera negociable.
Esta forma de trabajar, con specs pequeñas y criterios numerados, es la que desarrollo con calma en el libro de Spec-Driven Development.
Criterios de aceptación Given/When/Then: cada uno con su prueba
Given/When/Then obliga a escribir estado de partida, acción y resultado observable. Y lo observable se puede comprobar.
La regla: si no puedes escribir al lado de un criterio cómo se verifica, el criterio no está terminado.
| Criterio | Cómo se verifica |
|---|---|
| CA-1 cancela pendiente o pagado | Test de integración con Supertest |
| CA-2 rechaza enviado | Test que comprueba 409 y estado intacto |
| CA-3 no revela ajenos | Test que compara el cuerpo con el de un id inexistente |
| CA-4 idempotente | Test que cancela dos veces y cuenta eventos |
| CA-5 publica evento | Spy sobre eventBus.publish |
| CA-6 sin regresiones | npm run typecheck && npm test |
| CA-7 email al cliente | Comprobación manual en staging |
No todo se automatiza, como CA-7. Un criterio manual declarado es honesto; uno olvidado es un bug con fecha de llegada.
El bloque de CLAUDE.md para trabajar con specs
No necesitas un CLAUDE.md nuevo. Necesitas una sección que le diga al agente cómo comportarse cuando la tarea viene con spec. El archivo completo, con su plantilla, está en CLAUDE.md para proyectos. Esta es solo la parte de SDD:
## Trabajo con specs (SDD)
- Si la tarea menciona una spec en specs/, léela entera antes de abrir otros archivos.
- Empieza en plan mode. El plan indica, para cada CA-n, qué archivo cambia y qué test lo cubre.
- No modifiques archivos fuera de la sección "Alcance". Si hace falta, para y pregunta.
- Si la spec contradice el código, no lo resuelvas tú: anótalo en "Preguntas abiertas" y para.
- Escribe primero los tests de cada CA-n y comprueba que fallan.
- No des la tarea por terminada sin que pasen: npm run typecheck && npm test
- La descripción del PR lista cada CA-n con su evidencia o con "manual: pendiente".
Lo dice la propia documentación de Claude Code: Claude trata el CLAUDE.md como contexto, no como configuración obligatoria. Lo seguirá casi siempre. Si necesitas que sea siempre, la guía de buenas prácticas recomienda un hook Stop que devuelva a Claude al trabajo hasta que pase el typecheck (con un tope de bloqueos configurable).
De la spec al PR en Claude Code: cinco pasos
Con la spec en specs/shop-482-cancelar-pedido.md, el recorrido es corto. Todo lo que sigue está en la documentación oficial de Claude Code, consultada en noviembre de 2026: flujos de trabajo comunes, modos de permiso y buenas prácticas.
1. Explora sin editar. Arranca en plan mode con claude --permission-mode plan. En este modo Claude lee archivos y ejecuta comandos para explorar, pero no edita el código hasta que apruebes un plan. Pídele que lea la spec y localice el enum de estados, pay.ts y el eventBus. Si el repo es nuevo para ti, entender un repositorio con Claude Code cubre esta fase a fondo.
2. Pide un plan que cite los criterios. Sigue en plan mode (o pulsa Shift+Tab hasta ver ⏸ plan mode on, o empieza el prompt con /plan) y pide un plan donde cada CA-n tenga archivo y test. Con Ctrl+G abres el plan en tu editor y lo corriges antes de aprobar. Si falta un criterio, no apruebes.
3. Tests primero, luego código. Pide los tests de CA-1 a CA-5 y comprueba que fallan por la razón correcta. Después, la implementación.
4. Verifica contra la spec, no contra el plan. Ejecuta los comandos de CA-6 y pide a un subagente que compare el diff con la spec y marque los criterios sin test, un patrón que propone la guía oficial.
5. Abre el PR con la tabla. Basta con pedirle a Claude que cree el PR; si lo hace con gh pr create, Claude Code enlaza la sesión al PR y luego la recuperas con claude --from-pr <número>. Si prefieres controlarlo tú, escribe la descripción en un archivo y usa gh pr create --draft --title "SHOP-482: cancelar pedidos" --body-file pr.md.
La descripción del PR no resume los cambios. Mapea:
## SHOP-482 — Cancelar pedidos
Spec: specs/shop-482-cancelar-pedido.md
| CA | Evidencia |
|----|-----------|
| CA-1 | test "cancela un pedido pendiente o pagado" (verde) |
| CA-2 | test "rechaza cancelar un pedido enviado" (verde) |
| CA-3 | test "no revela pedidos ajenos" (verde) |
| CA-4 | test "cancelar es idempotente" (verde) |
| CA-5 | test "publica order.cancelled una vez" (verde) |
| CA-6 | typecheck y suite completa en verde |
| CA-7 | manual: pendiente de QA en staging |
Fuera de alcance: reembolsos (SHOP-483).
El revisor ya no lee el diff adivinando qué hace: comprueba cada criterio y busca lo que falta. Es la idea de la revisión por contrato del código de agentes.
Ramas, commits y conflictos con git merecen su propio post. Y si quieres encadenar todo desde Jira hasta el deploy, el pipeline está en automatizar el proceso de desarrollo con IA.
¿Cuánto se tarda de ticket a PR con Claude Code?
Para un ticket acotado como este, una hora es un presupuesto razonable. No es una medición: es un reparto orientativo para saber cuándo te estás pasando en una fase.
| Fase | Presupuesto orientativo | Si te pasas, suele significar |
|---|---|---|
| Leer el ticket y sacar preguntas | 10 min | El ticket esconde una decisión de producto |
| Escribir la spec y los criterios | 15 min | El ticket es en realidad dos tickets |
| Explorar el repo en plan mode | 10 min | No hay un patrón claro que copiar |
| Revisar y aprobar el plan | 5 min | Los criterios son ambiguos |
| Tests e implementación | 10 min | El alcance creció por el camino |
| Verificar y abrir el PR | 10 min | Algún criterio no tenía forma real de comprobarse |
Dos tercios del tiempo, 40 de los 60 minutos, se van antes de que el agente edite nada. Es al revés de lo habitual: el tiempo se gasta en decidir, no en rehacer el PR.
Límites de este método
No sirve para tickets que esconden una decisión de producto grande. Si al listar preguntas descubres que nadie sabe qué tiene que pasar, la spec no lo arregla. Te obliga a verlo antes, pero el ticket vuelve a producto.
Los criterios verifican lo que escribiste, no lo que olvidaste. Si nadie pensó en la concurrencia (dos cancelaciones a la vez), el PR saldrá con todas las filas en verde y el bug dentro. La tabla da trazabilidad, no completitud.
El CLAUDE.md no obliga a nada. Si tu repo tiene carpetas que no se pueden tocar nunca, eso va en una regla de permisos o en un hook, no en una frase.
No todo ticket necesita spec. Un cambio de texto, un bump de dependencia o un prototipo de UI que vas a tirar no compensan el ritual. Los casos están en cuándo no usar Spec-Driven Development. Y si lo que tienes delante es reestructurar código existente sin cambiar comportamiento, el enfoque es otro: refactorizar código legacy con SDD y Claude Code.
Tu próximo ticket, en una hoja
No cambies todo tu flujo. Coge el próximo ticket que te asignen y, antes de abrir Claude Code, escribe sus preguntas abiertas y cinco criterios Given/When/Then con su verificación al lado. Solo eso.
El código no mejora porque el modelo sea más listo. Mejora porque por fin sabe qué significa "terminado".
Si quieres practicar este recorrido completo con un proyecto real de principio a fin, es lo que hacemos en el curso Construye con IA: de la idea al producto con Claude Code. Y si tu problema es revisar lo que genera el agente con criterios y un harness, eso lo trabajamos en el workshop SDD + Agentic Engineering.
Preguntas frecuentes
¿Quién escribe la spec del ticket, el dev o el agente?
Los dos, con papeles distintos. El agente es bueno encontrando las preguntas que el ticket no contesta y proponiendo criterios a partir del código. El dev decide, y las decisiones de producto las cierra quien abrió el ticket. Si nadie la lee antes de implementar, la spec solo documenta las suposiciones del agente.
¿La spec va en el PR o se queda en el ticket?
En el repo, dentro del PR. Así viaja con el código, el revisor la tiene al lado del diff y queda en el historial de git. En el ticket basta con un enlace al archivo. Si discrepan, manda la spec.
¿Qué hago si a mitad de implementación descubro que la spec estaba mal?
Para, corrige la spec y vuelve a plan mode. No dejes que el agente lo arregle "sobre la marcha" en el código, porque entonces la spec y el PR cuentan dos historias distintas. Si es técnico, anótalo en "Decisiones tomadas"; si es de producto, vuelve a preguntar.
¿Necesito una spec si ya uso plan mode?
Sí, porque resuelven cosas distintas. Plan mode decide cómo se construye; la spec fija qué cuenta como terminado. Un plan sin criterios externos siempre está de acuerdo consigo mismo: el agente lo escribe y luego lo cumple. Con la spec, el plan tiene que citar cada CA-n con su archivo y su test, y lo que falte se ve antes de aprobar.
Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.
¿Quieres construir con agentes en serio?
Curso «Construye con IA» con Claude Code, de la idea al producto.
Developer con más de 15 años en frontend y creador de Dominicode. Escribe sobre IA aplicada al desarrollo de software.
¿Te resultó útil este artículo?
Compártelo con tu comunidad y ayuda a otros desarrolladores.