@trycore/spec-build-harness 0.6.0 → 0.7.1

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 (47) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/GOVERNANCE.md +26 -3
  3. package/INSTALL.md +7 -7
  4. package/METODOLOGIA.md +22 -3
  5. package/README.md +8 -4
  6. package/VERSION +1 -1
  7. package/agents/build/api-contract-tester.md +8 -0
  8. package/agents/build/build-orchestrator.md +8 -2
  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/security-reviewer.md +11 -3
  13. package/agents/build/simple-design-reviewer.md +4 -3
  14. package/agents/build/stack-guardian.md +12 -4
  15. package/agents/build/ux-krug-reviewer.md +12 -3
  16. package/agents/build/wiring-adversarial-verifier.md +14 -7
  17. package/commands/build/onboard.md +18 -1
  18. package/commands/build/reflect.md +32 -8
  19. package/commands/build/release.md +84 -0
  20. package/commands/build/slice.md +93 -0
  21. package/commands/build/work.md +68 -0
  22. package/dist/lib/settings-merge.js +1 -1
  23. package/docs/agents.md +20 -13
  24. package/docs/commands.md +22 -4
  25. package/docs/customization/mcp-extensions.md +5 -4
  26. package/docs/getting-started.md +6 -5
  27. package/docs/hooks.md +13 -7
  28. package/hooks/build/build-gate-check.sh +3 -1
  29. package/hooks/build/load-build-state.sh +27 -5
  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 +21 -0
  36. package/skills/building-a-slice/references/dod.md +3 -1
  37. package/skills/building-a-slice/references/exploration-fanout.md +36 -0
  38. package/skills/building-a-slice/references/state-protocol.md +5 -0
  39. package/skills/building-a-slice/workflows/README.md +25 -0
  40. package/skills/building-a-slice/workflows/explore-fanout.workflow.js +77 -0
  41. package/skills/building-a-slice/workflows/wiring-verify.workflow.js +88 -0
  42. package/skills/releasing-a-version/SKILL.md +21 -0
  43. package/skills/releasing-a-version/references/release-dod.md +2 -1
  44. package/skills/releasing-a-version/workflows/README.md +19 -0
  45. package/skills/releasing-a-version/workflows/release-gate.workflow.js +104 -0
  46. package/templates/CLAUDE.md.template +6 -2
  47. package/templates/settings-hooks.template.json +1 -1
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "trycore-spec-build-harness",
4
4
  "displayName": "Trycore — Spec & Build Harness",
5
- "version": "0.6.0",
5
+ "version": "0.7.1",
6
6
  "description": "Arnés de construcción de dos loops (slice por épica + release gate) para Claude Code, con gates de calidad, estado compartido y OpenSpec. Compañero de @trycore/spec-product-flow. Agnóstico al proyecto.",
7
7
  "author": {
8
8
  "name": "Trycore",
package/GOVERNANCE.md CHANGED
@@ -9,8 +9,9 @@ evoluciona con el modelo y el proyecto. No es "instalar y olvidar".
9
9
  | Contexto | sección Construcción de CLAUDE.md, `openspec/project.md` | raíz / `openspec/` |
10
10
  | Estado | `build-state.json` (+schema, README) | `.claude/state/` |
11
11
  | Agentes | 12 agentes de build | `.claude/agents/build/` |
12
- | Hooks | settings.json + 9 scripts | `.claude/settings.json`, `.claude/hooks/build/` |
13
- | Skill | `building-a-slice` (+10 refs) · `building-a-micro-change` (carril ligero de mantenimiento) | `.claude/skills/` |
12
+ | Hooks | settings.json + 10 scripts | `.claude/settings.json`, `.claude/hooks/build/` |
13
+ | Skill | `building-a-slice` (+11 refs · `workflows/`) · `releasing-a-version` (`workflows/`) · `building-a-micro-change` (carril ligero de mantenimiento) | `.claude/skills/` |
14
+ | Comandos | `/opsx:*` · `/build:onboard` · `/build:reflect` · `/build:slice` · `/build:release` · `/build:work` | `.claude/commands/` |
14
15
  | Config | allowlist de stack | `.claude/config/stack-allowlist.json` |
15
16
 
16
17
  ## Fases de activación (`harness_phase`)
@@ -28,7 +29,8 @@ evoluciona con el modelo y el proyecto. No es "instalar y olvidar".
28
29
  hook o un agente? Si sí, retirarlo.
29
30
  3. **Allowlist vs PRD**: re-sincronizar `stack-allowlist.json` con la sección de requisitos técnicos del PRD del consumidor (ruta declarada en `stack-allowlist.json#source`) si el stack cambió.
30
31
  4. **Coherencia de convenciones**: que `build-state.schema.json`, los agentes y la skill sigan
31
- alineados (mismos nombres de gate/fase).
32
+ alineados (mismos nombres de gate/fase). Las plantillas `*.workflow.js` de `skills/*/workflows/`
33
+ entran al **mismo barrido agnóstico** que `agents/` y `skills/` (`check-agnostic.sh` incluye `*.js`).
32
34
 
33
35
  ## DRI (Directly Responsible Individual)
34
36
  Un **Agent Manager** (rol híbrido PM + DevEx) centraliza qué funciona y evita la fragmentación de
@@ -44,6 +46,27 @@ Editar `stack-allowlist.json` SOLO si la sección de requisitos técnicos del PR
44
46
  ## Bitácora de cambios de metodología
45
47
  Cambios a la política de construcción (unidad de trabajo, gates, DoR/DoD). Aprueba el DRI; van por PR.
46
48
 
49
+ - **2026-06-24 · v0.7.0** — **Workflows dinámicos en el flujo build-a-slice** (origen:
50
+ evaluación adversarial del arnés con workflows dinámicos; 18 mejoras aprobadas contra una rúbrica de
51
+ hardness). **Aprobado por el DRI (Agent Manager); fusionado en PR #7.** No cambia la unidad de trabajo, los gates,
52
+ el DoR ni el DoD — introduce **mecanismos de ejecución** opcionales y endurece componentes existentes.
53
+ Cambios que tocan la metodología: (1) **§1-bis.2** — la exploración solo-lectura puede conducirse con un
54
+ **workflow fan-out** opcional, gated por el size-gate (`sub_slices[]`); read-only, no escribe estado.
55
+ (2) **§1-bis.3** — **plantilla de conducción** opcional del verificador adversarial del gate
56
+ `wiring_verified`; no es gate nuevo y no escribe estado (`build-orchestrator` sigue siendo single-writer).
57
+ (3) **§4** — el nudge "≥2 épicas archivadas desde el último `releases[]`" también lo emite un **hook Stop
58
+ determinista** (`release-gate-nudge.sh`) por aritmética de conjuntos sobre `epicas[]`. (4) **§0** — nuevo
59
+ router de entrada `/build:work` (classify-and-act) que codifica el ruteo micro-change/slice/release (ruteo,
60
+ no política nueva). Hardening sin cambio de política: corrección de la **deriva de nombres de gate** de los 5
61
+ reviewers pesados (`releases[].gates.{security,smell,ux,coherence,stack_arch}`; `stack`→`stack_arch`) y del
62
+ gate de inner loop `coherence_link`; **contrato de degradación segura** de reviewers (fallo de herramienta →
63
+ bloqueante, nunca PASS); refuerzo del `wiring-adversarial-verifier` (evidencia ejecutada, no por inspección);
64
+ **escritura atómica** del estado en `load-build-state.sh`; cierre del **bypass de specs no-semver** en
65
+ `stack-guard.sh`; `check-agnostic.sh` ahora barre `*.js`/`*.mjs`. Artefactos nuevos: **3 comandos**
66
+ (`/build:slice`, `/build:release`, `/build:work`), **1 hook** (`release-gate-nudge.sh`, total **10**),
67
+ **3 plantillas `*.workflow.js`** (read-only) + 1 reference. Empaquetado en el **release 0.7.0** (bump
68
+ simultáneo de `VERSION`, `package.json` y `plugin.json`, por la regla de version-sync).
69
+
47
70
  - **2026-06-19 · v0.6.0** — **Calidad de cierre contra horizonte largo** (origen: feedback del equipo
48
71
  exodocs tras varios releases, revisado por un experto de Anthropic; raíz: agotamiento de contexto +
49
72
  cierre prematuro en épicas multicapa). Toca gates/DoR/DoD y el schema de estado → aprobado por el DRI
package/INSTALL.md CHANGED
@@ -11,7 +11,7 @@ Es el **compañero** de [`@trycore/spec-product-flow`](https://www.npmjs.com/pac
11
11
  | CLI (bin) | `trycore-build` |
12
12
  | Plugin | `trycore-spec-build-harness` |
13
13
  | Marketplace | `trycore-build` |
14
- | Versión | `0.5.0` |
14
+ | Versión | `0.7.0` |
15
15
 
16
16
  > **¿Solo quieres empezar ya?** El [Quickstart](docs/getting-started.md) te lleva de 0 a tu primer slice en pocos comandos. Esta guía es la **referencia detallada** (flags, CI, plugin, troubleshooting).
17
17
 
@@ -30,7 +30,7 @@ npm install -g @trycore/spec-build-harness
30
30
  Esto expone el binario `trycore-build`. Comprueba la versión:
31
31
 
32
32
  ```bash
33
- trycore-build --version # → 0.5.0
33
+ trycore-build --version # → 0.7.0
34
34
  trycore-build --help
35
35
  ```
36
36
 
@@ -87,9 +87,9 @@ Qué hace `init`:
87
87
  1. **Verifica requisitos duros** (a menos que uses `--skip-doctor`).
88
88
  2. **Siembra los assets** en rutas nativas de Claude Code:
89
89
  - `.claude/agents/build/` — 12 agentes.
90
- - `.claude/commands/opsx/` (10 comandos `/opsx:*`) y `.claude/commands/build/` (`/build:onboard`, `/build:reflect`).
91
- - `.claude/skills/` — 12 skills (`building-a-slice`, `releasing-a-version`, `openspec-*`).
92
- - `.claude/hooks/build/` — 9 hooks bash.
90
+ - `.claude/commands/opsx/` (10 comandos `/opsx:*`) y `.claude/commands/build/` (5 comandos: `/build:onboard`, `/build:reflect`, `/build:slice`, `/build:release`, `/build:work`).
91
+ - `.claude/skills/` — 13 skills (`building-a-slice`, `building-a-micro-change`, `releasing-a-version`, `openspec-*`).
92
+ - `.claude/hooks/build/` — 10 hooks bash.
93
93
  3. **Siembra el estado**: `state/build-state.schema.json` y `state/README.md` se versionan;
94
94
  `state/build-state.json` se siembra **vacío y nunca se sobrescribe** (va al `.gitignore`).
95
95
  4. **Siembra `config/stack-allowlist.json`** (artefacto del consumidor; lo puebla `/build:onboard`).
@@ -205,7 +205,7 @@ Como conveniencia a nivel usuario, el arnés también se instala como **plugin n
205
205
  ### ⚠ Caveat de canales (importante)
206
206
 
207
207
  - El **canal npm CLI es el CANÓNICO**. `trycore-build init` instala los comandos en
208
- `.claude/commands/{opsx,build}/`, que namespacean **por subcarpeta** → `/opsx:*` y `/build:onboard`,
208
+ `.claude/commands/{opsx,build}/`, que namespacean **por subcarpeta** → `/opsx:*` y `/build:*`,
209
209
  y los agentes se referencian por su **nombre** (`security-reviewer`, `stack-guardian`, …).
210
210
  - El **canal plugin nativo** namespacea **todos** los componentes bajo el **nombre del plugin**
211
211
  (→ `/trycore-spec-build-harness:*`), por diseño de Claude Code.
@@ -235,7 +235,7 @@ Ambos paquetes coexisten en el mismo `.claude/` **sin colisión**, porque usan *
235
235
 
236
236
  | | Discovery — `spec-product-flow` | Construcción — `spec-build-harness` |
237
237
  |---|---|---|
238
- | Comandos | `/trycore:*` | `/opsx:*` + `/build:onboard` + `/build:reflect` |
238
+ | Comandos | `/trycore:*` | `/opsx:*` + `/build:*` (`onboard`, `reflect`, `slice`, `release`, `work`) |
239
239
  | Marca de versión | `.trycore-version` | `.build-harness-version` |
240
240
  | Bloque en `CLAUDE.md` | `<!-- BEGIN trycore-vertical -->` | `<!-- BEGIN trycore-build-harness -->` |
241
241
 
package/METODOLOGIA.md CHANGED
@@ -28,7 +28,7 @@ Ambos paquetes **coexisten en el mismo `.claude/` sin colisión**, con namespace
28
28
 
29
29
  | Eje | Discovery (`spec-product-flow`) | Construcción (`spec-build-harness`) |
30
30
  |---|---|---|
31
- | Comandos | `trycore/` (`/trycore:*`) | `opsx/` + `build/` (`/opsx:*`, `/build:onboard`, `/build:reflect`) |
31
+ | Comandos | `trycore/` (`/trycore:*`) | `opsx/` + `build/` (`/opsx:*`, `/build:work`, `/build:slice`, `/build:release`, `/build:onboard`, `/build:reflect`) |
32
32
  | Sentinela de versión | `.trycore-version` | `.build-harness-version` |
33
33
  | Bloque en `CLAUDE.md` | `<!-- BEGIN trycore-vertical -->` | `<!-- BEGIN trycore-build-harness -->` |
34
34
 
@@ -80,12 +80,23 @@ sola pasada**: a medida que crece el contexto, la atención se degrada ("context
80
80
  supera el **gate de tamaño** (>3 HU ó ≥3 capas) se trocea en `sub_slices[]` construidos de a uno. El
81
81
  orquestador trabaja por **fases encadenadas** (mapear → generar → revisar → fix-loop → optimizar) y
82
82
  reparte la **exploración** "ancho antes que profundo" con subagentes **solo-lectura** por área
83
- (devuelven síntesis condensada); el **cableado** lo hace la sesión, no subagentes en paralelo.
83
+ (devuelven síntesis condensada); el **cableado** lo hace la sesión, no subagentes en paralelo. Ese
84
+ reparto de exploración solo-lectura **puede** conducirse, de forma OPCIONAL, con un **workflow fan-out**
85
+ (`parallel()`) **SOLO** para épicas que superaron el gate de tamaño (con `sub_slices[]`); en épicas
86
+ atómicas se hace secuencial en sesión. El workflow es **solo-lectura**: no escribe código de producto ni
87
+ `build-state.json` (plantilla en `skills/building-a-slice/workflows/explore-fanout.workflow.js`).
84
88
  3. **Verificación adversarial independiente (no DoD declarativo).** Reusar el mismo agente como generador
85
89
  y verificador produce **auto-confirmación**. Por eso el gate **`wiring_verified`** lo cierra un
86
90
  subagente **independiente, de contexto virgen** (`wiring-adversarial-verifier`) cuyo trabajo es
87
91
  **asumir que el slice está incompleto y refutarlo** (stubs, rutas sin cablear, AC sin test, items
88
92
  `failing`) **antes** de permitir `dod`. El DoD declarativo del gatekeeper es un **piso, no el arreglo**.
93
+ Esta verificación adversarial **puede** conducirse, de forma OPCIONAL, con una **plantilla de conducción**
94
+ (`skills/building-a-slice/workflows/wiring-verify.workflow.js`) que envuelve al verificador ya existente: (a)
95
+ **NO** es un gate nuevo ni cambia política; (b) **NO** escribe estado — el verificador sigue read-only y
96
+ `build-orchestrator` sigue siendo el **único** escritor de `gates.wiring_verified`; (c) equivale a la prosa
97
+ actual, solo la hace reproducible. Los workflows quedan **acotados a tres hogares** (fan-out de exploración,
98
+ conducción del verificador adversarial, y el Release Gate del §5) y **prohibidos** en el camino caliente de
99
+ las fases del inner loop.
89
100
  4. **Producto completo, no MVP (anti-deriva).** El alcance acordado se construye **entero**. **Recortar o
90
101
  diferir es bloqueante explícito** que exige acuerdo del equipo — **nunca** una decisión del modelo. No
91
102
  se "deja para después" ni se deriva en lo complejo. La verificación es **ejecutada, no por inspección**
@@ -128,7 +139,9 @@ se listan en `active_slice.hus[]`. Construir por HU individual es sobre-ingenier
128
139
  > PR, sin abrir `active_slice`). No es una excepción a la regla, sino mantenimiento fuera de su
129
140
  > alcance. **Límites duros:** si el cambio añade una dependencia nueva, crea un endpoint/API nuevo, o
130
141
  > toca lógica de dominio o el modelo/invariantes de datos, **deja de ser micro-change** y se escala a
131
- > épica. Ante la duda, es una épica.
142
+ > épica. Ante la duda, es una épica. El comando `/build:work` codifica este *decision gate* (y el default
143
+ > del Release Gate del §4) como **ruteo** —no política nueva—: enruta a `building-a-micro-change`,
144
+ > `building-a-slice` o `releasing-a-version` según el cambio; ante ambigüedad, escala a épica.
132
145
 
133
146
  ---
134
147
 
@@ -229,6 +242,12 @@ Tras archivar la épica, `building-a-slice` **pregunta al humano** si correr el
229
242
  El humano siempre puede sobreescribir el default. Si acepta, se invoca `releasing-a-version` sobre la
230
243
  release correspondiente.
231
244
 
245
+ > El *nudge* "≥ 2 épicas archivadas desde el último entry de `releases[]`" también lo emite un **hook
246
+ > `Stop` determinista** (`release-gate-nudge.sh`), computado por **aritmética de conjuntos** sobre
247
+ > `epicas[]` (archivadas − cubiertas), independiente de timestamps; **solo sugiere** (no ejecuta nada, no
248
+ > llama al modelo, no escribe estado). El criterio "cierra una línea de release" requiere
249
+ > `docs/02-user-story-map/` y por eso **no** lo cubre el hook: lo computa la skill `building-a-slice`.
250
+
232
251
  ---
233
252
 
234
253
  ## 5. Los gates del Release Gate (outer loop)
package/README.md CHANGED
@@ -73,6 +73,9 @@ Gate de release que corre **una sola vez por versión** sobre el conjunto de sli
73
73
  |---|---|
74
74
  | `/build:onboard` | Onboarding capa 2: lee el PRD, pregunta por PII/IA/determinismo/secretos, resuelve `{{placeholders}}` y escribe la auto-memory. |
75
75
  | `/build:reflect` | Reflexión post-slice: tras archivar, propone convenciones aprendidas / errores recurrentes al bloque `trycore-build-learnings` de `CLAUDE.md` (tras tu aprobación) y marca el slice como reflexionado. |
76
+ | `/build:slice` | 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. Adaptador delgado que delega en la skill `building-a-slice`. |
77
+ | `/build:release` | Entrada del **outer loop**: corre el Release Gate **una sola vez** sobre el diff acumulado (los 5 reviewers pesados en paralelo + integración secuencial). Delega en la skill `releasing-a-version`. |
78
+ | `/build:work` | Router *classify-and-act*: clasifica el trabajo entrante y enruta al carril correcto (`building-a-micro-change` · `building-a-slice` · `releasing-a-version`). Es ruteo, no política: no ejecuta el pipeline ni toca el estado. |
76
79
  | `/opsx:*` (10) | Ciclo OpenSpec: `explore` · `new` · `continue` · `apply` · `verify` · `archive` · `bulk-archive` · `ff` · `onboard` · `sync`. Detalle → [`docs/commands.md`](docs/commands.md). |
77
80
 
78
81
  ## Arquitectura
@@ -85,9 +88,9 @@ trycore-spec-build-harness/
85
88
  ├── agents/build/ ← 12 agentes revisores (segunda opinión, contexto limpio)
86
89
  ├── commands/
87
90
  │ ├── opsx/ ← 10 comandos /opsx:* (ciclo OpenSpec)
88
- │ └── build/ ← /build:onboard, /build:reflect
89
- ├── skills/ ← 12 skills (building-a-slice, releasing-a-version, 10 openspec-*)
90
- ├── hooks/build/ ← 9 hooks bash (gate-check, reflect-nudge, scaffold-guard, gitflow-guard, stack-guard, …)
91
+ │ └── build/ ← 5 comandos /build:* (onboard, reflect, slice, release, work)
92
+ ├── skills/ ← 13 skills (building-a-slice, building-a-micro-change, releasing-a-version, 10 openspec-*) + 3 plantillas *.workflow.js (opt-in, read-only)
93
+ ├── hooks/build/ ← 10 hooks bash (gate-check, reflect-nudge, release-gate-nudge, scaffold-guard, gitflow-guard, stack-guard, …)
91
94
  ├── state/ ← máquina de estado: build-state.json + schema + README
92
95
  ├── config/ ← stack-allowlist.template.json (artefacto del consumidor)
93
96
  ├── src/ + dist/ ← CLI trycore-build (init/update/status/uninstall/doctor)
@@ -122,7 +125,8 @@ El core no menciona ningún dominio de cliente. Toda parametrización entra por
122
125
  - ✅ **v0.3.0** — **ciclo autocorrectivo** (hook `reflect-nudge.sh` + comando `/build:reflect`: propone convenciones aprendidas al bloque `trycore-build-learnings` de `CLAUDE.md` tras tu aprobación; campos `reflected`/`reflected_at`) y **LSP opt-in** (`docs/customization/lsp-extensions.md` + sugerencia en `doctor` para stacks tipados). Total: **8 hooks**; comandos `/opsx:*` + `/build:onboard` + `/build:reflect`.
123
126
  - ✅ **v0.4.0** — carril `building-a-micro-change` (mantenimiento ligero sin slice) + DoR proporcional a la complejidad.
124
127
  - ✅ **v0.5.0** — seguro de fuente de diseño (`design_source` + `design-source-guard.sh`) + agente `ux-fidelity-reviewer` (gate `fidelity`, inner loop). Total: **11 agentes**, **9 hooks**.
125
- - ✅ **v0.6.0 (actual)** — **calidad de cierre contra horizonte largo** (feedback exodocs): handoff fino en disco (`wiring_checklist[]` + `progress_log[]` + `sub_slices[]`), gate `wiring_verified` por nuevo agente **`wiring-adversarial-verifier`** (verificación adversarial independiente, contexto virgen), cimiento pre-construido + tag `layer` y gate de tamaño en el DoR, runner fuera-de-chat `integration-check`, y **fidelidad estricta por verificación visual real** (MCP requerido para UI). Convenciones anti-deriva (producto completo, no MVP) upstreadas al bloque del arnés. Total: **12 agentes**, **9 hooks**.
128
+ - ✅ **v0.6.0** — **calidad de cierre contra horizonte largo** (feedback exodocs): handoff fino en disco (`wiring_checklist[]` + `progress_log[]` + `sub_slices[]`), gate `wiring_verified` por nuevo agente **`wiring-adversarial-verifier`** (verificación adversarial independiente, contexto virgen), cimiento pre-construido + tag `layer` y gate de tamaño en el DoR, runner fuera-de-chat `integration-check`, y **fidelidad estricta por verificación visual real** (MCP requerido para UI). Convenciones anti-deriva (producto completo, no MVP) upstreadas al bloque del arnés. Total: **12 agentes**, **9 hooks**.
129
+ - ✅ **v0.7.0 (actual)** — **orquestación con workflows dinámicos + hardening** (de una evaluación adversarial del propio arnés): **3 plantillas `*.workflow.js`** opt-in y read-only (`explore-fanout`, `wiring-verify`, `release-gate`) que entran **solo donde aportan valor** y nunca en el camino caliente del inner loop; **3 comandos nuevos** `/build:slice` (entrada del inner loop), `/build:release` (outer loop) y `/build:work` (router *classify-and-act*); hook **`release-gate-nudge.sh`** (Stop, determinista: solo sugiere el Release Gate). Rename de los gates de los 5 reviewers pesados → `releases[].gates.{security,smell,ux,coherence,stack_arch}` (`stack`→`stack_arch`; separación `coherence` (release) / `coherence_link` (inner)). Hardening: degradación segura en 8 agentes, escritura atómica del estado, cierre del bypass de specs no-semver, `wiring` exige evidencia ejecutada y `check-agnostic` barre `*.js`. Total: **12 agentes**, **10 hooks**, **5 comandos `/build:*`**.
126
130
 
127
131
  ## Licencia
128
132
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.1
@@ -29,4 +29,12 @@ Eres el **tester de contratos de API** del arnés de construcción. Si el slice
29
29
  - Resumen de ejecución (totales, fallos con request + aserción).
30
30
  - Veredicto: 100% verde → propón `gates.api: true`; fallos → `false` con el detalle; sin endpoints → `null`.
31
31
 
32
+ ## Degradación segura
33
+ Si **no puedes completar tu verificación** (la app no levanta, `newman`/`npx` ausente, la colección no se puede
34
+ crear, readiness no llega), **NO devuelvas PASS ni inventes**: devuelve veredicto **BLOQUEANTE / INCONCLUSO** con
35
+ el motivo y qué falta para correr. *La ausencia de evidencia no es evidencia de ausencia de problemas.* Distingue
36
+ —como `ux-fidelity-reviewer` (INCONCLUSO ≠ N/A)— el **N/A legítimo** (slice **sin endpoints** → `gates.api: null`)
37
+ de **"no pude verificar"** (fallo de herramienta → `false`). Reserva el `null` SOLO para el N/A genuino (sin
38
+ endpoints), nunca para un fallo de herramienta.
39
+
32
40
  No edites código de producto: si faltan casos, propón los requests a añadir. Devuelve al `build-orchestrator`.
@@ -24,7 +24,10 @@ escriben en paralelo.
24
24
 
25
25
  **Descomposición (gate de tamaño).** Si la épica supera el umbral (>3 HU ó ≥3 capas; configurable),
26
26
  el DoR la trocea en `sub_slices[]`: constrúyelos **de a uno**, con `journey_smoke` verde entre cada
27
- uno, marcando `sub_slices[].status: done` al cerrar cada uno.
27
+ uno, marcando `sub_slices[].status: done` al cerrar cada uno. Para épicas troceadas, la exploración
28
+ solo-lectura por área **puede** conducirse con la plantilla (opcional) `skills/building-a-slice/workflows/
29
+ explore-fanout.workflow.js` (gated por la guarda del propio workflow: `sub_slices[]` no vacío). **No** la uses
30
+ en épicas atómicas: inflaría el inner loop barato. Es read-only; el cableado sigue siendo de la sesión.
28
31
 
29
32
  **Refresh de contexto por defecto.** Mantén el handoff fino en disco. **Siémbralo al entrar a `change`/`tdd`**:
30
33
  deriva de las HU de `hus[]` un item de `wiring_checklist[]` por **cada escenario AC** y uno por **cada
@@ -57,7 +60,10 @@ El `ux-fidelity-reviewer` **sí** corre aquí (en `smoke`): es barato (la app ya
57
60
  por-slice; no es la revisión pesada de UX/Krug (esa sigue en `releasing-a-version`). El
58
61
  `wiring-adversarial-verifier` **también** corre aquí (al inicio de `dod`): es un verificador
59
62
  **enfocado y corto** (solo refuta cableado/AC/stubs, no re-revisa diseño/seguridad), e **independiente**
60
- del que generó el código — por eso evita la auto-confirmación del cierre prematuro.
63
+ del que generó el código — por eso evita la auto-confirmación del cierre prematuro. Opcionalmente esa
64
+ verificación se conduce con la plantilla read-only `skills/building-a-slice/workflows/wiring-verify.workflow.js`
65
+ (envuelve al verificador); **tú** —`build-orchestrator`— sigues siendo quien escribe `gates.wiring_verified`
66
+ a partir del veredicto que la plantilla devuelve (la plantilla no toca el estado).
61
67
 
62
68
  ## Reglas de orquestación
63
69
  - **No saltes gates.** No avances de fase si el gate previo está en `false`. Reporta qué falta.
@@ -35,7 +35,16 @@ que aparezca un OpenSpec change "huérfano" desconectado de la discovery de Tryc
35
35
 
36
36
  ## Salida
37
37
  - Veredicto: **COHERENTE** / **INCOHERENTE**, con lista ✓/✗ y citas textuales (archivo:línea).
38
- - Si COHERENTE: indica que se puede marcar `gates.coherence: true` para la fase `change`.
38
+ - Si COHERENTE: indica que se puede marcar `gates.coherence_link: true` (gate del **inner loop**, fase
39
+ `change`). NO es el `coherence` pesado del outer loop (ese lo cierra `coherence-three-way` en
40
+ `releasing-a-version` sobre la implementación real): son gates distintos del schema — `coherence_link`
41
+ (inner, barato, enlace change↔épica) vs `coherence` (release, trazabilidad triple completa).
39
42
  - Si INCOHERENTE: por cada ✗, propone el fix concreto (texto exacto a añadir/corregir).
40
43
 
41
- No edites archivos: devuelve el diagnóstico al `build-orchestrator`.
44
+ ## Degradación segura
45
+ Si **no puedes completar tu verificación** (`openspec validate` no corre, no puedes leer el `proposal.md`/las
46
+ HU/la épica), **NO devuelvas COHERENTE ni inventes**: devuelve **INCOHERENTE / INCONCLUSO** con el motivo y qué
47
+ falta para correr. *La ausencia de evidencia no es evidencia de ausencia de problemas.* Un fallo de herramienta
48
+ **no es N/A**: nunca devuelvas `null` por no poder verificar — devuelve bloqueante/`false`.
49
+
50
+ No edites archivos: devuelve el diagnóstico al `build-orchestrator` (gate de inner loop `coherence_link`).
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: coherence-three-way
3
- description: Verifica la coherencia triple AC de las HU de la épica (Given/When/Then) ↔ OpenSpec change(specs/tasks) ↔ código/tests implementados. Detecta AC sin test, tasks sin AC, y código que no traza a ninguna HU. Úsalo en la fase verify, antes del DoD.
3
+ description: Verifica la coherencia triple AC de las HU de la épica (Given/When/Then) ↔ OpenSpec change(specs/tasks) ↔ código/tests implementados. Detecta AC sin test, tasks sin AC, y código que no traza a ninguna HU. Úsalo en el Release Gate (releasing-a-version), sobre el diff acumulado de la release.
4
4
  tools: Read, Grep, Glob, Bash
5
5
  model: opus
6
6
  ---
@@ -29,7 +29,15 @@ construcción: nada implementado sin razón, nada especificado sin implementar.
29
29
  ## Salida
30
30
  - Matriz de trazabilidad AC ↔ escenario ↔ task ↔ test/código (tabla compacta).
31
31
  - Huérfanos top-down (AC de alguna HU sin test) y bottom-up (código sin HU), con `archivo:línea`.
32
- - Veredicto: **COHERENTE** → propone `gates.coherence: true`; o **INCOHERENTE** con fixes.
32
+ - Veredicto: **COHERENTE** → propone `releases[].gates.coherence: true`; o **INCOHERENTE** con fixes.
33
33
 
34
- Complementa a `change-epic-coherence` (que valida el enlace) verificando la **implementación real**.
35
- No edites: devuelve el diagnóstico al `build-orchestrator`.
34
+ ## Degradación segura
35
+ Si **no puedes completar tu verificación** (no puedes leer las HU/specs/código, `openspec`/LSP ausente, `diff`
36
+ vacío inesperado), **NO devuelvas COHERENTE ni inventes**: devuelve **INCOHERENTE / INCONCLUSO** con el motivo y
37
+ qué falta para correr. *La ausencia de evidencia no es evidencia de ausencia de problemas.* Un fallo de
38
+ herramienta **no es N/A**: nunca devuelvas `null` por no poder verificar — devuelve bloqueante/`false`.
39
+
40
+ Complementa a `change-epic-coherence` (que valida el enlace, gate de inner loop `coherence_link`) verificando la
41
+ **implementación real** (gate de release `coherence`). No edites: devuelve el diagnóstico a la skill
42
+ `releasing-a-version` (Release Gate, **outer loop**), que escribe `releases[].gates`. Cadencia: **una vez por
43
+ RELEASE**, no por slice.
@@ -45,4 +45,11 @@ bloque de dominio del CLAUDE.md del consumidor / del PRD del consumidor. Este ag
45
45
  - Invariantes ✓/✗ con evidencia (`archivo:línea` o salida de test).
46
46
  - Veredicto: todas ✓ → propón `gates.data: true`; alguna ✗ → `false` con el fix/test faltante.
47
47
 
48
+ ## Degradación segura
49
+ Si **no puedes completar tu verificación** (los tests no corren, runner `vitest`/`jest` ausente, no puedes leer
50
+ la capa de decisión), **NO devuelvas PASS ni inventes**: devuelve veredicto **BLOQUEANTE / INCONCLUSO** con el
51
+ motivo y qué falta para correr. *La ausencia de evidencia no es evidencia de ausencia de problemas.* Un fallo de
52
+ herramienta **no es N/A**: nunca devuelvas `null` por no poder verificar — devuelve bloqueante/`false` (el caso
53
+ N/A de `data` lo decide el `build-orchestrator` por aplicabilidad del slice, no tú por un fallo de herramienta).
54
+
48
55
  No edites: devuelve el diagnóstico al `build-orchestrator`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: security-reviewer
3
- description: Revisión de seguridad del slice con foco en el dominio declarado por el consumidor (PII/datos regulados, claves de servicios externos, validación de entrada). Envuelve la lógica de /security-review y la especializa para este proyecto. Úsalo en la fase review. Requiere código.
3
+ description: Revisión de seguridad del slice con foco en el dominio declarado por el consumidor (PII/datos regulados, claves de servicios externos, validación de entrada). Envuelve la lógica de /security-review y la especializa para este proyecto. Úsalo en el Release Gate (releasing-a-version), sobre el diff acumulado de la release. Requiere código.
4
4
  tools: Read, Grep, Glob, Bash
5
5
  model: sonnet
6
6
  ---
@@ -41,6 +41,14 @@ consumidor o de su PRD (la sección de requisitos técnicos del PRD, en la ruta
41
41
 
42
42
  ## Salida
43
43
  - Hallazgos por severidad **CRÍTICO / ALTO / MEDIO / BAJO** con `archivo:línea` y remediación.
44
- - Veredicto: sin CRÍTICO/ALTO abiertos → propón `gates.security: true`; si los hay → `false`.
44
+ - Veredicto: sin CRÍTICO/ALTO abiertos → propón `releases[].gates.security: true`; si los hay → `false`.
45
45
 
46
- No edites: devuelve el diagnóstico al `build-orchestrator`.
46
+ ## Degradación segura
47
+ Si **no puedes completar tu verificación** (herramienta/comando indisponible, app no levantable, `diff`
48
+ vacío inesperado, runner/`npm audit` ausente), **NO devuelvas PASS ni inventes**: devuelve veredicto
49
+ **BLOQUEANTE / INCONCLUSO** con el motivo y qué falta para correr. *La ausencia de evidencia no es evidencia
50
+ de ausencia de problemas.* Un fallo de herramienta **no es N/A**: nunca devuelvas `null` por no poder
51
+ verificar — devuelve bloqueante/`false`.
52
+
53
+ No edites: devuelve el diagnóstico a la skill `releasing-a-version` (Release Gate, **outer loop**), que
54
+ escribe `releases[].gates`. Cadencia: **una vez por RELEASE**, no por slice.
@@ -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.
@@ -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.
@@ -41,19 +41,26 @@ prueba es del código: ante la duda, es `failing`.
41
41
 
42
42
  ## Método
43
43
  - Traza **cada** AC y **cada** integration_point hasta el código y un test que lo ejerza de verdad.
44
- - Donde el `wiring_checklist[]` diga `passing`, **verifica la `evidence`** (corre/lee el test o el
45
- comando citado). Si no reproduce, es `failing`.
46
- - Corre la suite si hace falta (`Bash`) para confirmar que lo verde es verde 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.
47
48
 
48
49
  ## Salida + mapeo al gate
49
50
  Veredicto **CABLEADO COMPLETO** o **HUECOS** + lista priorizada de huecos con `archivo:línea`, la HU/AC o
50
51
  el par de capas afectado, y el fix mínimo. Devuelve también qué items de `wiring_checklist[]` deberían
51
- estar `failing`.
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.
52
59
 
53
60
  Mapeo que aplicará el `build-orchestrator` a `gates.wiring_verified`:
54
- - **CABLEADO COMPLETO** (ningún hueco tras intentar refutar) → `true` → habilita la fase `dod`.
55
- - **HUECOS** (≥1) → `false` → el `build-orchestrator` retrocede `phase`, marca los items afectados
56
- `failing` y **NO** se cierra `dod`.
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`.
57
64
 
58
65
  No editas el estado tú mismo: devuelves el diagnóstico al `build-orchestrator`. Si una regla aquí
59
66
  contradice `METODOLOGIA.md` (§1-bis), gana la metodología.
@@ -91,6 +91,17 @@ No inventes la clasificación: derívala del PRD/Story Map y confírmala con el
91
91
 
92
92
  Lee `CLAUDE.md`. Encuentra el bloque entre `<!-- BEGIN trycore-build-harness` y `<!-- END trycore-build-harness -->`.
93
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
+
94
105
  Reemplaza dentro del bloque los placeholders `{{PRD_TECH_PATH}}`, `{{EXTERNAL_SERVICE_LAYER}}`,
95
106
  `{{DETERMINISTIC_LAYER}}`, `{{SENSITIVE_DATA_CATEGORIES}}`, `{{SERVER_SIDE_SECRETS}}`,
96
107
  `{{HIGH_STAKES_DECISIONS}}`, `{{DESIGN_SOURCE}}` por los valores confirmados.
@@ -117,6 +128,10 @@ Espejo de la confirmación de scaffold, para `design_source` en `build-state.jso
117
128
  `confirmed_at`). El arnés **no genera** el prototipo.
118
129
  - Si **no hay UI**: setea `design_source.applies=false` (el mecanismo de fidelidad queda N/A).
119
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
+
120
135
  ---
121
136
 
122
137
  ## Fase 4: Guardar en auto-memory
@@ -154,7 +169,9 @@ CLAUDE.md actualizado. Auto-memory escrita. Los agentes de calidad ya leen tu do
154
169
 
155
170
  | Acción | Para qué |
156
171
  |---|---|
157
- | 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 |
158
175
  | `/opsx:new` | Crear un OpenSpec change |
159
176
  | `trycore-build doctor` | Verificar openspec/python3/hooks |
160
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
  ```