@trycore/spec-build-harness 0.8.5 → 0.11.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.
- package/.claude-plugin/plugin.json +1 -1
- package/GOVERNANCE.md +27 -4
- package/INSTALL.md +27 -5
- package/METODOLOGIA.md +55 -5
- package/README.md +39 -6
- package/VERSION +1 -1
- package/agents/build/build-orchestrator.md +33 -7
- package/agents/build/dor-dod-gatekeeper.md +13 -5
- package/agents/build/wiring-adversarial-verifier.md +52 -5
- package/commands/build/architect.md +1 -1
- package/commands/build/claim.md +46 -0
- package/commands/build/escalate.md +36 -0
- package/commands/build/front.md +9 -3
- package/commands/build/onboard.md +75 -14
- package/commands/build/prototype.md +3 -2
- package/commands/build/reflect.md +60 -40
- package/commands/build/release.md +10 -7
- package/commands/build/resume.md +33 -13
- package/commands/build/slice.md +32 -27
- package/commands/build/status.md +35 -0
- package/commands/build/work.md +11 -8
- package/config/build-config.template.json +4 -0
- package/dist/cli.js +32 -0
- package/dist/commands/doctor.js +42 -0
- package/dist/commands/init.js +84 -1
- package/dist/commands/migrate.js +153 -0
- package/dist/commands/status.js +34 -0
- package/dist/lib/normalize.js +1123 -0
- package/dist/lib/paths.js +6 -0
- package/dist/lib/runtime-client.js +196 -0
- package/dist/lib/settings-merge.js +3 -3
- package/dist/lib/state-bundle.js +150 -0
- package/docs/commands.md +25 -8
- package/docs/getting-started.md +1 -0
- package/docs/hooks.md +114 -27
- package/docs/runtime/guia-modo-dual-y-migracion.md +143 -0
- package/docs/runtime/plan-migracion-harness-v0.9.md +11 -0
- package/docs/runtime/protocolo-cliente-runtime.md +120 -35
- package/hooks/build/build-gate-check.sh +21 -0
- package/hooks/build/context-monitor.sh +82 -15
- package/hooks/build/context-sync.sh +192 -0
- package/hooks/build/design-source-guard.sh +30 -2
- package/hooks/build/dual-compare.sh +92 -0
- package/hooks/build/event-emitter.sh +32 -0
- package/hooks/build/gitflow-guard.sh +164 -14
- package/hooks/build/heartbeat.sh +259 -0
- package/hooks/build/lib/agent-context.sh +139 -0
- package/hooks/build/lib/config.sh +27 -0
- package/hooks/build/lib/projection.sh +71 -0
- package/hooks/build/lib/runtime-client.sh +625 -0
- package/hooks/build/lib/runtime-ops.sh +227 -0
- package/hooks/build/lib/state-io.sh +5 -18
- package/hooks/build/load-build-state.sh +64 -2
- package/hooks/build/reflect-nudge.sh +15 -0
- package/hooks/build/release-gate-nudge.sh +15 -0
- package/hooks/build/release-ops.sh +171 -0
- package/hooks/build/scaffold-guard.sh +29 -2
- package/hooks/build/session-start.sh +103 -0
- package/hooks/build/session-stop.sh +22 -0
- package/hooks/build/slice-ops.sh +948 -0
- package/hooks/build/stack-guard.sh +8 -0
- package/hooks/build/statusline-bridge.sh +24 -3
- package/hooks/build-harness.json +16 -0
- package/package.json +3 -3
- package/scripts/check-agnostic.sh +3 -1
- package/scripts/check-pack-clean.sh +31 -0
- package/scripts/check-runtime-purity.sh +43 -0
- package/scripts/denylist.txt +4 -0
- package/scripts/lib/front-plan.py +4 -0
- package/scripts/lib/graph-bundle.py +181 -0
- package/scripts/runtime-purity-allow.txt +5 -0
- package/scripts/smoke-test.sh +1 -1
- package/scripts/tests/lib/http-stub.py +46 -0
- package/scripts/tests/test-baseline-verdict.sh +92 -0
- package/scripts/tests/test-config.sh +25 -0
- package/scripts/tests/test-hooks-runtime.sh +828 -0
- package/scripts/tests/test-install.sh +103 -0
- package/scripts/tests/test-runtime-client.sh +298 -0
- package/scripts/tests/test-schema.sh +29 -1
- package/scripts/tests/test-skill-ops.sh +1367 -0
- package/skills/building-a-micro-change/SKILL.md +22 -4
- package/skills/building-a-slice/SKILL.md +55 -21
- package/skills/building-a-slice/assets/baseline-verdict.sh +172 -0
- package/skills/building-a-slice/references/dod.md +12 -3
- package/skills/building-a-slice/references/dor.md +3 -2
- package/skills/building-a-slice/references/evidence-budget.md +51 -0
- package/skills/building-a-slice/references/exploration-fanout.md +1 -1
- package/skills/building-a-slice/references/gitflow.md +1 -1
- package/skills/building-a-slice/references/regression-baseline.md +67 -0
- package/skills/building-a-slice/references/runtime-protocol.md +75 -0
- package/skills/building-a-slice/references/state-protocol.md +12 -1
- package/skills/building-a-slice/workflows/README.md +7 -3
- package/skills/building-a-slice/workflows/explore-fanout.workflow.js +3 -3
- package/skills/building-a-slice/workflows/wiring-verify.workflow.js +26 -4
- package/skills/managing-parallel-front/SKILL.md +32 -16
- package/skills/openspec-archive-change/SKILL.md +15 -0
- package/skills/prototyping-screens/SKILL.md +9 -5
- package/skills/releasing-a-version/SKILL.md +26 -16
- package/skills/releasing-a-version/references/release-dod.md +7 -5
- package/skills/releasing-a-version/workflows/README.md +2 -1
- package/skills/releasing-a-version/workflows/release-gate.workflow.js +6 -5
- package/skills/setup-architecture/SKILL.md +4 -2
- package/state/README.md +16 -1
- package/state/build-state.schema.json +2 -1
- package/templates/CLAUDE.md.template +16 -0
- package/templates/settings-hooks.template.json +8 -4
- package/internal/skills/auditar-arnes/SKILL.md +0 -29
package/commands/build/front.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "BUILD: Front"
|
|
3
|
-
description:
|
|
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.
|
|
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`.
|
|
@@ -68,21 +68,21 @@ Voy a leer tu PRD/openspec para proponer valores y confirmar contigo:
|
|
|
68
68
|
visual de verdad (prototipo, export de diseño o mockups) y cómo localizar cada pantalla; si no,
|
|
69
69
|
"N/A". Si hay UI pero **no existe** fuente aún, indica que `/build:prototype` puede generarla
|
|
70
70
|
tras el onboarding (prototipo HTML de referencia en `docs/05-prototipo/`; la confirmación
|
|
71
|
-
sigue siendo humana). (
|
|
72
|
-
en la Fase 3c.)
|
|
71
|
+
sigue siendo humana). (El reporte del hecho `design_source` se hace en la Fase 3c.)
|
|
73
72
|
3. Si un punto no aplica al proyecto, registra explícitamente "no aplica" (no lo dejes como `{{...}}`).
|
|
74
73
|
|
|
75
74
|
---
|
|
76
75
|
|
|
77
76
|
## Fase 2b: Clasificar la capa de las épicas (cimiento vs negocio)
|
|
78
77
|
|
|
79
|
-
**Primero, resuelve `project_kind`** (
|
|
78
|
+
**Primero, resuelve `project_kind`** (léelo de `bash .claude/hooks/build/slice-ops.sh status`):
|
|
80
79
|
- `"brownfield"` → el mecanismo del caparazón es **N/A**: no preguntes ni exijas nada de la
|
|
81
80
|
Fase 2c; clasifica capas como siempre y sigue.
|
|
82
81
|
- `null` / ausente (detección ambigua del CLI) → pregunta **una sola vez** vía AskUserQuestion:
|
|
83
82
|
*"¿Este proyecto es una app nueva (greenfield: el caparazón — menús, layout, homepage, login,
|
|
84
|
-
redirecciones — aún no existe) o ya construida (brownfield)?"*.
|
|
85
|
-
`
|
|
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).
|
|
86
86
|
- `"greenfield"` → tras clasificar capas (abajo), continúa a la **Fase 2c**.
|
|
87
87
|
|
|
88
88
|
El factor que más reduce el consumo de contexto por slice es que el **cimiento** ya esté construido y
|
|
@@ -124,11 +124,16 @@ Si `project_kind !== "greenfield"`, salta esta fase (N/A total).
|
|
|
124
124
|
- **Rechaza** → **STOP** de la fase: deja `foundation.epic: null`, instruye crearla en
|
|
125
125
|
discovery (`/trycore:*`) con la checklist como alcance. El gate del DoR bloqueará las
|
|
126
126
|
épicas de negocio igual hasta que exista y se archive.
|
|
127
|
-
3. **
|
|
128
|
-
(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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.
|
|
132
137
|
|
|
133
138
|
---
|
|
134
139
|
|
|
@@ -167,16 +172,22 @@ Si el usuario lo desea y existe `package.json` en el proyecto:
|
|
|
167
172
|
|
|
168
173
|
## Fase 3c: (Si hay UI) Confirmar la fuente de diseño en el estado
|
|
169
174
|
|
|
170
|
-
Espejo de la confirmación de scaffold, para
|
|
175
|
+
Espejo de la confirmación de scaffold, para el hecho `design_source`:
|
|
171
176
|
- Si el producto **tiene UI**: setea `design_source.applies=true`, `source` (el puntero confirmado) y
|
|
172
177
|
`confirmed=true` **solo si** el usuario confirma que la fuente de diseño existe (con `confirmed_by`,
|
|
173
178
|
`confirmed_at`). El prototipo puede generarse con `/build:prototype` (skill `prototyping-screens`);
|
|
174
179
|
`confirmed` sigue siendo exclusivamente humano.
|
|
175
180
|
- Si **no hay UI**: setea `design_source.applies=false` (el mecanismo de fidelidad queda N/A).
|
|
176
181
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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.
|
|
180
191
|
|
|
181
192
|
---
|
|
182
193
|
|
|
@@ -225,6 +236,56 @@ CLAUDE.md actualizado. Auto-memory escrita. Los agentes de calidad ya leen tu do
|
|
|
225
236
|
|
|
226
237
|
---
|
|
227
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). La salida va a
|
|
257
|
+
`.claude/state/graph-bundle.json` para que `trycore-build migrate` la encuentre sola:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
python3 .claude/scripts/lib/graph-bundle.py < /tmp/epics.json > .claude/state/graph-bundle.json
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
El script emite las épicas en el **formato exacto del hub**: `layer` en MAYÚSCULA
|
|
264
|
+
(`FOUNDATIONAL`|`BUSINESS` — un layer inválido rechaza el grafo ENTERO), historias con
|
|
265
|
+
`code`, y `release_line` (string) por épica.
|
|
266
|
+
|
|
267
|
+
3. **Lee los avisos** (`warnings` del bundle y stderr): capa ausente, dependencia inexistente,
|
|
268
|
+
ciclo, línea de release que referencia una épica desconocida. Corrígelos en discovery — el arnés
|
|
269
|
+
**no inventa** el grafo ni edita `epicas.md` (salvo el carve-out de la épica caparazón, Fase 2c).
|
|
270
|
+
|
|
271
|
+
4. **Entrega al ADMIN un solo fichero.** Si vas a migrar historial, corre `trycore-build migrate`:
|
|
272
|
+
embebe el grafo como sección `graph` del bundle de estado (un único fichero para la pantalla de
|
|
273
|
+
import del proyecto). Sin historial que migrar, entrega `graph-bundle.json` con esta instrucción
|
|
274
|
+
literal: *«súbelo en la consola del hub, pantalla de import del proyecto»*. El import es
|
|
275
|
+
idempotente y **reanuda** si falló a medias.
|
|
276
|
+
|
|
277
|
+
Si el proyecto tiene épicas construidas **antes** de instalar el arnés (sin slice en el
|
|
278
|
+
historial del estado local), decláralas en un fichero que **el humano confirme** y pásalo con
|
|
279
|
+
`--pre-harness <fichero>` (`{"epics": [{"code": "EP-001", "note": "…opcional…"}]}`): se
|
|
280
|
+
importan archivadas **sin declarar gates**, con un acta de migración honesta — sin esto
|
|
281
|
+
quedan «sin empezar» en el hub y `claim_next` las reparte como trabajo nuevo. `migrate`
|
|
282
|
+
avisa de las épicas del grafo sin slice ni declaración pre-arnés, pero **nunca** las
|
|
283
|
+
añade por su cuenta.
|
|
284
|
+
|
|
285
|
+
5. Modo `legacy` o sin credenciales: salta esta fase; el grafo sigue viviendo en los documentos.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
228
289
|
## Guardrails
|
|
229
290
|
|
|
230
291
|
- No avances sin confirmar los 7 puntos. Si el usuario omite alguno, repregunta o marca "no aplica".
|
|
@@ -18,5 +18,6 @@ Delega en la skill **prototyping-screens**. Uso:
|
|
|
18
18
|
|
|
19
19
|
Es **outer-loop**: córrelo antes de abrir slices (como `/build:architect`). Produce
|
|
20
20
|
`docs/05-prototipo/` (DESIGN.md, tokens.css, manifest.json, pantallas/) y, en greenfield con
|
|
21
|
-
aprobación humana,
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
56
|
-
corrupto → repórtalo y **detente sin escribir** (no estampes ni
|
|
57
|
-
slices pendientes de reflexión ✅" y termina. No inventes
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
157
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
-
|
|
71
|
-
`
|
|
72
|
-
|
|
73
|
-
-
|
|
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.
|
package/commands/build/resume.md
CHANGED
|
@@ -1,29 +1,49 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "BUILD: Resume"
|
|
3
|
-
description: Rehidrata el slice activo
|
|
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
|
|
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. **
|
|
15
|
-
(degrada wiring sin evidencia; anota branch drift; fail-open).
|
|
15
|
+
1. **Averigua el modo y refresca**:
|
|
16
16
|
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
28
|
+
2. **Determina la siguiente acción**:
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
```bash
|
|
31
|
+
bash .claude/hooks/build/slice-ops.sh next-step
|
|
32
|
+
```
|
|
28
33
|
|
|
29
|
-
|
|
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`).
|
package/commands/build/slice.md
CHANGED
|
@@ -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
|
-
|
|
33
|
-
|
|
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
|
-
- `
|
|
49
|
-
|
|
50
|
-
- `
|
|
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ámalo 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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
95
|
-
|
|
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.
|
package/commands/build/work.md
CHANGED
|
@@ -6,7 +6,7 @@ tags: [build-harness, router, classify-and-act, trycore]
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
**Router puro.** Decide *qué carril* aplica y **delega** en la skill correspondiente. NO ejecuta el pipeline,
|
|
9
|
-
NO
|
|
9
|
+
NO transiciona el estado del slice, NO crea ramas. Codifica como **ruteo** (no política nueva) el *decision gate*
|
|
10
10
|
del micro-change y el default del Release Gate de `METODOLOGIA.md` (§2, §4). Si algo contradice la metodología,
|
|
11
11
|
**gana la metodología**.
|
|
12
12
|
|
|
@@ -28,12 +28,14 @@ Si `NOT_INSTALLED` → ejecuta `trycore-build init` y vuelve.
|
|
|
28
28
|
|
|
29
29
|
Aplica las reglas en orden:
|
|
30
30
|
|
|
31
|
-
0. **¿Greenfield sin caparazón construido?** —
|
|
32
|
-
`
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
0. **¿Greenfield sin caparazón construido?** — consulta los hechos de proyecto con
|
|
32
|
+
`bash .claude/hooks/build/slice-ops.sh status` (línea `hechos:`): si `project_kind` es
|
|
33
|
+
`greenfield` y `foundation_done` es `false` → **enruta directo a construir la épica caparazón**
|
|
34
|
+
por el carril **`building-a-slice`**. `status` no trae **cuál** es esa épica (`foundation.epic`
|
|
35
|
+
vive hoy en el fichero legacy, leído directo por `dor-dod-gatekeeper`); si no la encuentras o
|
|
36
|
+
está sin definir, enruta a `/build:onboard` (Fase 2c) o a discovery para definirla. Ninguna épica de
|
|
37
|
+
negocio pasa por delante. Brownfield, o caparazón no requerida (`foundation_done` ya `true`) →
|
|
38
|
+
esta regla es N/A.
|
|
37
39
|
Esta regla gatea **aperturas de slice** (épicas): el carril de **mantenimiento**
|
|
38
40
|
(`building-a-micro-change`, regla 1) no se bloquea — un typo/copy/config fix sigue su carril
|
|
39
41
|
normal aun sin caparazón construida.
|
|
@@ -70,7 +72,8 @@ modelo/invariantes de datos** → **deja de ser micro-change** y se enruta a **`
|
|
|
70
72
|
|
|
71
73
|
## Guardrails
|
|
72
74
|
|
|
73
|
-
- **Solo ruteo.** No corres el pipeline, no
|
|
75
|
+
- **Solo ruteo.** No corres el pipeline, no transicionas el estado (ni por `slice-ops.sh` ni por el
|
|
76
|
+
fichero), no creas ramas: eso es de las skills destino.
|
|
74
77
|
- **Ante la duda, épica.** Nunca degrades un cambio con límite duro a micro-change.
|
|
75
78
|
- **No dupliques gates** ni saltes el orden: las skills destino los gobiernan.
|
|
76
79
|
- **Agnóstico**: vocabulario genérico del arnés; lo específico del dominio entra por `/build:onboard`.
|