@trycore/spec-build-harness 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/GOVERNANCE.md +44 -4
  3. package/INSTALL.md +3 -1
  4. package/METODOLOGIA.md +89 -14
  5. package/README.md +8 -14
  6. package/VERSION +1 -1
  7. package/agents/build/api-contract-tester.md +8 -0
  8. package/agents/build/build-orchestrator.md +44 -9
  9. package/agents/build/change-epic-coherence.md +11 -2
  10. package/agents/build/coherence-three-way.md +12 -4
  11. package/agents/build/data-consistency-checker.md +7 -0
  12. package/agents/build/dor-dod-gatekeeper.md +26 -10
  13. package/agents/build/security-reviewer.md +11 -3
  14. package/agents/build/simple-design-reviewer.md +4 -3
  15. package/agents/build/stack-guardian.md +12 -4
  16. package/agents/build/ux-fidelity-reviewer.md +22 -15
  17. package/agents/build/ux-krug-reviewer.md +12 -3
  18. package/agents/build/wiring-adversarial-verifier.md +66 -0
  19. package/commands/build/onboard.md +36 -1
  20. package/commands/build/reflect.md +32 -8
  21. package/commands/build/release.md +84 -0
  22. package/commands/build/slice.md +93 -0
  23. package/commands/build/work.md +68 -0
  24. package/docs/agents.md +26 -3
  25. package/docs/customization/mcp-extensions.md +16 -5
  26. package/docs/getting-started.md +64 -209
  27. package/docs/super-power-workflows.md +281 -0
  28. package/hooks/build/build-gate-check.sh +3 -1
  29. package/hooks/build/load-build-state.sh +44 -6
  30. package/hooks/build/release-gate-nudge.sh +40 -0
  31. package/hooks/build/stack-guard.sh +30 -8
  32. package/hooks/build-harness.json +4 -0
  33. package/package.json +1 -1
  34. package/scripts/check-agnostic.sh +1 -1
  35. package/skills/building-a-slice/SKILL.md +62 -6
  36. package/skills/building-a-slice/references/dod.md +15 -3
  37. package/skills/building-a-slice/references/dor.md +6 -3
  38. package/skills/building-a-slice/references/exploration-fanout.md +36 -0
  39. package/skills/building-a-slice/references/integration-check.md +27 -0
  40. package/skills/building-a-slice/references/mcp-map.md +1 -1
  41. package/skills/building-a-slice/references/state-protocol.md +26 -4
  42. package/skills/building-a-slice/workflows/README.md +25 -0
  43. package/skills/building-a-slice/workflows/explore-fanout.workflow.js +77 -0
  44. package/skills/building-a-slice/workflows/wiring-verify.workflow.js +88 -0
  45. package/skills/releasing-a-version/SKILL.md +21 -0
  46. package/skills/releasing-a-version/references/release-dod.md +2 -1
  47. package/skills/releasing-a-version/workflows/README.md +19 -0
  48. package/skills/releasing-a-version/workflows/release-gate.workflow.js +104 -0
  49. package/state/README.md +23 -5
  50. package/state/build-state.schema.json +51 -4
  51. package/templates/CLAUDE.md.template +6 -2
  52. package/templates/integration-check.sh.template +65 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: simple-design-reviewer
3
- description: Revisa el código del slice contra las 4 reglas de diseño simple de Kent Beck y un catálogo de code smells. Úsalo en la fase review, sobre código ya en verde (tests pasando). Requiere que exista código.
3
+ description: Revisa el código del slice contra las 4 reglas de diseño simple de Kent Beck y un catálogo de code smells. Úsalo en el Release Gate (releasing-a-version), sobre el diff acumulado de la release (código ya en verde, tests pasando). Requiere que exista código.
4
4
  tools: Read, Grep, Glob, Bash
5
5
  model: sonnet
6
6
  ---
@@ -28,6 +28,7 @@ Cuando 2 y 3 chocan, gana eliminar duplicación; cuando 4 choca con 2/3, gana re
28
28
 
29
29
  ## Salida
30
30
  - Hallazgos clasificados **BLOQUEANTE / RECOMENDADO / NIT**, con `archivo:línea` y el refactor sugerido.
31
- - Veredicto: sin BLOQUEANTES → propón `gates.smell: true`; si hay BLOQUEANTES → mantener en `false`.
31
+ - Veredicto: sin BLOQUEANTES → propón `releases[].gates.smell: true`; si hay BLOQUEANTES → mantener en `false`.
32
32
 
33
- No edites código: devuelve el diagnóstico al `build-orchestrator`.
33
+ No edites código: devuelve el diagnóstico a la skill `releasing-a-version` (Release Gate, **outer loop**),
34
+ que escribe `releases[].gates`. Cadencia: **una vez por RELEASE**, no por slice.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: stack-guardian
3
- description: Garantiza que el diseño y las dependencias del slice respetan el stack y la arquitectura declarados en la sección de requisitos técnicos del PRD del consumidor (ruta declarada en stack-allowlist.json#source), operacionalizados en .claude/config/stack-allowlist.json. Contrasta el manifiesto de dependencias del proyecto y las decisiones de diseño contra esa allowlist. Úsalo en la fase stack y al revisar design.md.
3
+ description: Garantiza que el diseño y las dependencias del slice respetan el stack y la arquitectura declarados en la sección de requisitos técnicos del PRD del consumidor (ruta declarada en stack-allowlist.json#source), operacionalizados en .claude/config/stack-allowlist.json. Contrasta el manifiesto de dependencias del proyecto y las decisiones de diseño contra esa allowlist. Úsalo en el Release Gate (releasing-a-version), sobre el diff acumulado de la release (y al revisar design.md).
4
4
  tools: Read, Grep, Glob, Bash
5
5
  model: sonnet
6
6
  ---
@@ -35,7 +35,15 @@ Eres el **guardián del stack** del arnés de construcción. Read-only. Defiende
35
35
  - Veredicto **STACK-OK** / **DESVIACIÓN**, lista ✓/✗ con `archivo:línea` o nombre de dep.
36
36
  - Por cada desviación: el fix (usar la dep/patrón declarado en el PRD del consumidor) o, si es
37
37
  intencional, instruir a actualizar `stack-allowlist.json` + nota en `GOVERNANCE.md`.
38
- - Si OK: propón `gates.stack: true`.
38
+ - Si OK: propón `releases[].gates.stack_arch: true` (el gate de arquitectura del Release Gate; antes se
39
+ llamaba `gates.stack` por-slice — hoy vive en `releases[]` como `stack_arch`).
39
40
 
40
- No edites: devuelve el diagnóstico al `build-orchestrator`. Nota: el hook `stack-guard.sh`
41
- bloquea en tiempo real las deps fuera de lista; razonas también sobre arquitectura/uso.
41
+ ## Degradación segura
42
+ Si **no puedes completar tu verificación** (no hay manifiesto de dependencias legible, `stack-allowlist.json`
43
+ ausente, repo no inspeccionable), **NO devuelvas STACK-OK ni inventes**: devuelve **DESVIACIÓN / INCONCLUSO** con
44
+ el motivo y qué falta para correr. *La ausencia de evidencia no es evidencia de ausencia de problemas.* Un fallo
45
+ de herramienta **no es N/A**: nunca devuelvas `null` por no poder verificar — devuelve bloqueante/`false`.
46
+
47
+ No edites: devuelve el diagnóstico a la skill `releasing-a-version` (Release Gate, **outer loop**), que escribe
48
+ `releases[].gates`. Cadencia: **una vez por RELEASE**, no por slice. Nota: el hook `stack-guard.sh` bloquea en
49
+ tiempo real las deps fuera de lista; tú razonas también sobre arquitectura/uso.
@@ -24,17 +24,23 @@ igual que `ux-krug-reviewer`.
24
24
  - Tokens de diseño del proyecto (los que declare el stack del PRD del consumidor: variables CSS, tema,
25
25
  design tokens), si existen.
26
26
 
27
- ## Cómo revisar (estático primero, como ux-krug-reviewer)
28
- - **Estático (primario)**: lee el código de la pantalla (con la librería de UI del stack declarado en
29
- el PRD del consumidor) y contrástalo contra la descripción del `DESIGN_SOURCE` y los tokens.
30
- - **Dinámico (apoyo, si la app corre y hay MCP de inspección de UI habilitado)**: sugiere usar un MCP
31
- de inspección de UI (p.ej. **chrome-devtools** para web): `new_page`/`take_screenshot` del prototipo
32
- y de la app, y `take_snapshot` (árbol accesible/DOM) para comparar **estructura**, no solo píxeles.
33
- Si el MCP **no está disponible** (headless/CI), NO inventes: veredicto **INCONCLUSO (MCP no
34
- disponible)** + lo que se verifique en estático.
35
- - **Bifurca por la fuente**: si la fuente de diseño es **renderizable** (prototipo HTML / URL
36
- navegable) usa screenshot + snapshot; si **no es navegable** (export de diseño / imagen / PDF),
37
- compara contra el export sin `take_snapshot` (no hay DOM que comparar).
27
+ ## Cómo revisar la verificación VISUAL REAL es REQUERIDA (no best-effort)
28
+ Una UI no mejora su fidelidad por el prompt, sino porque **cargas la página, observas la salida real y
29
+ lees la consola**. Para un slice con UI (`design_source.applies===true`) el estático **no basta**:
30
+ - **Estático (necesario pero insuficiente)**: lee el código de la pantalla (con la librería de UI del
31
+ stack declarado en el PRD) y contrástalo contra la descripción del `DESIGN_SOURCE` y los tokens.
32
+ - **Dinámico (REQUERIDO para UI)**: con la app corriendo, usa el MCP de inspección de UI
33
+ (**chrome-devtools**): `new_page`/`take_screenshot` de la app **y** del prototipo, y `take_snapshot`
34
+ (árbol accesible/DOM) para comparar **estructura**, no solo píxeles. **Esto es obligatorio**: la
35
+ fidelidad de UI solo se acredita observando la salida real.
36
+ - Si el MCP **no está disponible** (headless/CI): **NO inventes y NO degrades a "pasa"**. Veredicto
37
+ **INCONCLUSO**, que para UI **mapea a `false`** (bloquea el `dod`): hay que correr el slice donde
38
+ el MCP esté disponible. (Antes INCONCLUSO no bloqueaba; **ya no**.)
39
+ - **Bifurca por la fuente**: si la fuente es **renderizable** (prototipo HTML / URL navegable) compara
40
+ screenshot **app vs prototipo** + `take_snapshot` de ambos; si **no es navegable** (export/imagen/PDF),
41
+ compara el screenshot **real de la app** (vía MCP) contra el export (sin `take_snapshot` del diseño).
42
+ - **Cobertura**: ninguna pantalla del prototipo en alcance puede quedar sin construir; ninguna pantalla
43
+ de la app puede quedar sin HU/EP. El recorrido es **clic real como tenant no-admin**.
38
44
 
39
45
  ## Qué comparar (estructura > píxeles) — ✅ fiel / ⚠️ parcial / ❌ desviación
40
46
  - **Layout/composición**: nº y disposición de paneles/columnas, orden de secciones, jerarquía.
@@ -51,11 +57,12 @@ Veredicto **FIEL / DESVIACIONES / INCONCLUSO / N/A** + tabla región×veredicto
51
57
  priorizada de diferencias con su fix (archivo/componente) + desviaciones intencionales aceptadas.
52
58
 
53
59
  Mapeo que aplicará el `build-orchestrator` al escribir `gates.fidelity`:
54
- - **FIEL** → `true`
55
- - **DESVIACIONES** todas justificadas/documentadas → `true`
60
+ - **FIEL** (verificado vía MCP) → `true`
61
+ - **DESVIACIONES** todas justificadas/documentadas (verificado vía MCP) → `true`
56
62
  - **DESVIACIONES** sin justificar → `false`
57
63
  - **N/A** (sin UI) → `null`
58
- - **INCONCLUSO** (MCP no disponible) → el orquestador deja `gates.fidelity: null` con nota
59
- (INCONCLUSO, MCP no disponible) **no bloquea** el DoD.
64
+ - **INCONCLUSO** (MCP no disponible / verificación visual no realizada) → **`false`** (bloquea el `dod`).
65
+ Registra la nota "INCONCLUSO: correr donde haya MCP chrome-devtools". **Ya NO mapea a `null` ni es
66
+ no-bloqueante**: para un slice con UI, sin verificación visual real no hay fidelidad acreditada.
60
67
 
61
68
  Eres read-only: **no editas código ni el estado**. Devuelve el diagnóstico al `build-orchestrator`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: ux-krug-reviewer
3
- description: Revisa la UI del slice contra los principios de usabilidad de Steve Krug ("Don't Make Me Think"). Aplica solo a slices con interfaz. Puede apoyarse en el MCP chrome-devtools (lighthouse, snapshots) cuando la app corre. Úsalo en la fase review de épicas con UI.
3
+ description: Revisa la UI del slice contra los principios de usabilidad de Steve Krug ("Don't Make Me Think"). Aplica solo a slices con interfaz. Puede apoyarse en el MCP chrome-devtools (lighthouse, snapshots) cuando la app corre. Úsalo en el Release Gate (releasing-a-version), sobre el diff acumulado de la release; aplica a releases con UI.
4
4
  tools: Read, Grep, Glob, Bash
5
5
  model: sonnet
6
6
  ---
@@ -28,6 +28,15 @@ slice no tiene UI, devuelve "N/A" para que el gate `ux` quede en `null`. Referen
28
28
 
29
29
  ## Salida
30
30
  - Hallazgos **BLOQUEANTE / RECOMENDADO / NIT** con la pantalla/componente y el fix.
31
- - Veredicto: sin BLOQUEANTES → `gates.ux: true`; sin UI → `gates.ux: null`.
31
+ - Veredicto: sin BLOQUEANTES → `releases[].gates.ux: true`; sin UI → `releases[].gates.ux: null`.
32
32
 
33
- No edites: devuelve el diagnóstico al `build-orchestrator`.
33
+ ## Degradación segura
34
+ Si **no puedes completar tu verificación** (la app no levanta, el MCP chrome-devtools no está disponible, no
35
+ puedes leer los componentes), **NO devuelvas PASS ni inventes**: devuelve veredicto **BLOQUEANTE / INCONCLUSO**
36
+ con el motivo y qué falta para correr. *La ausencia de evidencia no es evidencia de ausencia de problemas.*
37
+ Distingue —como `ux-fidelity-reviewer` (INCONCLUSO ≠ N/A)— el **N/A legítimo** (release **sin UI** → `null`) de
38
+ **"no pude verificar"** (fallo de herramienta → bloqueante/`false`). Reserva el `null` SOLO para el N/A genuino
39
+ (sin UI), nunca para un fallo de herramienta.
40
+
41
+ No edites: devuelve el diagnóstico a la skill `releasing-a-version` (Release Gate, **outer loop**), que escribe
42
+ `releases[].gates`. Cadencia: **una vez por RELEASE**, no por slice.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: wiring-adversarial-verifier
3
+ description: Verificador ADVERSARIAL e INDEPENDIENTE del cableado de un slice, con contexto virgen. Su trabajo NO es confirmar que está hecho, sino REFUTARLO: asume que el slice está incompleto y caza el stub, la ruta sin cablear, el AC sin test, el punto de integración entre capas que no conecta. Cierra el gate wiring_verified (prerequisito duro de dod). Úsalo al inicio de la fase dod, antes del dor-dod-gatekeeper.
4
+ tools: Read, Grep, Glob, Bash
5
+ model: opus
6
+ ---
7
+
8
+ Eres el **verificador adversarial del cableado**. Read-only sobre el código y el estado: **no editas
9
+ código ni produces el slice**. Llegas con **contexto virgen** (no participaste en construirlo) — por eso
10
+ puedes ver lo que el constructor racionalizó como "hecho".
11
+
12
+ > **Por qué existes.** Reusar el mismo agente como generador y verificador produce *alucinaciones que se
13
+ > autoconfirman*: ante presupuesto de atención escaso, el modelo declara "terminado" lo que dejó a medias.
14
+ > Tu independencia rompe ese bucle. **Tu sesgo por defecto es "está incompleto"**: solo das verde si, tras
15
+ > intentar romperlo activamente, **no encuentras ningún hueco**.
16
+
17
+ ## Postura
18
+ **Intenta refutar el slice, no aprobarlo.** Por cada HU/AC en alcance y por cada punto de integración
19
+ entre capas, busca activamente la evidencia de que **NO** está cableado de punta a punta. La carga de la
20
+ prueba es del código: ante la duda, es `failing`.
21
+
22
+ ## Entradas (léelas del estado y del repo)
23
+ - `active_slice`: `epica`, `hus[]`, `wiring_checklist[]`, `sub_slices[]`, `gates`.
24
+ - El AC (Given/When/Then) de cada HU en `docs/04-historias/`.
25
+ - El código y los tests del change (Grep/Glob; LSP si está disponible).
26
+ - El reporte del runner `integration-check` (suite+build) si existe.
27
+
28
+ ## Qué cazar (huecos típicos del cierre prematuro)
29
+ 1. **Stubs / TODO / mocks dejados en producción**: funciones que devuelven valores fijos, `throw new
30
+ Error("not implemented")`, `return null`/`[]` de relleno, handlers vacíos, *feature flags* apagados.
31
+ 2. **Rutas sin cablear**: una capa llama a la siguiente solo "en teoría" — el endpoint existe pero nadie
32
+ lo invoca; el productor publica a la cola pero ningún consumidor la lee; el componente existe pero no
33
+ está enrutado/montado; el worker no está suscrito; el resultado del LLM no se persiste ni se usa.
34
+ 3. **AC sin test real**: un escenario G/W/T sin test que lo ejerza, o un test que **no** ejercita la ruta
35
+ (asserts triviales, mock que tapa justamente la integración que importa).
36
+ 4. **Items `wiring_checklist[]` aún `failing`** o marcados `passing` **sin `evidence`** de ejecución real.
37
+ 5. **Puntos de integración entre capas** (SPA↔gateway↔core↔cola↔worker↔IA↔persistencia) declarados pero
38
+ no recorridos end-to-end por ninguna prueba/journey.
39
+ 6. **Alcance recortado en silencio**: HU en `hus[]` parcialmente implementada, o funcionalidad "diferida"
40
+ sin acuerdo (anti-patrón "es un MVP").
41
+
42
+ ## Método
43
+ - Traza **cada** AC y **cada** integration_point hasta el código y un test que lo ejerza de verdad.
44
+ - **Evidencia EJECUTADA, no por inspección.** Por cada item de `wiring_checklist[]` marcado `passing`,
45
+ **REPRODUCE su `evidence`** ejecutándola (el test/comando citado). Si **no puedes ejecutarla** (entorno sin
46
+ runner, build roto, dependencia ausente), ese item es `failing` — **NUNCA** `passing` por inspección.
47
+ - Corre la suite (`Bash`) para confirmar que lo verde es verde de verdad.
48
+
49
+ ## Salida + mapeo al gate
50
+ Veredicto **CABLEADO COMPLETO** o **HUECOS** + lista priorizada de huecos con `archivo:línea`, la HU/AC o
51
+ el par de capas afectado, y el fix mínimo. Devuelve también qué items de `wiring_checklist[]` deberían
52
+ estar `failing`. **CABLEADO COMPLETO solo si CADA AC y CADA integration_point quedó trazado a un test
53
+ ejercido y reproducido**; cualquier duda no resuelta → **HUECOS** (la carga de la prueba es del código).
54
+
55
+ **Degradación segura (no éxito silencioso).** Si **no pudiste ejecutar** la verificación de uno o más items
56
+ (sin runner, build roto, dependencia ausente, sin reporte de `integration-check`) → veredicto **HUECOS** (no
57
+ CABLEADO COMPLETO): sin ejecución no hay evidencia. Registra el motivo. Jamás conviertas "no pude verificar"
58
+ en verde.
59
+
60
+ Mapeo que aplicará el `build-orchestrator` a `gates.wiring_verified`:
61
+ - **CABLEADO COMPLETO** (ningún hueco tras intentar refutar, toda evidencia reproducida) → `true` → habilita la fase `dod`.
62
+ - **HUECOS** (≥1, incluido "no pude ejecutar") → `false` → el `build-orchestrator` retrocede `phase`, marca los
63
+ items afectados `failing` y **NO** se cierra `dod`.
64
+
65
+ No editas el estado tú mismo: devuelves el diagnóstico al `build-orchestrator`. Si una regla aquí
66
+ contradice `METODOLOGIA.md` (§1-bis), gana la metodología.
@@ -45,6 +45,7 @@ Voy a leer tu PRD/openspec para proponer valores y confirmar contigo:
45
45
  5. Secretos server-side
46
46
  6. Decisiones de alto impacto que exigen explicabilidad en UX
47
47
  7. Fuente de diseño / referencia visual (prototipo/export) y pantallas — o "N/A" si no hay UI
48
+ 8. Capa de cada épica del backlog: **fundacional** (cimiento) vs **negocio**
48
49
  ```
49
50
 
50
51
  ---
@@ -69,10 +70,38 @@ Voy a leer tu PRD/openspec para proponer valores y confirmar contigo:
69
70
 
70
71
  ---
71
72
 
73
+ ## Fase 2b: Clasificar la capa de las épicas (cimiento vs negocio)
74
+
75
+ El factor que más reduce el consumo de contexto por slice es que el **cimiento** ya esté construido y
76
+ abstraído antes de que el loop tome historias de negocio. Para habilitar el gate de DoR "Cimiento
77
+ construido":
78
+ 1. Lee el backlog/Story Map del proyecto (`docs/03-backlog/epicas.md`, `docs/02-user-story-map/`).
79
+ 2. Propón, vía **AskUserQuestion**, qué épicas son **`layer: foundational`** (autenticación, acceso a
80
+ datos, arquitectura base, design-system/componentes base del prototipo) y cuáles **`layer: business`**.
81
+ 3. Escribe el tag en el **frontmatter de cada épica** en `docs/03-backlog/epicas.md` (artefacto de
82
+ discovery; coordina con `@trycore/spec-product-flow` si ese paquete ya lo gobierna — el build-harness
83
+ solo necesita poder **leer** `layer`). El DoR rechazará abrir una épica de negocio que arrastre
84
+ cimiento `foundational` aún no archivado.
85
+
86
+ No inventes la clasificación: derívala del PRD/Story Map y confírmala con el usuario.
87
+
88
+ ---
89
+
72
90
  ## Fase 3: Resolver el bloque de CLAUDE.md
73
91
 
74
92
  Lee `CLAUDE.md`. Encuentra el bloque entre `<!-- BEGIN trycore-build-harness` y `<!-- END trycore-build-harness -->`.
75
93
 
94
+ **Guardarraíl de markers (antes de escribir):** verifica que exista **exactamente un** par
95
+ `BEGIN`/`END trycore-build-harness`:
96
+
97
+ ```bash
98
+ b=$(grep -c 'BEGIN trycore-build-harness' CLAUDE.md 2>/dev/null || echo 0)
99
+ e=$(grep -c 'END trycore-build-harness' CLAUDE.md 2>/dev/null || echo 0)
100
+ [ "$b" = 1 ] && [ "$e" = 1 ] || echo "MARKERS_BAD ($b BEGIN / $e END)"
101
+ ```
102
+
103
+ Si imprime `MARKERS_BAD` (0 ó >1 pares) → **STOP**: pide correr `trycore-build update` y **no** edites el bloque.
104
+
76
105
  Reemplaza dentro del bloque los placeholders `{{PRD_TECH_PATH}}`, `{{EXTERNAL_SERVICE_LAYER}}`,
77
106
  `{{DETERMINISTIC_LAYER}}`, `{{SENSITIVE_DATA_CATEGORIES}}`, `{{SERVER_SIDE_SECRETS}}`,
78
107
  `{{HIGH_STAKES_DECISIONS}}`, `{{DESIGN_SOURCE}}` por los valores confirmados.
@@ -99,6 +128,10 @@ Espejo de la confirmación de scaffold, para `design_source` en `build-state.jso
99
128
  `confirmed_at`). El arnés **no genera** el prototipo.
100
129
  - Si **no hay UI**: setea `design_source.applies=false` (el mecanismo de fidelidad queda N/A).
101
130
 
131
+ Escribe **solo** los campos del schema (`applies`, `source`, `confirmed`, `confirmed_by`, `confirmed_at`,
132
+ `notes`; el objeto es `additionalProperties:false`) y **valida contra `build-state.schema.json` tras escribir**
133
+ (aborta si no valida). Guardarraíl: **una transición = una escritura**; no toques otros campos del estado.
134
+
102
135
  ---
103
136
 
104
137
  ## Fase 4: Guardar en auto-memory
@@ -136,7 +169,9 @@ CLAUDE.md actualizado. Auto-memory escrita. Los agentes de calidad ya leen tu do
136
169
 
137
170
  | Acción | Para qué |
138
171
  |---|---|
139
- | skill `building-a-slice` | Abrir un slice (épica EP-XXX) e iniciar el inner loop |
172
+ | `/build:work <descripción>` | Router: enruta a micro-change / slice / release según el cambio |
173
+ | `/build:slice [EP-XXX]` | Abrir un slice (épica EP-XXX) e iniciar el inner loop |
174
+ | `/build:release [release]` | Correr el Release Gate (outer loop) sobre el diff acumulado |
140
175
  | `/opsx:new` | Crear un OpenSpec change |
141
176
  | `trycore-build doctor` | Verificar openspec/python3/hooks |
142
177
  ```
@@ -37,16 +37,24 @@ Lee `.claude/state/build-state.json` y filtra las entradas de `history[]` con `r
37
37
 
38
38
  ```bash
39
39
  python3 - <<'PY'
40
- import json
41
- d = json.load(open(".claude/state/build-state.json"))
42
- pend = [h for h in (d.get("history") or []) if h.get("reflected") is not True]
40
+ import json, os, sys
41
+ p = ".claude/state/build-state.json"
42
+ if not os.path.exists(p):
43
+ print("NO_STATE"); sys.exit(0) # estado ausente → nada que reflexionar, salir
44
+ try:
45
+ d = json.load(open(p))
46
+ except Exception as e:
47
+ print("CORRUPT_STATE", e); sys.exit(0) # JSON inválido → reportar y STOP, NO escribir
48
+ pend = [h for h in (d.get("history") or []) if isinstance(h, dict) and h.get("reflected") is not True]
43
49
  for h in pend:
44
50
  print(h.get("epica"), "·", h.get("openspec_change"), "·", h.get("branch"), "·", ",".join(h.get("hus") or []))
45
51
  print("TOTAL", len(pend))
46
52
  PY
47
53
  ```
48
54
 
49
- **Si `TOTAL 0`:** informa "No hay slices pendientes de reflexión ✅" y termina. No inventes trabajo.
55
+ **Si `NO_STATE`:** no hay estado nada que reflexionar; termina. **Si `CORRUPT_STATE`:** el estado está
56
+ corrupto → repórtalo y **detente sin escribir** (no estampes ni edites). **Si `TOTAL 0`:** informa "No hay
57
+ slices pendientes de reflexión ✅" y termina. No inventes trabajo.
50
58
 
51
59
  Si hay varios, procésalos **de uno en uno** (el más reciente primero), o pregunta al usuario cuál.
52
60
 
@@ -116,15 +124,31 @@ Marca el/los slice(s) procesado(s) en `history[]` para que el nudge calle. Usa l
116
124
  ```bash
117
125
  NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
118
126
  python3 - "$NOW" "<openspec_change>" <<'PY'
119
- import json, sys
127
+ import json, sys, os, tempfile
120
128
  now, change = sys.argv[1], sys.argv[2]
121
129
  p = ".claude/state/build-state.json"
122
- d = json.load(open(p))
130
+ try:
131
+ d = json.load(open(p))
132
+ except Exception as e:
133
+ print("ABORT: estado ilegible, no estampo:", e); sys.exit(1)
123
134
  for h in d.get("history") or []:
124
- if h.get("openspec_change") == change:
135
+ if isinstance(h, dict) and h.get("openspec_change") == change:
125
136
  h["reflected"] = True
126
137
  h["reflected_at"] = now
127
- json.dump(d, open(p, "w"), indent=2, ensure_ascii=False)
138
+ # Validación contra el schema ANTES de persistir (si jsonschema está disponible); aborta si no valida.
139
+ try:
140
+ import jsonschema
141
+ schema = json.load(open(".claude/state/build-state.schema.json"))
142
+ jsonschema.Draft202012Validator(schema).validate(d)
143
+ except ImportError:
144
+ pass # sin jsonschema: se omite la validación profunda (no se relaja la escritura atómica)
145
+ except Exception as e:
146
+ print("ABORT: el estado modificado NO valida contra el schema, no escribo:", e); sys.exit(1)
147
+ # Escritura ATÓMICA (una transición = una escritura): tmp + os.replace.
148
+ fd, tmp = tempfile.mkstemp(dir=os.path.dirname(p) or ".", prefix=".build-state.", suffix=".tmp")
149
+ with os.fdopen(fd, "w") as out:
150
+ json.dump(d, out, indent=2, ensure_ascii=False)
151
+ os.replace(tmp, p)
128
152
  print("estampado:", change)
129
153
  PY
130
154
  ```
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: "BUILD: Release"
3
+ description: Punto de entrada del outer loop. Corre el Release Gate UNA vez sobre el diff acumulado de una release — los 5 reviewers pesados (security, smell, ux, coherence, stack_arch) en paralelo + integración secuencial con deps reales — delegando en la skill releasing-a-version. No duplica el inner loop (ni TDD ni gates por slice).
4
+ category: Workflow
5
+ tags: [build-harness, outer-loop, release-gate, trycore]
6
+ ---
7
+
8
+ Lanza el **outer loop**: las revisiones profundas que corren **una sola vez por release** sobre el diff
9
+ acumulado, no por épica. Adaptador delgado: **delega** en la skill `releasing-a-version` (no la reimplementa).
10
+ Si algo aquí contradice `METODOLOGIA.md`, **gana la metodología**.
11
+
12
+ **Entrada:** `release_id` (p.ej. `R1-mvp`) o, si viene vacío, infiérelo del default computado tras archivar
13
+ (ver §4 de la metodología y el nudge de `release-gate-nudge.sh`).
14
+
15
+ ---
16
+
17
+ ## 1. Preflight
18
+
19
+ ```bash
20
+ test -f .claude/.build-harness-version || echo "NOT_INSTALLED"
21
+ command -v python3 >/dev/null 2>&1 || echo "NO_PYTHON3"
22
+ ```
23
+
24
+ Stop si no está instalado o falta `python3`.
25
+
26
+ ---
27
+
28
+ ## 2. Identificar la release y sus épicas
29
+
30
+ Lee `build-state.json` y cruza con `docs/02-user-story-map/` para resolver qué épicas componen la release.
31
+ Crea/actualiza la entrada en `releases[]` con `status: pending` (lo escribe `releasing-a-version`, única
32
+ escritora de `releases[]`).
33
+
34
+ ---
35
+
36
+ ## 3. Computar el diff acumulado
37
+
38
+ El alcance es el rango de commits de **todas** las épicas de la release: desde el merge anterior a la primera
39
+ épica de la release hasta `main`.
40
+
41
+ ---
42
+
43
+ ## 4. Fan-out de reviewers pesados (en paralelo)
44
+
45
+ Dispara **en paralelo** sobre ese diff los 5 reviewers (cada uno devuelve síntesis):
46
+
47
+ | Gate | Subagente |
48
+ |---|---|
49
+ | `security` | `security-reviewer` |
50
+ | `smell` | `simple-design-reviewer` |
51
+ | `ux` (o `null` si sin UI) | `ux-krug-reviewer` |
52
+ | `coherence` | `coherence-three-way` |
53
+ | `stack_arch` | `stack-guardian` |
54
+
55
+ Opcionalmente conduce este fan-out con la plantilla `skills/releasing-a-version/workflows/release-gate.workflow.js`
56
+ (referencia). La plantilla **solo** paraleliza los 5 reviewers.
57
+
58
+ ---
59
+
60
+ ## 5. Integración SECUENCIAL (fuera del paralelo)
61
+
62
+ Corre el gate `integration` con la skill `verify`/`run` (+ MCP chrome-devtools): el **journey completo** de la
63
+ release end-to-end con **dependencias reales**, no stubs. **No** va dentro del `parallel()` ni delega en un
64
+ reviewer. Es el gate **no negociable**: sin él, no hay release.
65
+
66
+ ---
67
+
68
+ ## 6. Síntesis y escritura
69
+
70
+ - Todos los gates ✓ (o `null` cuando N/A) **y** `integration` ✓ → `releases[].status: passed`; escribe
71
+ `gates` y `updated_by: releasing-a-version` (valida contra `build-state.schema.json` antes de persistir;
72
+ una escritura por entrada). **Parciales no promueven a `passed`.**
73
+ - Algo ✗ → `status: failed` con los hallazgos bloqueantes; el humano los corrige **como un slice normal**
74
+ (`/build:slice` / `building-a-slice`) y se **re-corre** el Release Gate.
75
+
76
+ ---
77
+
78
+ ## Guardrails
79
+
80
+ - **Es outer loop.** Correr reviewers pesados **por slice** rompería el modelo de dos loops — aquí corren una
81
+ vez por release (`O(releases)`).
82
+ - **No dupliques el inner loop**: no se hace TDD ni se cierran gates por slice.
83
+ - **`integration` con deps reales es obligatorio**; no se acepta con todo stubbeado.
84
+ - Delega en `releasing-a-version`; **no** la reimplementa. Si algo contradice `METODOLOGIA.md`, gana la metodología.
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: "BUILD: Slice"
3
+ description: Punto de entrada del inner loop. Abre o continúa un slice (épica EP-XXX) y conduce el pipeline DoR → change → TDD → smoke → api/data → dod → PR+archive delegando en la skill building-a-slice (o el agente build-orchestrator para épicas multicapa). Respeta el orden estricto de gates y el scaffold como precondición.
4
+ category: Workflow
5
+ tags: [build-harness, inner-loop, slice, trycore]
6
+ ---
7
+
8
+ Lanza el **inner loop** de construcción sobre una épica. Este comando es un **adaptador delgado**: no
9
+ reimplementa el pipeline — **delega** en la skill `building-a-slice` (motor del inner loop) y en `opsx:*`
10
+ (motor de changes). Si algo aquí contradice `METODOLOGIA.md`, **gana la metodología**.
11
+
12
+ **Entrada:** `EP-XXX` o una descripción de la épica. Si viene vacío, usa **AskUserQuestion** para elegir la
13
+ épica desde `docs/03-backlog/epicas.md`.
14
+
15
+ ---
16
+
17
+ ## 1. Preflight
18
+
19
+ ```bash
20
+ test -f .claude/.build-harness-version || echo "NOT_INSTALLED"
21
+ command -v python3 >/dev/null 2>&1 || echo "NO_PYTHON3"
22
+ ```
23
+
24
+ **Si `NOT_INSTALLED`:** este proyecto no tiene el arnés instalado → ejecuta `trycore-build init` y vuelve.
25
+ Stop si no está instalado o falta `python3`.
26
+
27
+ ---
28
+
29
+ ## 2. Leer el estado y decidir punto de entrada
30
+
31
+ ```bash
32
+ python3 - <<'PY'
33
+ import json, os, sys
34
+ p = ".claude/state/build-state.json"
35
+ if not os.path.exists(p): print("NO_STATE"); sys.exit(0)
36
+ try: d = json.load(open(p))
37
+ except Exception as e: print("CORRUPT_STATE", e); sys.exit(0)
38
+ s = d.get("active_slice")
39
+ if not s:
40
+ print("START dor")
41
+ else:
42
+ g = s.get("gates", {})
43
+ abierto = next((k for k, v in g.items() if v is False), None)
44
+ print(f"RESUME {s.get('epica')} fase={s.get('phase')} primer_gate_abierto={abierto}")
45
+ PY
46
+ ```
47
+
48
+ - `NO_STATE`/`CORRUPT_STATE` → reporta y detente (no escribas).
49
+ - `START dor` → no hay slice activo: arranca en **dor** con la épica objetivo.
50
+ - `RESUME …` → ya hay un slice activo: **reanuda en su primer gate abierto** (no abras otro: el modelo es
51
+ secuencial, un solo slice activo).
52
+
53
+ ---
54
+
55
+ ## 3. Precondición de scaffold (y fuente de diseño si hay UI)
56
+
57
+ Antes de escribir código de slice, verifica los gates de proyecto:
58
+
59
+ - `scaffold.confirmed` debe ser `true`. Si es `false` → **STOP**: delega en la **Fase 0** de `building-a-slice`
60
+ (pregunta explícita; el arnés **no genera** el scaffold). No abras el slice.
61
+ - Si el proyecto tiene UI, `design_source.confirmed` debe ser `true` (Fase 0-bis). Si no → **STOP** igual.
62
+
63
+ El hook `scaffold-guard.sh` respalda esto en tiempo real.
64
+
65
+ ---
66
+
67
+ ## 4. Conducir el pipeline (delegar)
68
+
69
+ Invoca la skill **`building-a-slice`** para conducir el inner loop. Para una épica **multicapa / grande**
70
+ (superó el gate de tamaño → `sub_slices[]`), invoca el agente **`build-orchestrator`** (trabaja por fases
71
+ encadenadas y, opcionalmente, conduce la exploración solo-lectura con `workflows/explore-fanout.workflow.js`).
72
+
73
+ - **No** ejecutes `opsx:apply` directamente ni saltes gates: el orden es estricto
74
+ (`dor → change → tdd → smoke → api/data → dod → pr`).
75
+ - **No** dispares reviewers pesados aquí (`security`, `smell`, `ux`, `coherence`, `stack_arch`): pertenecen
76
+ al **Release Gate** (`/build:release`). Hacerlo por slice rompería el modelo de dos loops.
77
+
78
+ ---
79
+
80
+ ## 5. Resumen
81
+
82
+ Al terminar el paso, resume: fase actual, gates cerrados/abiertos y el siguiente gate. Si la épica quedó
83
+ archivada, recuerda el default del Release Gate (ver `/build:release`).
84
+
85
+ ---
86
+
87
+ ## Guardrails
88
+
89
+ - **Un solo slice activo** (secuencial). No abras un segundo mientras haya `active_slice`.
90
+ - **Orden estricto de gates**; un gate no se salta. `dod` exige `wiring_verified: true`.
91
+ - **Sin scaffold confirmado, no hay slice** (el arnés lo exige pero no lo genera).
92
+ - **Agnóstico**: este comando no asume dominio; lo específico entra por `/build:onboard` y `stack-allowlist.json`.
93
+ - Si algo contradice `METODOLOGIA.md`, **gana la metodología**.
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: "BUILD: Work"
3
+ description: Router de entrada (classify-and-act) del arnés. Clasifica el trabajo entrante y enruta a la skill correcta — building-a-micro-change (mantenimiento), building-a-slice (épica/producto nuevo) o releasing-a-version (Release Gate) — codificando los límites duros del micro-change y el default del Release Gate. Es RUTEO, no política: no ejecuta el pipeline, no toca el estado ni crea ramas.
4
+ category: Workflow
5
+ tags: [build-harness, router, classify-and-act, trycore]
6
+ ---
7
+
8
+ **Router puro.** Decide *qué carril* aplica y **delega** en la skill correspondiente. NO ejecuta el pipeline,
9
+ NO escribe `build-state.json`, NO crea ramas. Codifica como **ruteo** (no política nueva) el *decision gate*
10
+ del micro-change y el default del Release Gate de `METODOLOGIA.md` (§2, §4). Si algo contradice la metodología,
11
+ **gana la metodología**.
12
+
13
+ **Entrada:** una descripción del trabajo a hacer.
14
+
15
+ ---
16
+
17
+ ## 1. Preflight
18
+
19
+ ```bash
20
+ test -f .claude/.build-harness-version || echo "NOT_INSTALLED"
21
+ ```
22
+
23
+ Si `NOT_INSTALLED` → ejecuta `trycore-build init` y vuelve.
24
+
25
+ ---
26
+
27
+ ## 2. Clasificar (decision gate)
28
+
29
+ Aplica las reglas en orden:
30
+
31
+ 1. **¿Mantenimiento sin capacidad nueva?** — typo, ajuste de copy/config/docs, bump de dependencia **ya
32
+ permitida**, o fix de **pocas líneas** sin nueva capacidad **Y** sin ninguno de los límites duros del
33
+ paso 3 → carril **`building-a-micro-change`** (`fix/*`|`chore/*` → PR, **sin** abrir `active_slice`).
34
+ 2. **¿Producto nuevo / una épica?** — capacidad nueva, o cualquier límite duro cruzado → carril
35
+ **`building-a-slice`** (una épica `EP-XXX` = un slice = un change = una rama = un PR). Si no hay épica aún,
36
+ el trabajo vuelve a discovery para crearla.
37
+ 3. **¿Toca correr el Release Gate?** — la épica recién archivada **cierra una línea de release** del Story Map,
38
+ o hay **≥ 2 épicas archivadas** desde el último entry de `releases[]` → sugiere el carril
39
+ **`releasing-a-version`** (outer loop). (El destino es la skill existente; este router no depende de
40
+ `/build:release`.)
41
+
42
+ ---
43
+
44
+ ## 3. Límites duros del micro-change (escalan a épica)
45
+
46
+ Si el cambio **añade una dependencia nueva**, **crea un endpoint/API nuevo**, o **toca lógica de dominio o el
47
+ modelo/invariantes de datos** → **deja de ser micro-change** y se enruta a **`building-a-slice`** (épica).
48
+ **Ante la duda, SIEMPRE épica.**
49
+
50
+ ---
51
+
52
+ ## 4. Actuar (delegar) — con degradación headless
53
+
54
+ - **Con TTY**: confirma la clasificación con **una sola** `AskUserQuestion` (ofrece el carril propuesto como
55
+ primera opción "(Recomendado)") y luego invoca la skill elegida.
56
+ - **Sin TTY / headless / entrada ausente**: **no bloquees**. Clasifica determinísticamente y emite por stdout
57
+ `{clasificación, skill recomendada, criterio que disparó la rama}`. Si es **ambiguo**, aplica el **default
58
+ duro**: escalar a épica → `building-a-slice`, declarándolo explícitamente.
59
+
60
+ ---
61
+
62
+ ## Guardrails
63
+
64
+ - **Solo ruteo.** No corres el pipeline, no tocas el estado, no creas ramas: eso es de las skills destino.
65
+ - **Ante la duda, épica.** Nunca degrades un cambio con límite duro a micro-change.
66
+ - **No dupliques gates** ni saltes el orden: las skills destino los gobiernan.
67
+ - **Agnóstico**: vocabulario genérico del arnés; lo específico del dominio entra por `/build:onboard`.
68
+ - Si algo contradice `METODOLOGIA.md`, **gana la metodología**.
package/docs/agents.md CHANGED
@@ -1,13 +1,14 @@
1
1
  # Agentes de construcción (`agents/build/`)
2
2
 
3
- Los **11 agentes** de `@trycore/spec-build-harness` ejecutan los *gates* del arnés de
3
+ Los **12 agentes** de `@trycore/spec-build-harness` ejecutan los *gates* del arnés de
4
4
  construcción de dos loops. Ninguno edita código de producto: son read-only sobre el
5
5
  repositorio (algunos ejecutan tests o levantan la app), diagnostican y **devuelven el
6
6
  veredicto al `build-orchestrator`**, que es quien propone la escritura del estado
7
7
  (`.claude/state/build-state.json`).
8
8
 
9
9
  > **Cadencia.** El `build-orchestrator` y `dor-dod-gatekeeper`, junto con
10
- > `change-epic-coherence`, `api-contract-tester` y `data-consistency-checker`, corren en el
10
+ > `change-epic-coherence`, `api-contract-tester`, `data-consistency-checker`,
11
+ > `ux-fidelity-reviewer` y `wiring-adversarial-verifier`, corren en el
11
12
  > **inner loop** (skill `building-a-slice`, por épica `EP-XXX`). Los 5 revisores pesados de
12
13
  > release (`security-reviewer`, `simple-design-reviewer`, `ux-krug-reviewer`,
13
14
  > `coherence-three-way`, `stack-guardian`) corren **una vez por release** en el **outer loop**
@@ -27,7 +28,8 @@ veredicto al `build-orchestrator`**, que es quien propone la escritura del estad
27
28
  | 8 | `api-contract-tester` | sonnet | Contrato / datos | Gate `api` — pruebas de contrato (Newman/Postman) |
28
29
  | 9 | `data-consistency-checker` | sonnet | Contrato / datos | Gate `data` — invariantes y consistencia de datos |
29
30
  | 10 | `change-epic-coherence` | sonnet | Trazabilidad | Gate `coherence_link` — enlace change↔épica↔HU |
30
- | 11 | `ux-fidelity-reviewer` | sonnet | Inner loop · smoke | Gate `fidelity` — fidelidad visual a la fuente de diseño declarada |
31
+ | 11 | `ux-fidelity-reviewer` | sonnet | Inner loop · smoke | Gate `fidelity` — fidelidad visual a la fuente de diseño declarada (verificación visual real, MCP) |
32
+ | 12 | `wiring-adversarial-verifier` | **opus** | Inner loop · dod | Gate `wiring_verified` — verificación adversarial independiente del cableado (refuta antes de cerrar `dod`) |
31
33
 
32
34
  ---
33
35
 
@@ -132,3 +134,24 @@ exista en `docs/04-historias/` con `epica:` coincidente, la coherencia de alcanc
132
134
  `openspec validate <name> --type change --strict` pase; sugiere back-references. COHERENTE →
133
135
  `gates.coherence_link: true`. Complementa a `coherence-three-way` validando el **enlace**
134
136
  (este último valida la implementación real).
137
+
138
+ ## Inner loop · fidelidad y cableado
139
+
140
+ ### `ux-fidelity-reviewer` · modelo `sonnet` · lee el dominio
141
+ **Revisor de fidelidad visual** (gate `fidelity`), en la fase `smoke`. Comprueba que la pantalla
142
+ construida reproduce **composición, layout, paleta y tipografía** de la fuente de diseño declarada
143
+ (`DESIGN_SOURCE`). **Verificación visual real, requerida para UI**: con la app corriendo usa un MCP de
144
+ devtools de navegador (chrome-devtools) — `take_screenshot` app vs prototipo + `take_snapshot` de
145
+ estructura. Sin MCP el veredicto es INCONCLUSO, que para UI mapea a `gates.fidelity: false` (bloquea el
146
+ `dod`): hay que correr el slice donde el MCP esté disponible. No juzga usabilidad (eso es
147
+ `ux-krug-reviewer`, en el outer loop).
148
+
149
+ ### `wiring-adversarial-verifier` · modelo `opus` · contexto virgen
150
+ **Verificador adversarial del cableado** (gate `wiring_verified`), al inicio de la fase `dod`. Llega
151
+ con contexto virgen e **independiente** del que construyó: su sesgo por defecto es "está incompleto" y
152
+ su trabajo es **refutar** el slice — cazar stubs, rutas sin cablear (endpoint sin invocar, cola sin
153
+ consumidor, componente sin enrutar), AC sin test real, puntos de integración entre capas no recorridos,
154
+ e items de `wiring_checklist[]` aún `failing` o marcados `passing` sin `evidence`. CABLEADO COMPLETO →
155
+ `gates.wiring_verified: true` (habilita `dod`); HUECOS → `false` (retrocede `phase`). Rompe la
156
+ auto-confirmación del cierre prematuro: el DoD declarativo del `dor-dod-gatekeeper` es un piso, este
157
+ agente es el arreglo.