@trycore/spec-build-harness 0.8.4 → 0.10.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 (113) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/GOVERNANCE.md +43 -5
  3. package/INSTALL.md +28 -6
  4. package/METODOLOGIA.md +65 -10
  5. package/README.md +41 -7
  6. package/VERSION +1 -1
  7. package/agents/build/build-orchestrator.md +33 -7
  8. package/agents/build/dor-dod-gatekeeper.md +17 -6
  9. package/agents/build/ux-fidelity-reviewer.md +4 -1
  10. package/agents/build/wiring-adversarial-verifier.md +52 -5
  11. package/commands/build/architect.md +1 -1
  12. package/commands/build/claim.md +46 -0
  13. package/commands/build/escalate.md +36 -0
  14. package/commands/build/front.md +9 -3
  15. package/commands/build/onboard.md +63 -14
  16. package/commands/build/prototype.md +23 -0
  17. package/commands/build/reflect.md +60 -40
  18. package/commands/build/release.md +10 -7
  19. package/commands/build/resume.md +33 -13
  20. package/commands/build/slice.md +32 -27
  21. package/commands/build/status.md +35 -0
  22. package/commands/build/work.md +11 -8
  23. package/config/build-config.template.json +4 -0
  24. package/dist/cli.js +22 -0
  25. package/dist/commands/doctor.js +42 -0
  26. package/dist/commands/init.js +84 -1
  27. package/dist/commands/migrate.js +48 -0
  28. package/dist/commands/status.js +34 -0
  29. package/dist/lib/normalize.js +276 -0
  30. package/dist/lib/paths.js +6 -0
  31. package/dist/lib/runtime-client.js +196 -0
  32. package/dist/lib/settings-merge.js +3 -3
  33. package/dist/lib/state-bundle.js +46 -0
  34. package/docs/commands.md +32 -9
  35. package/docs/getting-started.md +2 -1
  36. package/docs/hooks.md +114 -27
  37. package/docs/runtime/guia-modo-dual-y-migracion.md +136 -0
  38. package/docs/runtime/plan-migracion-harness-v0.9.md +11 -0
  39. package/docs/runtime/protocolo-cliente-runtime.md +109 -34
  40. package/hooks/build/build-gate-check.sh +21 -0
  41. package/hooks/build/context-monitor.sh +82 -15
  42. package/hooks/build/context-sync.sh +192 -0
  43. package/hooks/build/design-source-guard.sh +31 -3
  44. package/hooks/build/dual-compare.sh +92 -0
  45. package/hooks/build/event-emitter.sh +75 -0
  46. package/hooks/build/gitflow-guard.sh +164 -14
  47. package/hooks/build/heartbeat.sh +259 -0
  48. package/hooks/build/lib/agent-context.sh +139 -0
  49. package/hooks/build/lib/config.sh +27 -0
  50. package/hooks/build/lib/projection.sh +71 -0
  51. package/hooks/build/lib/runtime-client.sh +465 -0
  52. package/hooks/build/lib/runtime-ops.sh +221 -0
  53. package/hooks/build/lib/state-io.sh +5 -18
  54. package/hooks/build/load-build-state.sh +64 -2
  55. package/hooks/build/reflect-nudge.sh +15 -0
  56. package/hooks/build/release-gate-nudge.sh +15 -0
  57. package/hooks/build/release-ops.sh +164 -0
  58. package/hooks/build/scaffold-guard.sh +29 -2
  59. package/hooks/build/session-start.sh +103 -0
  60. package/hooks/build/session-stop.sh +22 -0
  61. package/hooks/build/slice-ops.sh +877 -0
  62. package/hooks/build/stack-guard.sh +8 -0
  63. package/hooks/build/statusline-bridge.sh +24 -3
  64. package/hooks/build-harness.json +16 -0
  65. package/package.json +3 -3
  66. package/scripts/check-agnostic.sh +3 -1
  67. package/scripts/check-pack-clean.sh +31 -0
  68. package/scripts/check-runtime-purity.sh +43 -0
  69. package/scripts/lib/front-plan.py +4 -0
  70. package/scripts/lib/graph-bundle.py +133 -0
  71. package/scripts/runtime-purity-allow.txt +5 -0
  72. package/scripts/smoke-test.sh +1 -1
  73. package/scripts/tests/lib/http-stub.py +46 -0
  74. package/scripts/tests/test-baseline-verdict.sh +92 -0
  75. package/scripts/tests/test-config.sh +25 -0
  76. package/scripts/tests/test-hooks-runtime.sh +853 -0
  77. package/scripts/tests/test-install.sh +57 -0
  78. package/scripts/tests/test-runtime-client.sh +298 -0
  79. package/scripts/tests/test-schema.sh +29 -1
  80. package/scripts/tests/test-skill-ops.sh +847 -0
  81. package/skills/building-a-micro-change/SKILL.md +22 -4
  82. package/skills/building-a-slice/SKILL.md +58 -23
  83. package/skills/building-a-slice/assets/baseline-verdict.sh +172 -0
  84. package/skills/building-a-slice/references/dod.md +12 -3
  85. package/skills/building-a-slice/references/dor.md +7 -3
  86. package/skills/building-a-slice/references/evidence-budget.md +51 -0
  87. package/skills/building-a-slice/references/exploration-fanout.md +1 -1
  88. package/skills/building-a-slice/references/gitflow.md +1 -1
  89. package/skills/building-a-slice/references/regression-baseline.md +67 -0
  90. package/skills/building-a-slice/references/runtime-protocol.md +75 -0
  91. package/skills/building-a-slice/references/state-protocol.md +12 -1
  92. package/skills/building-a-slice/workflows/README.md +7 -3
  93. package/skills/building-a-slice/workflows/explore-fanout.workflow.js +3 -3
  94. package/skills/building-a-slice/workflows/wiring-verify.workflow.js +26 -4
  95. package/skills/managing-parallel-front/SKILL.md +32 -16
  96. package/skills/openspec-archive-change/SKILL.md +15 -0
  97. package/skills/prototyping-screens/SKILL.md +104 -0
  98. package/skills/prototyping-screens/assets/DESIGN.md.template +55 -0
  99. package/skills/prototyping-screens/assets/manifest.schema.json +70 -0
  100. package/skills/prototyping-screens/assets/screen.template.html +34 -0
  101. package/skills/prototyping-screens/references/aesthetic-directions.md +42 -0
  102. package/skills/prototyping-screens/references/extraction.md +57 -0
  103. package/skills/prototyping-screens/references/self-check.md +40 -0
  104. package/skills/releasing-a-version/SKILL.md +25 -16
  105. package/skills/releasing-a-version/references/release-dod.md +7 -5
  106. package/skills/releasing-a-version/workflows/README.md +2 -1
  107. package/skills/releasing-a-version/workflows/release-gate.workflow.js +6 -5
  108. package/skills/setup-architecture/SKILL.md +4 -2
  109. package/state/README.md +20 -3
  110. package/state/build-state.schema.json +3 -2
  111. package/templates/CLAUDE.md.template +17 -1
  112. package/templates/settings-hooks.template.json +8 -4
  113. package/internal/skills/auditar-arnes/SKILL.md +0 -29
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: "BUILD: Claim"
3
+ description: Reclama trabajo al Agent Orchestrator Runtime (POST /tasks/next) sincronizando el contexto antes, reportando los hashes locales de los archivos gobernados y continuando desde el checkpoint si lo hay. Adaptador delgado sobre slice-ops.sh claim; en modo legacy o dual el slice lo decide el fichero local.
4
+ category: Workflow
5
+ tags: [build-harness, runtime, claim, trycore]
6
+ ---
7
+
8
+ # /build:claim — Pedir la siguiente tarea al runtime
9
+
10
+ **Entrada (opcional):** `EP-XXX` para pedir una épica concreta. Sin argumento, el runtime elige.
11
+
12
+ ## 1. Reclamar
13
+
14
+ ```bash
15
+ bash .claude/hooks/build/slice-ops.sh claim ${1:+--epic "$1"}
16
+ echo "rc=$?"
17
+ ```
18
+
19
+ El comando ya hace, por ti y en este orden: sincroniza el contexto (no se abre trabajo con
20
+ contexto viejo), reporta los `sha256` locales efectivos de los archivos gobernados (el servidor
21
+ detecta drift), reclama por **POST**, refresca la caché de proyección y rinde el slice.
22
+
23
+ ## 2. Ramificar por el código de salida
24
+
25
+ | rc | Qué significa | Qué haces |
26
+ |---|---|---|
27
+ | 0 | slice reclamado | si la salida trae **CHECKPOINT**, haz checkout de esa rama y **continúa desde ahí, jamás reinicies**; luego `/build:slice` |
28
+ | 3 | modo `legacy`, o `dual` (el fichero decide) | abre/retoma el slice con el protocolo del fichero (`/build:slice`) |
29
+ | 5 | runtime inalcanzable | **sigue trabajando** con lo local; reintenta al reconectar |
30
+ | 6 | rechazo del servidor (409/401/422) | muestra su razón tal cual; **no reintentes** — token, contexto o proyecto a resolver con el ADMIN |
31
+ | 7 | sin trabajo disponible, o carrera perdida | dilo y termina limpio; ofrece `/build:status` |
32
+
33
+ ## 3. Seguir
34
+
35
+ ```bash
36
+ bash .claude/hooks/build/slice-ops.sh next-step
37
+ ```
38
+
39
+ Ejecuta esa acción, o entra al pipeline con `/build:slice`.
40
+
41
+ ## Guardrails
42
+
43
+ - **No** armes peticiones al runtime a mano: todo pasa por `slice-ops.sh`.
44
+ - **Un solo slice activo**: reclamar con lease vigente devuelve el mismo slice (idempotente).
45
+ - **Nunca reinicies** un slice que trae checkpoint: es trabajo de otro agente que cayó.
46
+ - Si algo contradice `METODOLOGIA.md`, **gana la metodología**.
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: "BUILD: Escalate"
3
+ description: Registra un bloqueo del slice en el runtime (evento escalation_raised) y devuelve la decisión al humano — recortar, diferir o desbloquear nunca lo decide el modelo. Adaptador delgado sobre slice-ops.sh escalate.
4
+ category: Workflow
5
+ tags: [build-harness, runtime, escalada, gobierno, trycore]
6
+ ---
7
+
8
+ # /build:escalate — Bloqueo que decide una persona
9
+
10
+ **Entrada:** la razón del bloqueo (una frase concreta y verificable). Opcional: el gate afectado.
11
+
12
+ ## 1. Registrar
13
+
14
+ ```bash
15
+ bash .claude/hooks/build/slice-ops.sh escalate "<razón>" --gate <gate opcional>
16
+ ```
17
+
18
+ Deja el rastro tipado en el runtime (ámbito slice si hay slice reclamado; de proyecto si no).
19
+ Para razones largas, escríbelas en un fichero y usa `--file`.
20
+
21
+ ## 2. Devolver la decisión al humano
22
+
23
+ Presenta al usuario, en tres líneas: **qué está bloqueado**, **qué evidencia lo demuestra** y
24
+ **las opciones reales** (con su coste). No elijas por él.
25
+
26
+ > **Regla dura del arnés:** *Producto completo, no MVP.* Recortar o diferir alcance es un
27
+ > **bloqueante explícito** que requiere acuerdo del equipo — jamás una decisión del modelo. Escalar
28
+ > no es rendirse: es negarse a degradar el alcance en silencio.
29
+
30
+ ## Guardrails
31
+
32
+ - **No** cierres gates «para avanzar» mientras el bloqueo esté vivo.
33
+ - **No** inventes alternativas fuera del alcance acordado.
34
+ - Si el bloqueo es de contexto gobernado (allowlist, política, reglas), la salida es una
35
+ **propuesta** en el hub que publica un ADMIN, no una edición local del fichero sincronizado.
36
+ - Si algo contradice `METODOLOGIA.md`, **gana la metodología**.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "BUILD: Front"
3
- description: Abre y coordina un front paralelo de épicas NO fundacionales y disjuntas en archivos, cada una en su worktree/rama/PR. Delega en la skill managing-parallel-front (selección disjunta vía scripts/lib/front-plan.py, worktrees, merge en orden con re-smoke).
3
+ description: Prepara y coordina un front paralelo de épicas NO fundacionales y disjuntas en archivos, cada una en su worktree/rama/PR con su propio agente registrado. Delega en la skill managing-parallel-front (selección disjunta local vía scripts/lib/front-plan.py, worktrees, merge en orden con re-smoke reportado por cada worktree). Planificar, abrir, drenar y cerrar el front son actos humanos en la consola.
4
4
  category: Workflow
5
5
  tags: [build-harness, outer-loop, front-paralelo, trycore]
6
6
  ---
@@ -8,8 +8,14 @@ tags: [build-harness, outer-loop, front-paralelo, trycore]
8
8
  # /build:front — Front paralelo inter-épica
9
9
 
10
10
  Delega en la skill **managing-parallel-front**. Resumen:
11
- 1. Verifica precondiciones (scaffold confirmado; sin épica foundational abierta).
11
+ 1. Verifica precondiciones (scaffold confirmado; sin épica foundational abierta) con
12
+ `bash .claude/hooks/build/slice-ops.sh status`.
12
13
  2. Reúne candidatas no fundacionales listas (DoR pasado) con `layer` y `files_scope`.
13
- 3. Selecciona el conjunto disjunto (`scripts/lib/front-plan.py`), abre worktrees, construye y mergea en orden con re-smoke.
14
+ 3. Propón el conjunto disjunto y el `merge_order` (`scripts/lib/front-plan.py`, local) y
15
+ **preséntaselo a una persona**: abrir el front es acto de gobierno, en la consola del hub.
16
+ 4. Un worktree por épica aprobada, cada uno registrado como **agente propio**
17
+ (`trycore-build init` con el mismo token de proyecto); construye con `/build:slice`.
18
+ 5. Tras cada merge, re-smoke y reporte desde el worktree:
19
+ `bash .claude/hooks/build/release-ops.sh front-integration <front_id> --merge-status … --resmoke …`.
14
20
 
15
21
  Úsalo solo cuando haya ≥2 épicas no fundacionales disjuntas listas. Para una sola épica, usa `/build:slice`.
@@ -66,20 +66,23 @@ Voy a leer tu PRD/openspec para proponer valores y confirmar contigo:
66
66
  - **Decisiones de alto impacto** (`HIGH_STAKES_DECISIONS`): decisiones que exigen explicabilidad/justificación en la UI.
67
67
  - **Fuente de diseño** (`DESIGN_SOURCE`): ¿el producto tiene UI? Si sí, ruta/URL de la fuente
68
68
  visual de verdad (prototipo, export de diseño o mockups) y cómo localizar cada pantalla; si no,
69
- "N/A". (La escritura del estado `design_source` en `build-state.json` se hace en la Fase 3c.)
69
+ "N/A". Si hay UI pero **no existe** fuente aún, indica que `/build:prototype` puede generarla
70
+ tras el onboarding (prototipo HTML de referencia en `docs/05-prototipo/`; la confirmación
71
+ sigue siendo humana). (El reporte del hecho `design_source` se hace en la Fase 3c.)
70
72
  3. Si un punto no aplica al proyecto, registra explícitamente "no aplica" (no lo dejes como `{{...}}`).
71
73
 
72
74
  ---
73
75
 
74
76
  ## Fase 2b: Clasificar la capa de las épicas (cimiento vs negocio)
75
77
 
76
- **Primero, resuelve `project_kind`** (lee `.claude/state/build-state.json`):
78
+ **Primero, resuelve `project_kind`** (léelo de `bash .claude/hooks/build/slice-ops.sh status`):
77
79
  - `"brownfield"` → el mecanismo del caparazón es **N/A**: no preguntes ni exijas nada de la
78
80
  Fase 2c; clasifica capas como siempre y sigue.
79
81
  - `null` / ausente (detección ambigua del CLI) → pregunta **una sola vez** vía AskUserQuestion:
80
82
  *"¿Este proyecto es una app nueva (greenfield: el caparazón — menús, layout, homepage, login,
81
- redirecciones — aún no existe) o ya construida (brownfield)?"*. Escribe `project_kind` y
82
- `project_kind_source: "human"` en el estado (valida contra el schema tras escribir).
83
+ redirecciones — aún no existe) o ya construida (brownfield)?"*. Repórtalo:
84
+ `bash .claude/hooks/build/slice-ops.sh fact project-kind <greenfield|brownfield> --source human`
85
+ (rc 3 = modo legacy: escríbelo en el fichero con su protocolo).
83
86
  - `"greenfield"` → tras clasificar capas (abajo), continúa a la **Fase 2c**.
84
87
 
85
88
  El factor que más reduce el consumo de contexto por slice es que el **cimiento** ya esté construido y
@@ -121,11 +124,16 @@ Si `project_kind !== "greenfield"`, salta esta fase (N/A total).
121
124
  - **Rechaza** → **STOP** de la fase: deja `foundation.epic: null`, instruye crearla en
122
125
  discovery (`/trycore:*`) con la checklist como alcance. El gate del DoR bloqueará las
123
126
  épicas de negocio igual hasta que exista y se archive.
124
- 3. **Persistir en el estado**: escribe `foundation.required: true`, `foundation.checklist[]`
125
- (todos los ítems con su `applies`, `evidence: ""`), `foundation.epic` (o `null`). Escribe
126
- **solo** los campos del schema (`foundation` es `additionalProperties: false`) y **valida
127
- contra `build-state.schema.json` tras escribir** (aborta si no valida). Una transición = una
128
- escritura.
127
+ 3. **Reportar el hecho**: escribe la checklist podada en un fichero temporal
128
+ (`[{"id","applies","evidence":""}, …]`) y repórtala en una sola transición:
129
+
130
+ ```bash
131
+ bash .claude/hooks/build/slice-ops.sh fact foundation \
132
+ --required true --epic EP-XXX --checklist-file /tmp/foundation-checklist.json
133
+ ```
134
+
135
+ `--required false` cuando el humano podó todos los ítems (no hay caparazón exigible).
136
+ En modo legacy (rc 3) se escribe en el fichero con su protocolo.
129
137
 
130
138
  ---
131
139
 
@@ -164,15 +172,22 @@ Si el usuario lo desea y existe `package.json` en el proyecto:
164
172
 
165
173
  ## Fase 3c: (Si hay UI) Confirmar la fuente de diseño en el estado
166
174
 
167
- Espejo de la confirmación de scaffold, para `design_source` en `build-state.json`:
175
+ Espejo de la confirmación de scaffold, para el hecho `design_source`:
168
176
  - Si el producto **tiene UI**: setea `design_source.applies=true`, `source` (el puntero confirmado) y
169
177
  `confirmed=true` **solo si** el usuario confirma que la fuente de diseño existe (con `confirmed_by`,
170
- `confirmed_at`). El arnés **no genera** el prototipo.
178
+ `confirmed_at`). El prototipo puede generarse con `/build:prototype` (skill `prototyping-screens`);
179
+ `confirmed` sigue siendo exclusivamente humano.
171
180
  - Si **no hay UI**: setea `design_source.applies=false` (el mecanismo de fidelidad queda N/A).
172
181
 
173
- Escribe **solo** los campos del schema (`applies`, `source`, `confirmed`, `confirmed_by`, `confirmed_at`,
174
- `notes`; el objeto es `additionalProperties:false`) y **valida contra `build-state.schema.json` tras escribir**
175
- (aborta si no valida). Guardarraíl: **una transición = una escritura**; no toques otros campos del estado.
182
+ Repórtalo en una sola transición:
183
+
184
+ ```bash
185
+ bash .claude/hooks/build/slice-ops.sh fact design-source \
186
+ --applies true --source "<ruta/URL confirmada>" --confirmed true --by "<quién confirmó>"
187
+ ```
188
+
189
+ Sin UI: `--applies false` (el mecanismo de fidelidad queda N/A). Guardarraíl: **una transición =
190
+ una llamada**; `--confirmed true` exige `--by`: confirmar es acto humano.
176
191
 
177
192
  ---
178
193
 
@@ -221,6 +236,40 @@ CLAUDE.md actualizado. Auto-memory escrita. Los agentes de calidad ya leen tu do
221
236
 
222
237
  ---
223
238
 
239
+ ## Fase 5: (Con runtime) Preparar el bundle de grafo para el ADMIN
240
+
241
+ El grafo del backlog (épicas, capas, `files_scope`, dependencias, historias, líneas de release) vive
242
+ en el runtime. **Importarlo es un acto de gobierno**: la superficie exige sesión de ADMIN y rechaza
243
+ el token de agente. El arnés **prepara y valida**; una persona sube.
244
+
245
+ 1. Extrae el grafo de los artefactos de discovery ya leídos (`docs/03-backlog/epicas.md`,
246
+ `docs/02-user-story-map/`, `docs/04-historias/`) a un JSON:
247
+
248
+ ```json
249
+ {"project_ref": "<nombre del proyecto>",
250
+ "epics": [{"code": "EP-001", "title": "…", "layer": "foundational",
251
+ "files_scope": ["src/core/**"], "depends_on": [],
252
+ "stories": [{"id": "HU-001", "title": "…"}]}],
253
+ "release_lines": [{"id": "R1-mvp", "epics": ["EP-001"]}]}
254
+ ```
255
+
256
+ 2. Normalízalo y valídalo (determinista, nunca sube nada):
257
+
258
+ ```bash
259
+ python3 .claude/scripts/lib/graph-bundle.py < /tmp/epics.json > /tmp/graph-bundle.json
260
+ ```
261
+
262
+ 3. **Lee los avisos** (`warnings` del bundle y stderr): capa ausente, dependencia inexistente,
263
+ ciclo, línea de release que referencia una épica desconocida. Corrígelos en discovery — el arnés
264
+ **no inventa** el grafo ni edita `epicas.md` (salvo el carve-out de la épica caparazón, Fase 2c).
265
+
266
+ 4. **Entrega el fichero a un ADMIN** con esta instrucción literal: *«súbelo en la consola del hub,
267
+ pantalla de import del proyecto»*. El import es idempotente y **reanuda** si falló a medias.
268
+
269
+ 5. Modo `legacy` o sin credenciales: salta esta fase; el grafo sigue viviendo en los documentos.
270
+
271
+ ---
272
+
224
273
  ## Guardrails
225
274
 
226
275
  - No avances sin confirmar los 7 puntos. Si el usuario omite alguno, repregunta o marca "no aplica".
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: "BUILD: Prototype"
3
+ description: Genera o amplía el prototipo HTML de referencia (la fuente de diseño / DESIGN_SOURCE) en docs/05-prototipo/. Dos modos — greenfield (prototipo inicial desde los docs de discovery + dirección estética elegida por el humano) y feature (pantallas de una épica nueva extraídas en vivo del UI ya implementado). Delega en la skill prototyping-screens. La confirmación de design_source sigue siendo humana.
4
+ category: Workflow
5
+ tags: [build-harness, outer-loop, prototipo, design-source, trycore]
6
+ ---
7
+
8
+ # /build:prototype — Prototipo HTML de referencia
9
+
10
+ Delega en la skill **prototyping-screens**. Uso:
11
+
12
+ - `/build:prototype` — modo **greenfield** (o detección automática): prototipo inicial completo.
13
+ Inventario de pantallas desde PRD/mapa/historias → confirmación humana → dirección estética
14
+ (manual de marca o 2-3 variantes a elección humana) → generación por lotes con auto-verificación.
15
+ - `/build:prototype <épica>` — modo **feature**: pantallas nuevas de esa épica, coherentes con el
16
+ UX/UI ya implementado. **Precondición dura**: app corriendo + MCP de inspección de UI (p.ej.
17
+ chrome-devtools); sin ellos hace STOP (la extracción viva de CSS computado no se degrada).
18
+
19
+ Es **outer-loop**: córrelo antes de abrir slices (como `/build:architect`). Produce
20
+ `docs/05-prototipo/` (DESIGN.md, tokens.css, manifest.json, pantallas/) y, en greenfield con
21
+ aprobación humana, reporta el hecho `design_source` al runtime
22
+ (`slice-ops.sh fact design-source …`). **La skill genera; el humano aprueba**: la confirmación de la
23
+ fuente de diseño y el `estado: "aprobada"` del manifest nunca se auto-marcan.
@@ -33,12 +33,41 @@ Stop aquí si no está instalado o si falta `python3`.
33
33
 
34
34
  ## Fase 1: Identificar slices sin reflexionar
35
35
 
36
- Lee `.claude/state/build-state.json` y filtra las entradas de `history[]` con `reflected != true`:
36
+ El origen del dato depende del **modo** (igual que consume `reflect-nudge.sh`): en `runtime` puro el
37
+ fichero local NO se lee — puede faltar o quedar fósil (`runtime-protocol.md`) — y el cómputo lo hace el
38
+ servidor sobre **todo el proyecto**; en `legacy`/`dual` el fichero sigue siendo primario.
37
39
 
38
40
  ```bash
39
- python3 - <<'PY'
41
+ MODE="$(bash .claude/hooks/build/slice-ops.sh mode)"
42
+ if [ "$MODE" = "runtime" ]; then
43
+ # runtime: NO se lee el fichero legacy (fósil/ausente). El servidor ya agregó el nudge
44
+ # "reflect" sobre todo el proyecto; lo leemos de la línea `nudges:` de `status`, el mismo
45
+ # dato que consume reflect-nudge.sh.
46
+ bash .claude/hooks/build/slice-ops.sh status | python3 - <<'PY'
47
+ import json, sys
48
+ nudges = []
49
+ for line in sys.stdin:
50
+ if line.startswith("nudges:"):
51
+ try:
52
+ nudges = json.loads(line.split(":", 1)[1].strip())
53
+ except Exception:
54
+ nudges = []
55
+ break
56
+ pend = [n.get("message") for n in nudges
57
+ if isinstance(n, dict) and n.get("kind") == "reflect" and n.get("message")]
58
+ if pend:
59
+ for m in pend:
60
+ print(m)
61
+ print("TOTAL_RUNTIME", len(pend))
62
+ else:
63
+ print("TOTAL 0")
64
+ PY
65
+ else
66
+ # legacy o dual: el fichero es primario en ambos modos (en dual, reconcile-build-state.py
67
+ # lo mantiene sincronizado — runtime-protocol.md); filtra `history[]` con `reflected != true`.
68
+ python3 - <<'PY'
40
69
  import json, os, sys
41
- p = ".claude/state/build-state.json"
70
+ p = ".claude/state/build-state.json" # legacy: ruta local, no runtime
42
71
  if not os.path.exists(p):
43
72
  print("NO_STATE"); sys.exit(0) # estado ausente → nada que reflexionar, salir
44
73
  try:
@@ -50,13 +79,21 @@ for h in pend:
50
79
  print(h.get("epica"), "·", h.get("openspec_change"), "·", h.get("branch"), "·", ",".join(h.get("hus") or []))
51
80
  print("TOTAL", len(pend))
52
81
  PY
82
+ fi
53
83
  ```
54
84
 
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.
85
+ **Si `NO_STATE`** (solo aplica en `legacy`/`dual`): no hay estado → nada que reflexionar; termina.
86
+ **Si `CORRUPT_STATE`:** el estado está corrupto → repórtalo y **detente sin escribir** (no estampes ni
87
+ edites). **Si `TOTAL 0`:** informa "No hay slices pendientes de reflexión ✅" y termina. No inventes
88
+ trabajo.
89
+
90
+ **En `runtime`** el servidor solo entrega el **conteo agregado** (`TOTAL_RUNTIME`), sin `epica`/
91
+ `openspec_change`/`branch` por slice (el cliente no puede enumerarlos hoy). Si `TOTAL_RUNTIME > 0`,
92
+ dile al humano cuántos hay pendientes según el runtime y **pregúntale con AskUserQuestion** qué slice
93
+ (`EP-XXX` + `openspec_change`) reflexionar primero; no adivines ni asumas "el más reciente" sin ese dato.
58
94
 
59
- Si hay varios, procésalos **de uno en uno** (el más reciente primero), o pregunta al usuario cuál.
95
+ Si tienes el detalle por slice (`legacy`/`dual`) y hay varios, procésalos **de uno en uno** (el más
96
+ reciente primero), o pregunta al usuario cuál.
60
97
 
61
98
  ---
62
99
 
@@ -119,42 +156,25 @@ Para las viñetas aprobadas, **inserta** (no reemplaces) dentro del bloque marca
119
156
 
120
157
  ## Fase 5: Estampar el slice como reflexionado
121
158
 
122
- Marca el/los slice(s) procesado(s) en `history[]` para que el nudge calle. Usa la hora UTC real:
159
+ El estampado es una transición de dominio más: la hace el ejecutable, no un `json.dump` a mano.
123
160
 
124
161
  ```bash
125
- NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
126
- python3 - "$NOW" "<openspec_change>" <<'PY'
127
- import json, sys, os, tempfile
128
- now, change = sys.argv[1], sys.argv[2]
129
- p = ".claude/state/build-state.json"
130
- try:
131
- d = json.load(open(p))
132
- except Exception as e:
133
- print("ABORT: estado ilegible, no estampo:", e); sys.exit(1)
134
- for h in d.get("history") or []:
135
- if isinstance(h, dict) and h.get("openspec_change") == change:
136
- h["reflected"] = True
137
- h["reflected_at"] = now
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)
152
- print("estampado:", change)
153
- PY
162
+ MODE="$(bash .claude/hooks/build/slice-ops.sh mode)"
163
+ if [ "$MODE" = "legacy" ]; then
164
+ # Modo legacy (v0.8.5): se estampa `reflected`/`reflected_at` en la entrada de history[]
165
+ # del fichero, con escritura atómica y validación contra el schema local. Protocolo en
166
+ # skills/building-a-slice/references/state-protocol.md.
167
+ echo "legacy: estampa reflected=true en la entrada de history[] del fichero"
168
+ else
169
+ bash .claude/hooks/build/slice-ops.sh progress \
170
+ "reflexión completada de <EP-XXX> (<openspec_change>): <N> aprendizajes aplicados"
171
+ bash .claude/hooks/build/slice-ops.sh fact harness-phase building >/dev/null 2>&1 || true
172
+ fi
154
173
  ```
155
174
 
156
- Estampa **aunque no haya habido aprendizajes** (reflexionar y no encontrar nada también cierra el
157
- ciclo). Repite por cada slice procesado.
175
+ En `dual`/`runtime` el hecho «este slice ya se reflexionó» vive en el event stream del runtime:
176
+ el nudge de `reflect-nudge.sh` lo computa el servidor sobre **todo** el proyecto, no sobre este
177
+ clon (por eso el nudge dejó de mentir con 31/33 slices sin estampar).
158
178
 
159
179
  ---
160
180
 
@@ -165,7 +185,7 @@ ciclo). Repite por cada slice procesado.
165
185
 
166
186
  Slice: <EP-XXX> (<openspec_change>)
167
187
  Aprendizajes: <N agregados a CLAUDE.md> · <M a auto-memory> · <0 si solo se estampó>
168
- Estampado: reflected=true
188
+ Estampado: reportado al runtime (o reflected=true en modo legacy)
169
189
 
170
190
  Pendientes de reflexión restantes: <K>
171
191
  ```
@@ -27,9 +27,9 @@ Stop si no está instalado o falta `python3`.
27
27
 
28
28
  ## 2. Identificar la release y sus épicas
29
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[]`).
30
+ Consulta el estado con `bash .claude/hooks/build/slice-ops.sh status` y cruza con
31
+ `docs/02-user-story-map/` para resolver qué épicas componen la release. No hay entrada que crear: los
32
+ veredictos que reporte `releasing-a-version` pueblan la línea de release en el runtime.
33
33
 
34
34
  ---
35
35
 
@@ -67,10 +67,12 @@ reviewer. Es el gate **no negociable**: sin él, no hay release.
67
67
 
68
68
  ## 6. Síntesis y escritura
69
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**
70
+ - Reporta **cada** gate medido con
71
+ `bash .claude/hooks/build/release-ops.sh verdict <line> <gate> <pass|fail|na> --evidence-file f`
72
+ (`na` cuando no aplica). El **agregado** lo hace el runtime: **parciales no promueven a `passed`**.
73
+ - Con los seis reportados y en verde, recuérdale al humano que **el cierre es suyo**
74
+ (`release-ops.sh close-hint <line>`): el agente no cierra releases.
75
+ - Algo ✗ → los hallazgos bloqueantes se corrigen **como un slice normal**
74
76
  (`/build:slice` / `building-a-slice`) y se **re-corre** el Release Gate.
75
77
 
76
78
  ---
@@ -81,4 +83,5 @@ reviewer. Es el gate **no negociable**: sin él, no hay release.
81
83
  vez por release (`O(releases)`).
82
84
  - **No dupliques el inner loop**: no se hace TDD ni se cierran gates por slice.
83
85
  - **`integration` con deps reales es obligatorio**; no se acepta con todo stubbeado.
86
+ - **El agente reporta, el humano cierra** (spec §2): ninguna llamada de cierre con token de agente.
84
87
  - Delega en `releasing-a-version`; **no** la reimplementa. Si algo contradice `METODOLOGIA.md`, gana la metodología.
@@ -1,29 +1,49 @@
1
1
  ---
2
2
  name: "BUILD: Resume"
3
- description: Rehidrata el slice activo desde disco tras un reinicio de contexto (reconcilia estado, muestra continuidad y la siguiente acción).
3
+ description: Rehidrata el slice activo tras un reinicio de contexto — refresca la proyección desde el runtime (o reconcilia el fichero en modo legacy), muestra la continuidad y la siguiente acción determinista.
4
4
  category: Workflow
5
5
  tags: [build-harness, resume, rehydrate, trycore]
6
6
  ---
7
7
 
8
8
  # /build:resume — Retomar sin pérdida
9
9
 
10
- Objetivo: reconstruir el contexto de construcción **desde disco**, no desde la conversación.
10
+ Objetivo: reconstruir el contexto de construcción **desde fuera de la conversación** — del
11
+ runtime en `dual`/`runtime`, del disco en `legacy`.
11
12
 
12
13
  ## Pasos
13
14
 
14
- 1. **Reconciliar**: `python3 .claude/hooks/build/reconcile-build-state.py .claude/state/build-state.json`
15
- (degrada wiring sin evidencia; anota branch drift; fail-open).
15
+ 1. **Averigua el modo y refresca**:
16
16
 
17
- 2. **Leer estado**: `.claude/state/build-state.json`. Extraer `active_slice`, sus `gates`, `wiring_checklist`, `progress_log`, `session_continuity`, y `parallel_front` si existe.
17
+ ```bash
18
+ MODE="$(bash .claude/hooks/build/slice-ops.sh mode)"
19
+ bash .claude/hooks/build/slice-ops.sh status
20
+ ```
18
21
 
19
- 3. **Determinar la siguiente acción por prioridad** (la primera que aplique):
20
- 1. `session_continuity.resume_hint` presente ejecutarla.
21
- 2. Items de `wiring_checklist` en `failing` → cablear el primero (con prueba real; no marcar passing sin evidencia).
22
- 3. `sub_slices` con `status!=done` construir el siguiente.
23
- 4. Según `active_slice.phase` → continuar el pipeline (delegar en la skill `building-a-slice`).
22
+ - `legacy` reconcilia el fichero como en v0.8.5: `python3 .claude/hooks/build/reconcile-build-state.py .claude/state/build-state.json`
23
+ (degrada wiring sin evidencia; anota branch drift; fail-open) y sigue
24
+ `building-a-slice/references/state-protocol.md`.
25
+ - `dual|runtime` `status` ya trae slice, fase, gates, wiring `failing`, contexto, lease y
26
+ cola pendiente.
24
27
 
25
- 4. **Si hay `parallel_front`**: delegar en la skill `managing-parallel-front` (Task 12+).
28
+ 2. **Determina la siguiente acción**:
26
29
 
27
- 5. Registrar un hito en `progress_log[]` (`by: /build:resume`).
30
+ ```bash
31
+ bash .claude/hooks/build/slice-ops.sh next-step
32
+ ```
28
33
 
29
- **Regla dura:** mientras quede un item `failing`, el slice NO está terminado. Nunca marques `passing` sin evidencia de ejecución real.
34
+ La prioridad es determinista y la calcula el cliente: `resume_hint` items de wiring en
35
+ `failing` → sub-slice pendiente → primer gate abierto por fase. **Ejecútala.**
36
+
37
+ 3. **Si el `status` muestra `⚠ lease` (lease perdido)**: haz `slice-ops.sh checkpoint --note …`,
38
+ **detén** el trabajo y vuelve a reclamar (`slice-ops.sh claim`) antes de cerrar nada más.
39
+
40
+ 4. **Si el `status` muestra la proyección `⚠ stale`** (sin refrescar) y el runtime no responde:
41
+ sigue trabajando con lo que hay — offline nunca bloquea — y no cierres gates a ciegas.
42
+
43
+ 5. **Front paralelo**: si este worktree pertenece a un front, delega en la skill
44
+ `managing-parallel-front`.
45
+
46
+ 6. Deja el hito: `bash .claude/hooks/build/slice-ops.sh progress "retomado por /build:resume: <qué sigue>"`.
47
+
48
+ **Regla dura:** mientras quede un item `failing`, el slice NO está terminado. Nunca marques
49
+ `passing` sin evidencia de ejecución real (`--evidence-file`).
@@ -29,25 +29,20 @@ Stop si no está instalado o falta `python3`.
29
29
  ## 2. Leer el estado y decidir punto de entrada
30
30
 
31
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
32
+ MODE="$(bash .claude/hooks/build/slice-ops.sh mode)"
33
+ bash .claude/hooks/build/slice-ops.sh status
46
34
  ```
47
35
 
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
36
+ - `MODE=legacy` → el fichero es primario: sigue `building-a-slice/references/state-protocol.md`
37
+ (comportamiento v0.8.5 exacto, sin red).
38
+ - `MODE=dual|runtime`el estado vive en el runtime
39
+ (`building-a-slice/references/runtime-protocol.md`).
40
+ - **Sin slice activo** (`slice: ninguno reclamado`): en `runtime`, recláma­lo con
41
+ `bash .claude/hooks/build/slice-ops.sh claim --epic EP-XXX` — `rc 7` = sin trabajo disponible,
42
+ `rc 6` = rechazo del servidor (muestra su razón, no reintentes). En `legacy|dual` (rc 3) abre el
43
+ slice por **dor** con el protocolo del fichero.
44
+ - **Con slice activo** → **reanuda donde toca**:
45
+ `bash .claude/hooks/build/slice-ops.sh next-step` (no abras otro: el modelo es
51
46
  secuencial, un solo slice activo).
52
47
 
53
48
  ---
@@ -56,14 +51,19 @@ PY
56
51
 
57
52
  Antes de escribir código de slice, verifica los gates de proyecto:
58
53
 
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
- - **Caparazón (solo greenfield)**: si `project_kind === "greenfield"` y `foundation.required === true`
63
- y la épica objetivo es `layer: business` mientras `foundation.completed_at` no esté estampado (la
64
- caparazón `foundation.epic` archivada con evidencia) **STOP**: solo la épica caparazón
65
- (`foundation.epic`) u otra fundacional puede abrir. Lo valida en detalle el `dor-dod-gatekeeper`
66
- (criterio 7-bis). Brownfield N/A, no preguntes.
54
+ Los tres hechos salen de la línea `hechos:` que imprime `slice-ops.sh status` (`scaffold_confirmed`,
55
+ `design_source_confirmed`, `project_kind`, `foundation_done` del runtime en `dual`/`runtime`; del
56
+ fichero en `legacy`):
57
+
58
+ - **scaffold** confirmado (`scaffold_confirmed`). Si no **STOP**: delega en la **Fase 0** de
59
+ `building-a-slice` (pregunta explícita; el arnés **no genera** el scaffold). No abras el slice.
60
+ - Si el proyecto tiene UI, **fuente de diseño** confirmada (`design_source_confirmed`, Fase 0-bis). Si
61
+ no**STOP** igual.
62
+ - **Caparazón (solo greenfield)**: si `project_kind` es `greenfield` y `foundation_done` es `false`, y
63
+ la épica objetivo es `layer: business` → **STOP**: solo la épica caparazón u otra fundacional puede
64
+ abrir. `status` no identifica **cuál** es la épica caparazón (eso vive hoy en el fichero legacy,
65
+ leído directo por `dor-dod-gatekeeper`); pregunta o consúltalo ahí. Lo valida en detalle el
66
+ `dor-dod-gatekeeper` (criterio 7-bis). Brownfield → N/A, no preguntes.
67
67
 
68
68
  El hook `scaffold-guard.sh` respalda esto en tiempo real.
69
69
 
@@ -91,8 +91,13 @@ archivada, recuerda el default del Release Gate (ver `/build:release`).
91
91
 
92
92
  ## Guardrails
93
93
 
94
- - **Un solo slice activo** (secuencial). No abras un segundo mientras haya `active_slice`.
95
- - **Orden estricto de gates**; un gate no se salta. `dod` exige `wiring_verified: true`.
94
+ - **Un solo slice activo** (secuencial). No abras un segundo mientras haya uno reclamado; en
95
+ `runtime` el propio claim es idempotente y devuelve el mismo slice con el lease vigente.
96
+ - **Todo acto de dominio pasa por `slice-ops.sh`**; en modo `runtime` no se escribe el fichero local.
97
+ - **Orden estricto de gates**; un gate no se salta. `dod` exige `wiring_verified: true`. El bucle
98
+ adversarial tiene condición de parada: **máximo 2 pasadas completas por slice** (pasadas 2+
99
+ incrementales por `verified_at_sha`; lo diferido se declara en el PR y lo custodia el Release
100
+ Gate — ver METODOLOGIA §1-bis).
96
101
  - **Sin scaffold confirmado, no hay slice** (el arnés lo exige pero no lo genera).
97
102
  - **Agnóstico**: este comando no asume dominio; lo específico entra por `/build:onboard` y `stack-allowlist.json`.
98
103
  - Si algo contradice `METODOLOGIA.md`, **gana la metodología**.
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: "BUILD: Status"
3
+ description: Informe del agente y su trabajo — modo, proyecto, slice activo, gates, wiring failing, versión de contexto, lease y cola de eventos pendientes. Solo lectura, nunca bloquea. Adaptador delgado sobre slice-ops.sh status.
4
+ category: Workflow
5
+ tags: [build-harness, runtime, status, trycore]
6
+ ---
7
+
8
+ # /build:status — ¿Dónde estamos?
9
+
10
+ ## 1. Informe
11
+
12
+ ```bash
13
+ bash .claude/hooks/build/slice-ops.sh status
14
+ ```
15
+
16
+ Refresca la proyección desde `/agent/context` y muestra: **modo**, proyecto y agente, slice activo
17
+ con fase/gates/wiring, rama y change, versión de contexto (`✓ vN` o `⚠ stale`), lease y número de
18
+ eventos pendientes en la cola. En modo `legacy` dice que la fuente de verdad es el fichero local y
19
+ no consulta nada.
20
+
21
+ Con `--no-refresh` no toca la red (útil sin conexión).
22
+
23
+ ## 2. Leer las señales
24
+
25
+ - `⚠ stale` → la proyección no se pudo refrescar: los guards siguen operando con lo último
26
+ sincronizado. **No cierres gates a ciegas**; reintenta cuando vuelva el runtime.
27
+ - `⚠ lease perdido` → haz `slice-ops.sh checkpoint --note …`, detén el trabajo y vuelve a reclamar.
28
+ - `cola: N evento(s)` con N creciendo y sin bajar → el daemon no está despachando; revisa
29
+ `.claude/state/heartbeat-status.json` (y, en el sub-slice D, `trycore-build doctor`).
30
+
31
+ ## Guardrails
32
+
33
+ - **Solo lectura**: este comando no transiciona nada.
34
+ - El estado de **la flota** (otros agentes, otros slices, releases y fronts del proyecto) vive en la
35
+ consola del hub: es superficie humana, no de agente.