Skill: /epic-generate-stories
Cuándo usar este skill:
Usar cuando se quiera generar automáticamente las historias de usuario a partir de las
features definidas en un epic.md. Invocar también cuando el usuario mencione
"generar historias", "historias de la épica", "stories de la épica", "generar stories",
"derivar historias de épica", "epic-generate-stories" o equivalentes.
Objetivo
Lee epic.md de un directorio de la épica en $SPECS_BASE/specs/02-epics/ y genera automáticamente un directorio STORY-[ID]-[Nombre-kebab]/ con un archivo story.md por cada feature definida en la sección ## Historias de la épica. Cada archivo generado sigue exactamente la estructura de $SPECS_BASE/specs/templates/story-template.md.
Qué hace este skill:
- Resuelve la épica a procesar por nombre de directorio (parcial o completo) o por ruta explícita
- Extrae todas las features de la sección
## Historiasdelepic.mdindicado - Genera un
story.mdpor feature, respetando el template canónico en tiempo de ejecución - Pregunta al usuario antes de sobreescribir historias existentes
Qué NO hace este skill:
- Validar la calidad FINVEST de las historias generadas → usar
/story-evaluation - Modificar el archivo de épica
- Realizar evaluación INVEST ni splitting automático
Entrada
- Nombre de directorio de la épica (parcial o completo), o ruta relativa al directorio/archivo
epic.md
Parámetros
{epic}— nombre de directorio (parcial o completo) o ruta relativa al directorio de la épica o aepic.md(obligatorio)
Precondiciones
- El directorio de la épica indicada debe existir en
$SPECS_BASE/specs/02-epics/y contenerepic.md $SPECS_BASE/specs/templates/story-template.mddebe existirskill-preflightretorna estado OK (entorno válido)
Dependencias
- Skills: [
skill-preflight] - Archivos: [
$SPECS_BASE/specs/templates/story-template.md]
Modos de ejecución
- Manual (
/epic-generate-stories {epic}): interactivo cuando hay conflictos de sobreescritura — pregunta al usuario historia por historia - Automático: invocado por orquestador — reporta resultado sin interacción adicional
Restricciones / Reglas
- El skill no valida calidad FINVEST — en flujo batch la validación INVEST se delega al paso posterior
/story-evaluationpara no bloquear la generación masiva de historias; ejecutar/story-evaluationsobre cada historia generada como siguiente paso obligatorio - El skill solo modifica el archivo de épica para backfill de IDs de historia: reemplaza líneas
- [ ] **Nombre:** descpor- [ ] STORY-NNN - **Nombre:** descen la sección## Historiasantes de generar los directorios de historia. No modifica ninguna otra sección ni ningún otro archivo. - El skill procesa todas las features de la épica (pendientes
[ ]y completadas[x]) - Si dos features tienen el mismo ID (duplicado en la épica), añadir sufijo
-bisal segundo archivo (ej.STORY-029-nombre-bis/) e informar al usuario - Las secciones opcionales de cada historia se incluyen con placeholder
[Por completar]para facilitar la edición posterior - El skill no realiza evaluación INVEST ni splitting — si una historia parece demasiado grande, sugerirlo en las notas pero no dividirla automáticamente
- El template
story-template.mdes de solo lectura — nunca escribir en él ni usarlo como ruta de salida - NO modifique ningún archivo existente en el código fuente (estamos en etapa de especificación, no de implementación)
- NO genere código; estas especificando, no implementando los artefactos técnicos
- Encoding: All generated
.mdfiles MUST be saved as UTF-8 without BOM. Do not use Latin-1, CP-1252, or any other encoding. If you see characters likeóor📖, that indicates an encoding error — fix it.
Flujo de ejecución
Paso 0 — Verificar entorno (skill-preflight)
Invocar skill-preflight. Si retorna ✗ Entorno inválido, detener la ejecución. Usar $SPECS_BASE en todas las rutas siguientes.
Paso 1 — Resolver el input
El skill acepta dos formatos de input:
Formato A — Nombre de directorio (parcial o completo)
Señal: el input no contiene separadores de directorio (/ o \) o es un nombre de directorio sin epic.md.
Acción:
- Buscar en
$SPECS_BASE/specs/02-epics/subdirectorios cuyo nombre contenga el término (sin distinguir mayúsculas). - Si hay exactamente 1 coincidencia → usar ese directorio y leer
epic.mddentro. Continuar al Paso 2. - Si hay más de 1 coincidencia → mostrar la lista y pedir al usuario que especifique cuál usar antes de continuar.
- Si no hay ninguna coincidencia → mostrar el mensaje de error y terminar (ver Manejo de errores).
Formato B — Ruta relativa completa al directorio o al archivo epic.md
Señal: el input contiene separadores de directorio (/ o \) o empieza con $SPECS_BASE/.
Acción: resolver la ruta al archivo epic.md del directorio indicado. Si el archivo no existe, mostrar el mensaje de error y terminar (ver Manejo de errores).
En ambos casos, si epic.md no se encuentra: terminar inmediatamente sin generar ningún archivo de historia.
Paso 2 — Leer la épica y extraer features
Leer el archivo de épica resuelta en el Paso 1.
Localizar la sección ## Historias. Solo analizar el contenido dentro de esa sección.
Dentro de esa sección, extraer cada línea de feature. Reconocer dos formatos:
Formato A — Con ID asignado (épicas existentes o migrados):
- [ ] STORY-NNN - **Nombre:** descripción- [ ] STORY-NNN — Nombre: descripción- [ ] **STORY-NNN — Nombre:** descripción- Variantes con guion largo
—, doble guión--o dos puntos como separador
Formato B — Sin ID (épicas creadas con el flujo lazy):
- [ ] **Nombre:** descripción- [x] **Nombre:** descripción
Para cada feature, capturar:
- ID: el identificador
STORY-NNNsi está presente;nullsi no hay ID (Formato B). - Nombre: el nombre de la feature.
- Descripción: el texto después del separador, si existe.
- Estado:
[ ](pendiente) o[x](completada) — se procesan todas independientemente del estado.
Si la sección ## Historias no existe o no contiene ninguna entrada reconocible: terminar sin generar ningún archivo (ver Manejo de errores).
Paso 2b — Asignar IDs de historia a features sin ID
Si ninguna feature tiene ID null → saltar este paso (todos tienen ID; comportamiento actual sin cambios).
Si alguna feature tiene ID null:
Calcular el máximo ID en uso desde dos fuentes:
- Fuente A — Filesystem: usar Glob
$SPECS_BASE/specs/03-stories/STORY-*/story.md. De cada ruta extraer el númeroNNNdel segmentoSTORY-NNN-*. Tomar el mayor. - Fuente B — Épicas existentes: leer todos los archivos
$SPECS_BASE/specs/02-epics/*/epic.mdy extraer cualquierSTORY-NNNpresente en sus secciones## Historias. Tomar el mayor entre todos los encontrados. MAX_ID = máximo entre Fuente A y Fuente B(o0si ambas están vacías).
- Fuente A — Filesystem: usar Glob
Asignar IDs secuencialmente a las features con ID
null, en orden de aparición:- Primera sin ID →
STORY-(MAX_ID+1)con cero-padding a 3 dígitos (ej.STORY-091) - Segunda sin ID →
STORY-(MAX_ID+2), etc.
- Primera sin ID →
Backfill en
epic.md: actualizar la sección## Historiasde la épica activa, reemplazando cada línea sin ID por la versión con ID asignado:- [ ] **Nombre:** desc→- [ ] STORY-NNN - **Nombre:** desc
Escribir el archivo actualizado antes de continuar al Paso 3.
Emitir resumen de asignación:
ℹ️ IDs de historia asignados y registrados en epic.md: - story-bulk-plan → STORY-091 - story-bulk-implement → STORY-092 ...
Paso 3 — Preparar directorio de destino
Verificar si el directorio $SPECS_BASE/specs/03-stories/ existe.
Si no existe, crearlo antes de continuar.
Paso 4 — Generar archivos de historia
Para cada feature extraída en el Paso 2, ejecutar los siguientes sub-pasos:
4a. Construir el nombre del directorio
Convertir el nombre de la feature a kebab-case siguiendo estas reglas:
- Convertir a minúsculas
- Normalizar caracteres acentuados: á→a, é→e, í→i, ó→o, ú→u, ü→u, ñ→n
- Reemplazar espacios y cualquier carácter que no sea letra o número por un guion
- - Eliminar guiones consecutivos (
--→-) - Eliminar guiones al inicio o al final
Nombre de directorio resultante: STORY-[NNN]-[nombre-kebab]
Ruta del archivo de salida: $SPECS_BASE/specs/03-stories/STORY-[NNN]-[nombre-kebab]/story.md
Ejemplo: STORY-029 — Generar stories → directorio STORY-029-generar-stories/ con archivo story.md
4b. Verificar existencia previa
IMPORTANTE: La herramienta Glob solo encuentra archivos, nunca directorios. Para verificar si ya existe la historia, usar el patrón de archivo anidado:
$SPECS_BASE/specs/03-stories/STORY-[NNN]-[nombre-kebab]/story.md. Si Glob retorna ese archivo, el directorio existe. Si retorna vacío, no existe. Nunca usar el patrón de directorio desnudo (STORY-[NNN]-[nombre-kebab]/) — retornará vacío aunque el directorio exista, causando sobreescritura silenciosa sin confirmación.
Si ya existe $SPECS_BASE/specs/03-stories/STORY-[NNN]-[nombre-kebab]/story.md, informar al usuario:
El directorio $SPECS_BASE/specs/03-stories/STORY-[NNN]-[nombre-kebab]/ ya existe.
¿Deseas sobreescribir story.md? (s/n)
Esperar confirmación antes de continuar. Si el usuario responde n o no, saltar esta feature y continuar con la siguiente.
4c. Verificar que el template existe
El archivo de plantilla es la única fuente de información estructural para generar el output. Define qué secciones existen, en qué orden y con qué propósito. Nunca hardcodear los nombres o la estructura de las secciones — siempre derivarlos del template en tiempo de ejecución. El template es de solo lectura.
Leer el archivo $SPECS_BASE/specs/templates/story-template.md.
- Si el archivo central no existe: usar el fallback
$CLI_ROOT/skills/story-creation/assets/story-template.mdy emitir:⚠️ Usando template del skill story-creation. Ejecuta
sddf-initpara centralizarlo en$SPECS_BASE/specs/templates/. - Si tampoco existe el fallback: detener la ejecución (ver Manejo de errores).
- Si alguno de los dos existe: continuar.
4d. Inferir el contenido de la historia
Usando el nombre y la descripción de la feature, inferir:
- Rol (
Como): el desarrollador, PM o practitioner que ejecuta o se beneficia de esta feature dentro del sistema SDDF. Ser específico — evitar "usuario" genérico. - Acción (
Quiero): la acción concreta que habilita la feature, orientada al usuario y no a la implementación técnica. - Beneficio (
Para): el valor real que aporta al flujo de trabajo, medible o concreto.
Generar al menos:
- 1 escenario Gherkin principal (happy path): con
Dado/Cuando/Entoncesespecíficos y verificables. - 1 escenario alternativo/error: con condición de fallo y comportamiento esperado.
Si la feature tiene descripción detallada, usarla para enriquecer los escenarios. Si la descripción es mínima, inferir escenarios razonables desde el nombre y el contexto del sistema SDDF (framework de especificación de software con skills, agents y templates Markdown).
Las secciones opcionales (⚙️ Criterios no funcionales, 📎 Notas) se incluyen con placeholder [Por completar] si no hay datos suficientes.
4e. Escribir el archivo de historia
Crear el directorio $SPECS_BASE/specs/03-stories/STORY-[NNN]-[nombre-kebab]/ si no existe, luego crear el archivo story.md dentro de ese directorio con la estructura del template $SPECS_BASE/specs/templates/story-template.md. Completar dinámicamente la estructura de la plantilla en tiempo de ejecución para asegurar flexibilidad ante cambios futuros.
Al completar el frontmatter del archivo generado, usar:
status: SPECIFY— estado inicial de toda historia generada desde una épica planificada (pendiente de refinamiento)kind: feat— tipo de historia por defecto; cambiar afix,choreohotfixsegún la naturaleza del trabajo (determina el prefijo de rama)parent: <EPIC-NN>-<slug>— el nombre del directorio de la épica de origen (ej.EPIC-01-features-spec-builder), no el ID desnudo
Si no se puede leer el template, generar el archivo con la siguiente estructura de fallback:
---
type: story
id: <STORY-NNN>
kind: feat
slug: <nombre-del-directorio-de-historia>
title: "<Nombre de la feature>"
status: SPECIFY
substatus: IN-PROGRESS
parent: <EPIC-NN>-<slug>
created: <YYYY-MM-DD>
updated: <YYYY-MM-DD>
---
# Historia de Usuario
## 📖 Historia: [Nombre de la feature]
**Como** [rol específico inferido]
**Quiero** [acción concreta orientada al usuario]
**Para** [beneficio real y medible]
## ✅ Criterios de aceptación
### Escenario principal – [título descriptivo del happy path]
```gherkin
Dado [contexto inicial específico]
Y [condición adicional si aplica]
Cuando [acción del usuario]
Entonces [resultado esperado concreto]
Y [resultado adicional si aplica]
Escenario alternativo / error – [título]
Dado [contexto de fallo]
Cuando [acción inválida o condición de error]
Entonces [mensaje de error o comportamiento alternativo]
Pero [excepción si aplica]
⚙️ Criterios no funcionales
[Por completar]
📎 Notas / contexto adicional
Generado automáticamente desde la épica: [nombre del archivo de épica] Feature origen: [ID] — [Nombre de la feature]
---
### Paso 5 — Resumen
Al terminar de procesar todas las features, mostrar un resumen en pantalla:
Historias generadas
Se generaron [N] directorios de historia en $SPECS_BASE/specs/03-stories/:
- $SPECS_BASE/specs/03-stories/STORY-NNN-nombre/story.md
- $SPECS_BASE/specs/03-stories/STORY-NNN-nombre/story.md ...
Siguiente paso: Ejecuta /story-evaluation para verificar la calidad de cada historia generada, o /story-specify para especificarlas de forma interactiva.
Si alguna feature fue saltada (usuario eligió no sobreescribir), listarla como:
- $SPECS_BASE/specs/03-stories/STORY-NNN-nombre/ — saltada (ya existía)
Si alguna feature no pudo procesarse por formato inesperado, listarla como:
- STORY-NNN — [Nombre] — no procesada (formato no reconocido)
---
### Manejo de errores
| Condición | Mensaje | Acción |
|---|---|---|
| Entorno inválido (preflight) | `✗ Entorno inválido` | Detener inmediatamente |
| Épica no encontrado (Formato A, sin coincidencias) | `No se encontró el directorio de la épica: <término>. Asegúrate de que el directorio existe en $SPECS_BASE/specs/02-epics/ y vuelve a intentarlo.` | Detener sin generar archivos |
| Épica no encontrado (Formato B, ruta inválida) | `No se encontró epic.md en: <ruta>. Asegúrate de que la ruta es correcta y vuelve a intentarlo.` | Detener sin generar archivos |
| Sección `## Historias` vacía o ausente | `No se encontraron historias en el archivo de épica indicada.` | Mostrar orientación y detener |
| Template `story-template.md` no encontrado | `❌ No se encontró el template requerido en $SPECS_BASE/specs/templates/story-template.md.` | Detener la ejecución |
---
## Salida
- Directorios `$SPECS_BASE/specs/03-stories/STORY-[NNN]-[nombre-kebab]/story.md` creados — uno por feature de la épica
- Resumen con: historias generadas, historias saltadas (por conflicto), features con formato no reconocido