Skill: Validar trazabilidad de un trabajo
Genera un reporte de trazabilidad que cruza los criterios de aceptación de un trabajo contra los casos de prueba y los artefactos de prueba automatizada (unit, integración, e2e) presentes en el repositorio, y emite un veredicto sobre si el trabajo queda cubierto.
El trazado primario es para historias de usuario (US-XXX) con sus criterios de aceptación. Sirve igual para cualquier otro artefacto que tenga criterios con identificador codificado — el formato es indiferente (AC-001, 1.1, R-3…) y se usa verbatim, sin normalizar (ver Tipos de trabajo y criterios). Es el mismo contrato que produce test-define.
Qué hace: lee, mapea, obtiene los resultados de pruebas delegando en
quality-checky reporta. Es una actividad de verificación, no de desarrollo.No ejecuta pruebas por sí mismo. La ejecución de la batería de pruebas se delega en
quality-check: si existe una corrida previa fresca dequality-check(sin cambios en el código desde entonces), se reutilizan sus resultados; si no, se invocaquality-checken modotests-onlypara producirlos. Ver Resultados de pruebas: delegación en quality-check.Reporte idempotente. Si no han cambiado ni el código, ni las pruebas, ni los criterios y casos de prueba del artefacto desde la última vez que se generó el
coverage.md, no se genera un documento nuevo: se devuelven los mismos resultados del reporte existente. Ver Reutilización del reporte (idempotencia).Qué NO hace: no escribe ni modifica código de aplicación, no escribe nuevos tests (eso es de
quality-specialistvíawork-implement), no edita la especificación de producto (README de la US,TK-XXX,WI-XXX,FT-XXX,validation.md, ADRs), ni corre la suite de pruebas directamente. Lo único que produce es el reporte de trazabilidad. Lo que no se puede determinar de las fuentes va a Observaciones o se pregunta al usuario — nunca se inventa cobertura ni resultados.
Alcance del informe
El informe de este skill cubre un artefacto concreto y sus criterios de aceptación. La pregunta es «¿cada criterio de este trabajo está probado?», así que el universo lo definen los criterios del artefacto, no los archivos que cambiaron ni el repositorio completo.
Consecuencias prácticas:
- Se validan todos los criterios del artefacto, aunque en esta rama no se haya tocado el código de alguno de ellos: un criterio cuya prueba nunca se escribió sigue siendo un hueco.
- No se reportan pruebas ni código ajenos al artefacto. Tests de otros trabajos, cobertura global o fallos en módulos que no mapean a ningún criterio de este artefacto no entran en la matriz — a lo sumo van a «Observaciones y pendientes».
- Si la rama abarca varios trabajos, se valida uno por corrida, con su propio
coverage.mdjunto a su artefacto. Los resultados de pruebas sí son compartidos (vienen de la corrida dequality-check), pero el mapeo es por artefacto. - La clave de frescura tiene una mitad de rama y otra de artefacto. El
FINGERPRINTcubre el código y los tests de todo el árbol, así que un cambio en otro trabajo de la misma rama invalida también estecoverage.md; elSPEC_FINGERPRINTcubre solo la carpeta de este artefacto. Es conservador a propósito: prefiere revalidar de más antes que devolver un reporte que ya no corresponde.
Subagente
Si el proyecto define el subagente quality-specialist, ejecutar este skill bajo ese subagente (es el mismo agente que escribe los tests en el cierre de work-implement, por lo que es el contexto natural para validarlos). Si no existe en el proyecto, ejecutar el flujo normalmente.
Tipos de trabajo y criterios
El tipo se determina por el identificador que indique el usuario o por la ruta de trabajo. Cada tipo fija de dónde se leen los criterios de aceptación y con qué códigos se traza.
| Tipo | Identificador | Dónde viven los criterios | Códigos a trazar |
|---|---|---|---|
| Historia de usuario | US-XXX |
Sección Criterios de aceptación del README.md de la US (lista plana, habitualmente AC-XXX) |
El identificador de cada criterio, en el orden en que aparecen |
| Tarea de mantenimiento | WI-XXX |
Sección ## Criterios de aceptación del README.md del WI (WI-XXX-[kebab]/README.md) |
El identificador de cada criterio, en el orden en que aparecen |
| Feature (funcionalidad ya implementada) | FT-XXX |
Sección ## Criterios de aceptación del README.md del feature (docs/specs/features/FT-XXX-[slug]/README.md) |
El identificador de cada criterio, en el orden en que aparecen |
| Cualquier otro artefacto de especificación | Ruta o nombre que indique el usuario | La sección de criterios del documento (puede llamarse «Criterios de aceptación», «Requisitos», «Acceptance Criteria»…) | El identificador de cada criterio, tal como aparece |
El requisito no es el formato, es que exista identificador. En todo el flujo, «criterio» se refiere al identificador tal como está escrito en el artefacto —
AC-012,1.3,R-3,CA-07son todos válidos. Nunca normalizar ni renombrar: el vínculo de trazabilidad debe ser buscable literalmente tanto en el artefacto como en los TCs que produjotest-define. Si el trabajo no tiene criterios, o los tiene sin identificador, no hay nada que trazar → bloquear (ver «Cuándo bloquear»).
FT — validar cobertura de funcionalidad ya implementada. Un
FT-XXXes el registro de una funcionalidad ya implementada (inferida de código legacy por el análisis dework-research, o simplemente el registro de funcionalidad existente), no trabajo por construir. Validarlo responde: ¿esa funcionalidad ya existente está cubierta por pruebas? Un criterioUNCOVEREDsignifica que ese comportamiento carece de pruebas (un hueco a cerrar escribiendo tests), no que falte código funcional. Sus casos de prueba documentados viven endocs/specs/features/FT-XXX-[slug]/test-cases/, igual que en una US o un WI.
Modificadores de invocación
Las claves son en inglés (estándar); el usuario puede nombrarlas en español y se mapean. Sin modificador, se asume el comportamiento por defecto.
| Modifier | Efecto exacto |
|---|---|
revalidate |
Forzar la regeneración del coverage.md aunque el existente esté fresco: ignorar la comprobación del Paso 0 y ejecutar el flujo completo. Sinónimos aceptados: «revalidar», «forzar», «vuelve a validar». Necesario cuando los criterios viven fuera del repo (un ticket, un documento externo) y han cambiado allí — eso el SPEC_FINGERPRINT no puede verlo. Mismo modificador y mismo significado que en code-review. |
Cómo preguntar al usuario
Mecanismo, ritmo y fallback compartidos: ${PLUGIN_ROOT}/reference/asking.md.
Cada vez que este skill o sus referencias digan preguntar, pedir, confirmar, validar o sugerir algo al usuario, asume ese mecanismo; no se repite allí.
Rutas de las referencias compartidas
${PLUGIN_ROOT} es la raíz del plugin instalado (la carpeta que contiene skills/, agents/ y reference/), y toda referencia compartida de este skill se escribe como ${PLUGIN_ROOT}/reference/<archivo>.md. Resolverla así, en este orden: (1) en Claude Code, ${PLUGIN_ROOT} es ${CLAUDE_PLUGIN_ROOT} — comprobar con echo "$CLAUDE_PLUGIN_ROOT" y usar ese valor; (2) en cualquier otro cliente, o si la variable está vacía, la carpeta desde la que se cargó este archivo, dos niveles arriba. El destino de cada enlace markdown (../../reference/…) existe solo para navegar el repositorio en GitHub o en un editor: no resolverlo desde el directorio de trabajo. Nunca buscar reference/ en el proyecto: un <proyecto>/reference/language.md que no existe no es un archivo que falte, es una ruta mal resuelta — corregir la raíz y volver a leer, sin preguntar al usuario ni saltarse la lectura.
Resolución de idioma
Antes de ejecutar este skill, DEBES leer ${PLUGIN_ROOT}/reference/language.md.
Las reglas de language.md son obligatorias y tienen prioridad para determinar el idioma de todos los artefactos y mensajes generados por este skill.
No continúes hasta haber leído y aplicado language.md.
Excepción deliberada: la salida y los mensajes de error de las herramientas de prueba no se traducen; se citan literales.
Límite de intentos y escalamiento
Antes de ejecutar este skill, DEBES leer ${PLUGIN_ROOT}/reference/escalation.md.
Las reglas de escalation.md son obligatorias y determinan, vía escalation.maxAttempts y escalation.onLimit, cuántos intentos consecutivos se hacen sobre el mismo problema que no se resuelve —una corrida de pruebas que no se logra completar o un criterio que no se logra evidenciar— y qué se hace al agotarlos: detener el trabajo sobre ese problema, presentar el parte de bloqueo y preguntar al usuario cómo seguir (ask), o marcarlo como BLOCKED en el informe y continuar con el alcance que no dependa de él (report).
El contador es por problema, no global, y el límite es un techo, no una cuota: si no hay una hipótesis nueva que justifique el siguiente intento, se escala ya. Nunca se «resuelve» un bloqueo desactivando o saltando una prueba, relajando una aserción ni bajando un umbral.
No continúes hasta haber leído y aplicado escalation.md.
Información requerida antes de generar el reporte
No inventar nada. Si un dato no es explícito, obtenerlo del repo o preguntar al usuario.
| Dato | Cómo obtenerlo | Si no está disponible |
|---|---|---|
| Trabajo a validar | Indicado por el usuario o inferido de la ruta de trabajo; determinar el tipo (US-XXX / WI-XXX / FT-XXX / otro artefacto) |
Preguntar qué trabajo validar; sin él no se puede generar el reporte |
| Criterios de aceptación | Según el tipo (ver Tipos de trabajo y criterios) | Si el trabajo no tiene criterios de aceptación: bloquear y reportar — sin criterios no hay nada que trazar |
| Casos de prueba | Fuente primaria: la carpeta test-cases/ junto al artefacto y su índice test-cases/README.md, más la línea Casos de prueba: que test-define deja bajo cada criterio. Fallback: inferir desde los tests del repo |
Si no hay casos documentados, derivar la cobertura desde los artefactos de prueba del repo |
| Artefactos de prueba | Buscar en el repo archivos de test unit / integración / e2e relacionados con el trabajo (ver el Paso 2 en references/flow.md) |
Si no se encuentran, marcar criterios sin artefacto como UNCOVERED y dejar Observación |
| Resultados de pruebas | Delegados en quality-check: caché fresca test-run.json o invocación tests-only (ver Resultados de pruebas: delegación en quality-check) |
Si quality-check no puede ejecutarlas (sin stack, entorno sin correr, no disponible en la sesión) o el usuario declina la delegación: las filas con artefacto van con Ejecución = — y Resultado = NOT_RUN`, y el motivo a «Observaciones y pendientes» |
| Alcance | Todo el trabajo por defecto; el usuario puede acotar a ciertos criterios | Si es ambiguo, preguntar |
Leer siempre el documento de criterios completo (el
README.mddel trabajo, o el archivo del artefacto externo) antes de generar el reporte. No asumir criterios que no estén escritos.
Flujo
Resumen de los pasos. El detalle íntegro de cada paso está en references/flow.md (leerlo antes de ejecutar el flujo).
- Comprobar frescura del reporte — si ya existe
coverage.mdcon un fingerprint guardado y no hubo cambios en los archivos desde entonces, devolver el reporte existente sin regenerarlo (ver Reutilización del reporte (idempotencia)). Solo si hay cambios (o el usuario pide revalidar) continuar con los pasos siguientes. - Localizar y leer el trabajo — resolver tipo y ubicación; extraer todos los criterios con su identificador verbatim. Sin criterios (o sin identificador) → bloquear (ver «Cuándo bloquear»).
- Inventariar casos y artefactos — leer la carpeta
test-cases/del artefacto y su índice como fuente primaria, y clasificar los tests del repo por tipo, con ruta y criterio. - Mapear cobertura fila a fila — expandir cada criterio en sus filas (criterio × TC × tipo declarado), rellenar
Evidenciay derivar el estado del criterio (ver «Estados de cobertura») con sus observaciones. No forzar mapeos inciertos. - Obtener resultados de pruebas (delegando en
quality-check) — reutilizar la cachétest-run.jsonsi está fresca, o invocarquality-checken modotests-only; mapear por suite a las filas y rellenarEjecución(quality-check/Manual/—) yResultado(PASS/FAIL/NOT_RUN/UNCOVERED/N/A). No correr pruebas directamente. Nunca fabricar resultados (ver Resultados de pruebas: delegación en quality-check yreferences/flow.md). - Redactar el reporte desde
assets/coverage-template.md(leerla antes de redactar): cabecera, Resumen (prosa con la procedencia + tabla de indicadores), cobertura por criterio (una fila por criterio, con su Estado), matriz de trazabilidad (una fila por criterio × TC × tipo declarado, con Evidencia, Ejecución y Resultado) y, si los hay, los caveats globales en «Observaciones y pendientes». Ver Vistas del reporte. - Emitir el veredicto (ver «Veredicto») respondiendo si todos los criterios quedan cubiertos.
- Entregar y guardar el reporte en la ubicación del tipo (ver «Ubicación de archivos»), grabando el fingerprint del estado actual para la próxima comprobación de frescura; no modificar otros artefactos.
Vocabulario de veredictos y estados
Antes de redactar cualquier informe, DEBES leer ${PLUGIN_ROOT}/reference/verdicts.md.
Las reglas de verdicts.md son obligatorias: el valor canónico y el símbolo son estables, y la etiqueta que lee la persona se redacta siempre en el idioma resuelto por language.md. Ninguna etiqueta de este skill se fija en un idioma concreto.
No continúes hasta haber leído y aplicado verdicts.md.
Estados de cobertura
Los valores de la columna Estado son canónicos: en el reporte se escribe su símbolo seguido de la etiqueta en el idioma resuelto.
| Estado (canónico) | Símbolo | Cuándo aplicarlo |
|---|---|---|
COVERED |
✅ |
El criterio tiene al menos un caso de prueba y el artefacto que le corresponde, y lo valida de forma completa. Si se ejecutó automáticamente, pasó. Un criterio cuyos TCs son Manual por diseño también es COVERED si esos TCs lo validan por entero — ver la nota de abajo. |
PARTIAL |
⚠️ |
El criterio está cubierto solo en parte: hay prueba pero no abarca todo el criterio, el artefacto existe pero no se pudo ejecutar, el resultado fue parcial, existe solo validación manual no declarada como tal (TC automatizable aún sin automatizar), o la suite que agrupa su test dio FAIL pero no se pudo aislar si el test específico del criterio fue el que falló (ver «Mapeo a la matriz» más abajo). Detallar el límite en Observaciones. |
UNCOVERED |
❌ |
No existe caso de prueba ni artefacto que valide el criterio, o la prueba asociada falló y se pudo aislar que fue la suya. |
Cobertura ≠ ejecución. Que exista prueba que valida el criterio es una cosa; que haya corrido y con qué resultado, otra. Un criterio cuya prueba falló —y se pudo aislar que fue la suya— se reporta
UNCOVEREDcon el fallo en Observaciones; si la suite falló pero no se puede aislar el test, esPARTIAL(ver «Mapeo a la matriz»).El
statusdel TC filtra la cobertura. La fuente primaria es la columnaEstadodel índicetest-cases/README.md; si el índice no la trae, abrir elTC-XXX-*.mdy leer su campoEstado, con la marca oculta<!-- tc:status=Draft|Ready|Obsolete · testType=… -->como desempate. Los valores (Draft/Ready/Obsolete) son canónicos y no se traducen; lo que va en el idioma resuelto es la etiqueta del campo. Un TCObsoleteno cuenta como cobertura (dejar Observación); unoDraftcuenta como coberturaPARTIAL(aún no está listo para respaldar el criterio); solo losReadycuentan como cobertura plena. Si el TC no trae el campo, tratarlo comoReady. Esto es distinto delEstado:del artefacto, que este skill no exige (ver «Cuándo bloquear»).Manual por diseño vs. pendiente de automatizar.
test-definedistingue los dos casos en el campo Tipo de prueba del TC:Manualsignifica no se automatiza por decisión de diseño; cualquier otro valor (Unit,Integration,API Test,Visual Test,E2E) significa debería existir artefacto automatizado. Respetar esa declaración: un criterio con TCsManualque lo cubren por completo esCOVERED(sus filas van conEjecución = ManualyResultado = N/A), no una deuda; un criterio con TCs automatizables sin artefacto esPARTIALoUNCOVERED. No penalizar una decisión de diseño ni disimular una automatización pendiente.Un TC con varios tipos declarados se cubre por tipo. Si un TC declara
Unit, E2Ey solo existe el test unitario, el criterio no esCOVERED: la filaE2Ede la matriz queda enUNCOVEREDy el criterio baja aPARTIAL, con el tipo faltante nombrado en Observaciones. La matriz muestra esa asimetría fila a fila (ver Vistas del reporte).
Vistas del reporte: cobertura por criterio y matriz
El reporte tiene dos tablas complementarias, no una. Separarlas evita el problema de meter en una sola fila información de granularidades distintas (un criterio puede tener varios TCs, y un TC varios tipos de prueba).
El Resumen las precede con una tabla de indicadores de cuatro columnas y una sola fila de cifras
—criterios de aceptación, cubiertos, parciales, no cubiertos— cuyos tres últimos valores deben sumar el total. No lleva indicadores de
pruebas (fallidas, no ejecutadas): esa granularidad ya está en la matriz y mezclar los dos ejes confunde.
La procedencia de los resultados y el result por suite van en la línea «Pruebas» del Resumen;
test-run.json no trae un agregado global, así que no se inventa uno.
1. Cobertura por criterio — la vista de veredicto. Una fila por criterio:
| Criterio | Descripción | Estado | Observaciones |
|---|---|---|---|
| AC-2.1 | El usuario recibe confirmación por correo | PARTIAL |
E2E declarado en TC-001 sin automatizar |
| AC-2.2 | La plantilla del correo respeta la identidad visual | COVERED |
Validación manual por diseño (TC-004) |
2. Matriz de trazabilidad — la vista auditable. Una fila por cada combinación criterio × TC × tipo de
prueba declarado: si un TC declara Unit, E2E, produce dos filas.
| Criterio | TC | Tipo | Evidencia | Ejecución | Resultado |
|---|---|---|---|---|---|
| AC-2.1 | TC-001 | Unit | tests/unit/notify.test.ts |
quality-check | Paso |
| AC-2.1 | TC-001 | E2E | — | — | UNCOVERED |
| AC-2.2 | TC-004 | Manual | test-cases/TC-004-revision-visual.md |
Manual | N/A |
Semántica de las columnas:
| Columna | Qué contiene | Valores |
|---|---|---|
| Criterio | Identificador verbatim del artefacto, repetido en cada fila suya | AC-012, 1.3, R-3… |
| TC | Caso de prueba que produjo test-define |
TC-XXX · — si el criterio no tiene TC documentado (fila derivada de un test hallado, o hueco total) |
| Tipo | El Tipo de prueba declarado en el TC; en filas derivadas, el tipo del artefacto hallado |
Manual · Unit · Integration · API Test · Visual Test · E2E · — (sin TC y sin artefacto) |
| Evidencia | La prueba concreta que respalda la fila | Ruta del artefacto automatizado · ruta del TC para filas Manual · — si esa intención no está materializada |
| Ejecución | Quién produjo el resultado | quality-check · Manual · — (no se ejecutó) |
| Resultado | Qué dio esa prueba. Valores canónicos; en el reporte va la etiqueta en el idioma resuelto | PASS · FAIL · NOT_RUN (hay artefacto, no corrió) · UNCOVERED (no hay evidencia para ese tipo) · N/A (manual por diseño) |
Por qué se eliminó la columna
Automática. UnSíno decía nada útil: no revelaba qué tipo de prueba respaldaba el criterio, dónde vivía, ni quién produjo el resultado.Tipo+Evidencia+Ejecucióndan esa información sin ambigüedad, y la granularidad por tipo hace visible el hueco (la filaE2Esin evidencia) en lugar de esconderlo tras unSíque solo hablaba del test unitario.
**
Evidencia = —implicaEjecución = —yResultado =UNCOVERED.** No hay excepción: si no hay artefacto, no hubo ejecución ni puede haber resultado. Al revés no: puede haber evidencia conEjecución = —yResultado =NOT_RUN(el artefacto existe peroquality-checkno pudo correrlo; la razón va a «Observaciones y pendientes»).
Criterio sin TC documentado pero con test hallado. Si el mapeo se infirió desde el repo (fallback del Paso 2), la fila se escribe igual con
TC = —y el tipo del artefacto hallado, y se anota en Observaciones que el mapeo es inferido, no declarado. Solo el criterio sin TC y sin artefacto produce la fila vacía— | — | — | — |UNCOVERED`.
Derivación del Estado del criterio a partir de sus filas. Evaluar en orden y detenerse en la primera que aplique:
| # | Filas del criterio | Estado |
|---|---|---|
| 1 | Ninguna fila aporta cobertura — todas en UNCOVERED, o el criterio no tiene TC ni artefacto, o sus únicos TCs son Obsolete — o alguna fila dio FAIL aislado a su test |
UNCOVERED |
| 2 | Alguna fila en UNCOVERED/NOT_RUN, algún TC en Draft u Obsolete conviviendo con cobertura válida, o un FAIL de suite no aislable |
PARTIAL |
| 3 | Todas con Resultado ∈ {PASS, N/A} y todos los TCs en Ready (o sin campo Estado) |
COVERED |
Dos destinos de observación, no intercambiables. La prueba para elegir: ¿se puede atribuir a un criterio concreto? Si sí, va en la columna
Observacionesde la tabla 1; si no, en la sección «Observaciones y pendientes». La matriz no lleva columna de observaciones.
Destino Qué recibe Columna Observaciones(tabla 1)Tipo declarado sin automatizar · TC en Draft/Obsolete·FAILno aislable · suite efectiva distinta del tipo declarado · mapeo inferido en vez de declarado · cobertura apoyada en TCsManual(aunque sea por diseño: es el caveat que justifica elAPPROVED_WITH_NOTES) · límite de la coberturaSección «Observaciones y pendientes» Suite coverageenFAIL·workingTreeClean: false(árbol sucio) · clases de prueba ausentes en el repo · ejecución no delegable y su motivo · tests que no se pudieron vincular con certeza a ningún criterio · tests o fallos ajenos al artefactoLa sección global se omite entera si no hay ninguno. Cuando el skill diga «dejar Observación» sin más, aplicar esta prueba.
La derivación no es cerrada sobre la matriz: dos datos viven fuera de ella. (a) el
Estadodel TC (Draft/Obsolete/Ready, que viene del propio TC) y (b) si unFAILpudo aislarse al test del criterio. Ambos se anotan siempre en la columna Observaciones de la tabla 1 — sin esa anotación, dos criterios con filas idénticas quedarían con estados distintos sin explicación. UnFAILno aislable se escribe igual (Resultado = FAIL) en la matriz; lo que lo distingue es la Observación «suiteXenFAIL, no se pudo aislar el test» y elEstado =PARTIAL` resultante.TCs
Obsolete. Sus filas se conservan en la matriz por trazabilidad con su valor real, pero no cuentan como cobertura al derivar el estado: si son los únicos TCs del criterio, este cae en la fila 1 aunque sus filas diganPASS; si conviven con TCsReadyque sí lo cubren, el criterio es como máximoPARTIAL(fila 2). En ambos casos, Observación explicando que el TC está obsoleto.
Reutilización del reporte (idempotencia)
Mismo principio de caché que quality-check y code-review
—las tres puertas del cierre lo aplican, cada una sobre su propio artefacto—: si no hubo cambios en los
archivos desde la última generación, no se produce un reporte nuevo — se devuelven los mismos resultados del
coverage.md existente. Esto evita rehacer el mapeo y volver a delegar la ejecución de pruebas cuando nada
cambió.
Contexto de ejecución. Como
quality-check, este skill es una compuerta de cierre (al integrar o antes del PR), no corre por tarea ni durante la implementación. La frescura se evalúa sobre la rama consolidada del cierre. En el pipeline típico de cierrepr-createcorrequality-checkprimero (producetest-run.jsonfresco) y luegotrace-validate, que reutiliza esa corrida — sin doble ejecución de pruebas; y si el código tampoco cambió desde el últimocoverage.md, este Paso 0 lo devuelve sin regenerarlo.
Clave de frescura — dos hashes, porque el reporte depende de dos cosas distintas.
| Clave | Qué cubre | Cómo se calcula |
|---|---|---|
FINGERPRINT |
El código y los tests. El fingerprint canónico de la tubería, idéntico al de quality-check y code-review: excluye toda carpeta oculta, cualquier docs/, toda la documentación en texto (*.md, *.rst, *.adoc, LICENSE*, CHANGELOG*…) y el .gitignore, para que ni escribir un artefacto generado ni editar documentación invalide la caché: solo se mueve cuando cambia el código. Receta exacta en quality-check. |
Sobre todo el árbol, menos las exclusiones |
SPEC_FINGERPRINT |
Los criterios y los casos de prueba del artefacto que se valida: su README.md y su carpeta test-cases/. Viven bajo docs/specs/, que el FINGERPRINT excluye — sin esta segunda clave, reescribir un criterio no invalidaría nada. |
Sobre la carpeta del artefacto, excluyendo su propio coverage.md |
bash NO_REPORT=":(exclude)${ARTEFACTO%/}/coverage.md" SPEC_FINGERPRINT=$( { git -C "$ROOT" ls-files -s -- "$ARTEFACTO" "$NO_REPORT"; \ git -C "$ROOT" status --porcelain -uall -- "$ARTEFACTO" "$NO_REPORT"; \ git -C "$ROOT" diff -- "$ARTEFACTO" "$NO_REPORT"; \ } | git hash-object --stdin )
donde $ARTEFACTO es la carpeta del trabajo (docs/specs/user-stories/US-042-…/) o, si el artefacto es un archivo suelto, su ruta. Se reutiliza el reporte solo si coinciden los dos.
La exclusión del propio reporte no es opcional. El
coverage.mdvive dentro de$ARTEFACTO: sin$NO_REPORT, escribirlo en el Paso 7 movería elSPEC_FINGERPRINT, la marca de pie guardaría el hash de antes de escribir, y ninguna corrida posterior coincidiría — la idempotencia no se dispararía nunca. La clave cubre las entradas del reporte, no su salida; es el mismo motivo por el que elFINGERPRINTexcluye**/coverage.md.Y tiene que ser una ruta literal interpolando
$ARTEFACTO, no un glob.':(exclude,glob)**/coverage.md'junto a un pathspec positivo hace que git excluya todo: las tres órdenes devuelven vacío, la clave queda fija en el hash del blob vacío, y entonces la idempotencia se dispara siempre y un criterio editado nunca invalida el reporte. ElEXCdelFINGERPRINTpuede usar globs porque ahí no hay pathspec positivo.
Por qué dos claves y no una. Meter
docs/entero en elFINGERPRINTharía que escribir cualquier informe invalidara las tres cachés — el problema que las exclusiones resuelven. Acotar la segunda clave a la carpeta del artefacto captura exactamente lo que este reporte traza, sin arrastrar el resto de la documentación. Y a diferencia de «detectar que el usuario editó los criterios», es una comprobación determinista: no depende de que el cambio haya ocurrido en esta sesión, ni de que alguien lo mencione.
Lo que sigue sin cubrirse: criterios que viven fuera del repo (un ticket de un tracker, un documento externo). Ahí no hay nada que hashear; si el artefacto es externo, decirlo en el reporte y pedir
revalidatecuando cambie.
Un solo cálculo por corrida, con una excepción. Ambos hashes se computan una vez, en el Paso 0. El
FINGERPRINTsirve para las dos comprobaciones de frescura —la delcoverage.md(Paso 0) y la deltest-run.json(Paso 4, delegación)—; elSPEC_FINGERPRINTsolo para la primera. Los dos se regraban en el Paso 7. La delegación entests-onlyno toca código (y el.gitignoreque puede normalizar está excluido de la receta), así que elFINGERPRINTcalculado en el Paso 0 sigue siendo válido al grabar: no hace falta recalcularlo. Si aun así el valor guardado porquality-checkentest-run.jsonno coincide con el del Paso 0, algo cambió el código en medio: tratar la caché como obsoleta, no como fresca.
Comportamiento (Paso 0 del flujo):
- Resolver la ubicación del trabajo y calcular
FINGERPRINTySPEC_FINGERPRINT. Siempre, haya reporte previo o no: el Paso 7 los necesita para grabar la marca de pie, también en la primera validación. - Buscar su
coverage.md. Si no existe → generar normal (no hay caché). - Si existe, leer los hashes guardados en su marca de pie
(
<!-- trace-validate:verdict=<canónico> · fingerprint=<hash> · spec=<hash> · generated=YYYY-MM-DD -->) y reutilizarlo solo si se cumple todo: coinciden los dos hashes, el reporte no registra una ejecución fallida, y el usuario no pasórevalidate.- Se cumple → no regenerar: devolver el veredicto y el resumen del reporte existente tal cual,
e indicar al usuario que no hubo cambios desde la última validación ({{fecha guardada}}). No reescribir
el archivo, no delegar en
quality-check. - Falla algo, no hay marca de pie (reportes antiguos), o el usuario pide
revalidate→ ejecutar el flujo completo (Pasos 1-7) y regrabar ambos hashes al guardar (Paso 7).
- Se cumple → no regenerar: devolver el veredicto y el resumen del reporte existente tal cual,
e indicar al usuario que no hubo cambios desde la última validación ({{fecha guardada}}). No reescribir
el archivo, no delegar en
- La marca de pie se conserva en el documento publicado (no se elimina como el bloque de instrucciones
de la plantilla). Si está ilegible o incompleta (un hash que no parsea, marca truncada, o falta el
campo
spec=), no intentar repararla ni adivinar: tratar la caché como ausente, regenerar y regrabarla.
Un reporte que no pudo ejecutar las pruebas nunca se sirve desde caché. Si el reporte existente tiene filas en
NOT_RUNporque la delegación no fue posible (sin red, sin runner, el usuario declinó), su⚠️describe un fallo de entorno, no del código. Congelarlo significaría arrastrar ese «no se pudo comprobar» para siempre aunque el entorno ya funcione. Regenerar y volver a intentar la delegación.
Un cambio en una carpeta oculta (
.sdd-devkit/,.github/…), endocs/fuera de la carpeta del artefacto, o en uncoverage.mdno cuenta como cambio: están excluidos, así que ningún artefacto que generen este skill,quality-check,code-reviewoarch-auditdesplaza la clave. Los criterios y losTC-XXXsí cuentan, víaSPEC_FINGERPRINT.
Resultados de pruebas: delegación en quality-check
trace-validate no ejecuta la suite de pruebas. La ejecución es responsabilidad de quality-check,
que la persiste en un artefacto reutilizable test-run.json (esquema test-run/v1). Como el review es
una corrida completa de la rama, este artefacto vive en una ubicación fija, no por unidad:
.sdd-devkit/test-run.json, en la raíz del repositorio.
Cómo obtener los resultados (Paso 4 del flujo):
- Reusar el fingerprint canónico ya calculado en el Paso 0 (mismo valor; no recalcular).
- Si existe
test-run.json, suschemaestest-run/v1, sugeneratedByes"quality-check", sugit.fingerprintcoincide y susuites[]cubre el conjunto vigente (las dos fijas, más e2e si el repo la ejecuta, las que declare el estándar de testing y la entradaarchitecturesi el repo tiene runner de arquitectura) → caché fresca: no hubo cambios desde la corrida dequality-check. Reutilizar los resultados por suite sin ejecutar nada. SigeneratedBytrae cualquier otro valor, descartar la caché y delegar:quality-checkes el único productor autorizado. Si el estándar de testing cambió desde la corrida (suites de más o de menos), la caché es obsoleta aunque el fingerprint coincida —el estándar vive endocs/, excluido del fingerprint—: delegar. Anotar la procedencia en la línea «Pruebas» del Resumen: «resultados tomados de la corrida dequality-checkdel {{commit/fecha}}». - Si no existe o el fingerprint difiere (hubo cambios, o nunca corrió) → delegar en
quality-checken modotests-only, que ejecuta solo los checks de pruebas, escribetest-run.jsony devuelve los resultados. Luego consumir esa caché ya fresca. - Si
quality-checkno puede ejecutarlas (stack no detectable, entorno sin poder correr, dependencias ausentes, o el usuario declina la delegación) → las filas con artefacto van conEjecución = —yResultado =NOT_RUN`, con la razón en «Observaciones y pendientes», y se entrega igualmente la matriz con los artefactos hallados. Nunca fabricar resultados.
Mapeo a la matriz. Cada entrada suites[] de test-run.json trae type y result
(PASS/FAIL/SKIPPED/N/A). Las dos fijas —unit, coverage— vienen siempre; e2e solo si el repo la ejecuta, y el resto son
suites configuradas en el estándar de testing del repo, cuyo type es el ID del requisito que las
declara (p. ej. integration-testing, contract-testing) y que traen su referencia global en standard
(p. ej. testing/integration-testing). Pueden no existir: si el estándar no declara integración, no hay
entrada para ella —y no hay que buscar una clave fija llamada integration—, así que una fila de tipo
Integration se resuelve contra la suite donde el repo la tenga (típicamente unit) o queda
NOT_RUN. No inferir una suite ausente ni inventar su resultado. La suite coverage no se mapea a ningún
criterio: es cobertura de líneas/ramas, una métrica del repo que juzga quality-check, no cobertura
funcional; si viene en FAIL, mencionarlo en «Observaciones y pendientes» y nada más. La entrada
architecture tampoco se mapea: es la corrida del runner de validaciones de arquitectura del repo,
cacheada en el mismo archivo para arch-audit. No es una clase de prueba, no cubre ningún criterio de
aceptación y no se lista en la matriz; si viene en FAIL, como mucho una línea en «Observaciones y
pendientes». Su presencia o ausencia nunca degrada una fila a NOT_RUN. Mapeo al reporte: PASS→PASS,
FAIL→FAIL, SKIPPED→NOT_RUN, N/A (el repo no tiene esa suite)→NOT_RUN, dejando en
«Observaciones y pendientes» que esa clase de prueba no existe en el repo. Un criterio cuya prueba asociada dio FAIL y
se pudo aislar que fue la suya se reporta UNCOVERED con el fallo en Observaciones; si la suite falló
sin poder aislar el test, es PARTIAL (ver «Estados de cobertura» y la nota de granularidad más abajo).
El mapeo es por fila, no por criterio. Cada fila de la matriz (criterio × TC × tipo) se resuelve así:
| Situación de la fila | Evidencia | Ejecución | Resultado |
|---|---|---|---|
| Tipo declarado sin artefacto en el repo | — |
— |
UNCOVERED |
Artefacto hallado y la suite que lo corre dio PASS |
ruta | quality-check |
PASS |
Artefacto hallado y su test dio FAIL (aislable) |
ruta | quality-check |
FAIL |
Artefacto hallado, suite en FAIL sin poder aislar el test |
ruta | quality-check |
FAIL (+ Observación «no aislable» → criterio PARTIAL) |
Artefacto hallado, suite SKIPPED / ausente / no ejecutable |
ruta | — |
NOT_RUN |
TC Manual por diseño |
ruta del TC | Manual |
N/A |
Ejecución dice quién produjo el resultado, no de qué clase es la prueba: eso ya lo dice la columna
Tipo y la ruta de Evidencia. La suite efectiva que corrió el test (dónde vive realmente en el repo)
solo se menciona cuando no coincide con el tipo declarado —un API Test que corre en unit, por
ejemplo—, y va en Observaciones, no en la matriz.
Granularidad suite vs. criterio:
resultes por suite completa, no por test individual. Si varios criterios mapean a tests dentro de la misma suite y esa suite daFAIL, no propagarFAIL/UNCOVEREDa todos ellos sin más: si elsummaryu otro detalle dequality-checkpermite aislar qué test falló, marcar solo ese criterio; si no hay forma de aislarlo, marcar los criterios afectados comoPARTIAL(noUNCOVERED) con la ambigüedad explicada en Observaciones. Detalle completo enreferences/flow.md(Paso 4).
Si el proyecto no usa
quality-check(no está disponible en la sesión), degradar con elegancia: reportar las filas con artefacto enEjecución = —/Resultado =NOT_RUN(«ejecución delegada no disponible») —las que no tienen artefacto siguen enUNCOVERED— y entregar la cobertura estática. No reintroducir un runner propio entrace-validate`.
Cuándo bloquear
Parar y reportar (sin generar reporte parcial) cuando:
- El trabajo no existe o no se encuentra su documento de criterios.
- No hay sección de criterios de aceptación, está vacía, o los criterios no tienen identificador codificado (el formato es indiferente; lo que no puede faltar es el identificador): no hay nada que trazar de forma trazable; sugerir alinear el trabajo con su skill de definición/planificación antes de validar (para un
FT-XXX, completar su especificación con el flujo «Analizar legado» dework-research).
No bloquear por que el artefacto no siga las convenciones del plugin: identificadores fuera del formato
AC-XXX, ubicación fuera dedocs/specs/o ausencia de campoEstado:no son motivos de bloqueo.
` WARNING No es posible generar el reporte de trazabilidad:
- <razón concreta>
- <acción sugerida: p. ej. definir los criterios de aceptación del trabajo antes de validar> `
Ubicación de archivos
Layout completo del harness, identificadores y contrato de archivado: ${PLUGIN_ROOT}/reference/artifacts.md.
Lo propio de este skill:
| Rol | Ruta |
|---|---|
| Artefacto a trazar (entrada) | La carpeta de la US-XXX / WI-XXX / FT-XXX; para cualquier otro artefacto, la ruta que indique el usuario |
Casos de prueba documentados (entrada, los produce test-define) |
test-cases/ dentro de la carpeta del artefacto, con su índice test-cases/README.md |
| Caché de co |
…(truncated)