@trycore/spec-build-harness 0.14.0 → 0.14.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.
@@ -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.14.0",
5
+ "version": "0.14.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/INSTALL.md CHANGED
@@ -19,6 +19,26 @@ Hay **dos canales** de instalación: el **CLI npm** (canónico, recomendado para
19
19
  proyecto) y el **plugin nativo** de Claude Code (conveniencia a nivel usuario). Lee el
20
20
  [caveat de canales](#5-alternativa-plugin-nativo-con-caveat-de-canales) antes de elegir.
21
21
 
22
+ ## ¿Qué camino me toca?
23
+
24
+ Cuatro situaciones, cuatro caminos. Todos empiezan por §1 (CLI global) y §2 (requisitos).
25
+
26
+ | Tu situación | Cómo saberlo | Qué haces |
27
+ |---|---|---|
28
+ | **Proyecto nuevo**, sin el arnés | no existe `.claude/.build-harness-version` | §3 `init` → §4 `/build:onboard`. Listo. |
29
+ | **Proyecto existente** con el arnés, quieres ponerlo al día | existe la marca, con una versión vieja | §7 `update`. **No** re-corras `/build:onboard`: tu dominio ya está parametrizado. |
30
+ | **Proyecto existente en `legacy`**, quieres conectarlo al IA Hub | existe la marca y `runtime.mode` es `legacy` | §7 `update` **primero**, luego §9 → [guía runtime §4](docs/runtime/guia-modo-dual-y-migracion.md) |
31
+ | **Proyecto nuevo** que ya nace contra el IA Hub | — | §3 `init` **con las flags de runtime** (§9) → §4 `/build:onboard` |
32
+
33
+ Dos cosas que ahorran disgustos:
34
+
35
+ - **`init` y `update` son el mismo comando.** `init` detecta si ya hay instalación (por
36
+ `.claude/.build-harness-version`) y se comporta como `update`. Re-correrlo es seguro: **nunca**
37
+ pisa `build-state.json` ni `stack-allowlist.json`, que son del consumidor.
38
+ - **Conectar un proyecto al hub exige el arnés al día primero.** Los hooks del cliente runtime no
39
+ existen antes de la 0.9.0: si escribes el modo sobre una instalación vieja, queda escrito y nada
40
+ lo ejecuta.
41
+
22
42
  ---
23
43
 
24
44
  ## 1. Instalar el CLI global (npm)
@@ -87,7 +107,7 @@ Qué hace `init`:
87
107
  1. **Verifica requisitos duros** (a menos que uses `--skip-doctor`).
88
108
  2. **Siembra los assets** en rutas nativas de Claude Code:
89
109
  - `.claude/agents/build/` — 14 agentes.
90
- - `.claude/commands/opsx/` (10 comandos `/opsx:*`) y `.claude/commands/build/` (12 comandos: `/build:onboard`, `/build:reflect`, `/build:architect`, `/build:prototype`, `/build:slice`, `/build:release`, `/build:work`, `/build:resume`, `/build:front`, `/build:claim`, `/build:status`, `/build:escalate`).
110
+ - `.claude/commands/opsx/` (10 comandos `/opsx:*`) y `.claude/commands/build/` (13 comandos: `/build:onboard`, `/build:reflect`, `/build:architect`, `/build:prototype`, `/build:slice`, `/build:release`, `/build:work`, `/build:resume`, `/build:front`, `/build:claim`, `/build:status`, `/build:escalate`, `/build:epic`). Los cuatro últimos son la **superficie de agente del modo runtime** (§9): en `legacy`, `claim`, `escalate` y `epic` devuelven `rc 3` remitiendo al flujo del fichero, y `status` informa de que la fuente de verdad es `build-state.json`.
91
111
  - `.claude/skills/` — 16 skills (`building-a-slice`, `building-a-micro-change`, `releasing-a-version`, `managing-parallel-front`, `setup-architecture`, `prototyping-screens`, `openspec-*`).
92
112
  - `.claude/hooks/build/` — 19 hooks (bash + python; 6 son del cliente runtime opt-in — §9).
93
113
  3. **Siembra el estado**: `state/build-state.schema.json` y `state/README.md` se versionan;
@@ -166,9 +186,9 @@ Tras `init`, abre Claude Code en el proyecto y ejecuta:
166
186
  ```
167
187
 
168
188
  `/build:onboard` lee el PRD técnico (y `openspec/project.md` si existe) y, vía `AskUserQuestion`
169
- (proponiendo un valor "(Recomendado)" por punto), confirma y resuelve **6 puntos de extensión**
189
+ (proponiendo un valor "(Recomendado)" por punto), confirma y resuelve **7 puntos de extensión**
170
190
  que leen los agentes de calidad (`security-reviewer`, `stack-guardian`, `data-consistency-checker`,
171
- `ux-krug-reviewer`, `simple-design-reviewer`):
191
+ `ux-krug-reviewer`, `simple-design-reviewer`, `ux-fidelity-reviewer`):
172
192
 
173
193
  | Placeholder | Qué pregunta |
174
194
  |---|---|
@@ -178,6 +198,7 @@ que leen los agentes de calidad (`security-reviewer`, `stack-guardian`, `data-co
178
198
  | `{{SENSITIVE_DATA_CATEGORIES}}` | Categorías de datos sensibles / PII reguladas |
179
199
  | `{{SERVER_SIDE_SECRETS}}` | Secretos server-side que jamás van al cliente |
180
200
  | `{{HIGH_STAKES_DECISIONS}}` | Decisiones de alto impacto que exigen explicabilidad en la UI |
201
+ | `{{DESIGN_SOURCE}}` | Fuente de diseño / referencia visual (prototipo, export), o `N/A` si el proyecto no tiene UI |
181
202
 
182
203
  Qué resuelve:
183
204
 
@@ -308,12 +329,44 @@ modo `legacy` (default, sin cambios). Requiere un runtime corriendo y un **token
308
329
  emitido por un ADMIN en la consola del hub.
309
330
 
310
331
  ```bash
332
+ # Proyecto nuevo contra el hub, o proyecto existente YA actualizado (ver la tabla del principio)
311
333
  trycore-build init --runtime-url "https://tu-runtime.example.com" --runtime-token "<token>"
334
+
312
335
  trycore-build doctor # sección "Runtime": token, conectividad, lock, cola offline
313
336
  trycore-build status # sección "Runtime": conexión, proyección, contexto sincronizado
314
337
  trycore-build migrate --project-ref "<nombre-en-el-hub>" # bundle de estado histórico para un ADMIN
315
338
  ```
316
339
 
340
+ Sin `--runtime-mode`, el default con URL+token es **`dual`** (nunca `runtime`): el fichero local
341
+ sigue mandando y cada transición se espeja al servidor para poder compararlas. El corte directo a
342
+ `runtime` no está soportado por diseño.
343
+
344
+ ### Qué gana el proyecto al conectarse
345
+
346
+ | | Qué hace | Necesita el hub |
347
+ |---|---|---|
348
+ | **Contexto vivo** | El daemon refresca `/agent/context` cada `runtime.context_refresh_s` segundos (default 45, `0` desactiva) — una terminal abierta se entera de lo que pasa sin reiniciar | **No**, funciona hoy |
349
+ | **Épicas propuestas** | `/build:epic` propone la épica, el hub asigna el `EP-XXX` al aprobarla y `/build:epic --check` la proyecta a `epicas.md` | Sí (`trycore-ia-hub#113`/`#114`) |
350
+ | **Versión de grafo** | `claim` y la propuesta mandan la versión conocida; un `409` provoca refresco y reintento en vez de fallo | Sí (`trycore-ia-hub#115`) |
351
+ | **Reparto de trabajo** | `/build:claim` pide la siguiente tarea por lease; `/build:status` informa; `/build:escalate` registra un bloqueo | Sí |
352
+
353
+ Contra un hub que no implementa una de esas mitades, el cliente **degrada limpiamente**: un `404`
354
+ se aparta diciendo «esta instancia está sin soporte», sin reintentos en bucle, y un hub que no
355
+ versiona el grafo ve el mismo cuerpo de `claim` de siempre.
356
+
357
+ ### Configuración
358
+
359
+ `.claude/config/build-config.json`, bloque `runtime`:
360
+
361
+ | Clave | Default | Para qué |
362
+ |---|---|---|
363
+ | `mode` | `legacy` | `legacy` \| `dual` \| `runtime`. Un valor inválido cae a `legacy`, nunca a la red. |
364
+ | `stale_seconds` | `900` | A partir de cuántos segundos la proyección se considera stale |
365
+ | `context_refresh_s` | `45` | Cadencia del refresco de contexto del daemon; `0` lo desactiva |
366
+
367
+ Overrides por entorno, útiles para una sesión puntual sin editar el fichero:
368
+ `TRYCORE_RUNTIME_MODE`, `TRYCORE_CONTEXT_REFRESH_S`.
369
+
317
370
  Guía completa (los tres modos, cómo activar/verificar/volver a legacy, cuándo se corta) →
318
371
  [`docs/runtime/guia-modo-dual-y-migracion.md`](docs/runtime/guia-modo-dual-y-migracion.md).
319
372
 
package/README.md CHANGED
@@ -143,7 +143,7 @@ trycore-spec-build-harness/
143
143
  ├── agents/build/ ← 14 agentes revisores (segunda opinión, contexto limpio)
144
144
  ├── commands/
145
145
  │ ├── opsx/ ← 10 comandos /opsx:* (ciclo OpenSpec)
146
- │ └── build/ ← 12 comandos /build:* (onboard, reflect, architect, prototype, slice, release, work, resume, front, claim, status, escalate)
146
+ │ └── build/ ← 13 comandos /build:* (onboard, reflect, architect, prototype, slice, release, work, resume, front, claim, status, escalate, epic)
147
147
  ├── skills/ ← 16 skills (building-a-slice, building-a-micro-change, releasing-a-version, managing-parallel-front, setup-architecture, prototyping-screens, 10 openspec-*) + 3 plantillas *.workflow.js (opt-in, read-only)
148
148
  ├── hooks/build/ ← 19 hooks (gate-check, reflect-nudge, release-gate-nudge, scaffold-guard, gitflow-guard, stack-guard, statusline-bridge, context-monitor, reconcile-build-state, …) + 6 opt-in del cliente runtime (session-start, event-emitter, context-sync, heartbeat, dual-compare, session-stop — ver docs/hooks.md)
149
149
  ├── state/ ← máquina de estado legacy: build-state.json + schema + README
@@ -183,7 +183,50 @@ El core no menciona ningún dominio de cliente. Toda parametrización entra por
183
183
 
184
184
  ## Roadmap
185
185
 
186
- - ✅ **v0.10.0 (actual) — convergencia del inner loop** ataque a los seis multiplicadores de
186
+ - ✅ **v0.14.0 (actual) — contexto vivo, épicas propuestas y versión de grafo** (#61, #62, #63) —
187
+ tres capacidades del cliente del runtime que componen entre sí. **(1)** El daemon de heartbeat
188
+ refresca `GET /agent/context` con **cadencia propia** (`runtime.context_refresh_s`, default 45 s,
189
+ `0` la desactiva): antes la caché se hidrataba al arrancar la sesión y en cada `claim` —y el
190
+ `claim` ocurre una vez por slice—, así que una terminal abierta podía trabajar horas contra una
191
+ foto vieja; un refresco fallido **conserva** la caché anterior y la marca stale, porque quedarse
192
+ sin proyección dejaría al agente huérfano. **(2)** En modo `runtime` la épica se **propone** al
193
+ hub en vez de escribirse en el backlog: comando nuevo **`/build:epic`**, subcomandos
194
+ `propose-epic`/`epic-status`/`epic-writeback` y un **carril directo** en la cola offline que
195
+ sobrevive a quedarse sin red; el hub asigna el `EP-XXX` al aprobar y `docs/03-backlog/epicas.md`
196
+ pasa a ser **proyección del grafo**, nunca una fuente paralela —el agente pierde la potestad de
197
+ inventar identidades, que es lo que hacía colisionar las historias de dos épicas creadas el mismo
198
+ día—. **(3)** `claim` y la propuesta mandan la **versión de grafo** conocida, estampada *al
199
+ despachar* y no al encolar; un `409` por grafo rancio provoca refresco y un reintento en vez de
200
+ un fallo. `legacy` sigue siendo el default y no cambia. Las mitades de servidor de (2) y (3)
201
+ (`trycore-ia-hub#113`/`#114`/`#115`) están pendientes: contra un hub sin ellas el cliente degrada
202
+ a «instancia sin soporte». Total: **14 agentes**, **19 hooks**, **13 comandos `/build:*`**,
203
+ **16 skills**.
204
+ - ✅ **v0.13.0 — el grafo llega al hub con aristas** — las dependencias entre épicas viven como
205
+ prosa en `epicas.md` (`**Depende de**: EP-001`) y su lectura estaba delegada al modelo, con un
206
+ ejemplo en `/build:onboard` que mostraba `"depends_on": []`: las 41 épicas del piloto entraron
207
+ sin una sola arista y nadie lo detectó, porque un grafo vacío no falla, solo empobrece (#56).
208
+ `graph-bundle.py` gana un **extractor determinista** (`--from-docs` / `--print-deps`) que relee
209
+ el campo, con avisos de cobertura y traza de la fuente en `depends_on_source`. Además,
210
+ `unmapped[].original` viaja siempre como dict (#55): el hub valida el bundle entero con pydantic,
211
+ así que un escalar lo rechazaba **completo** antes de persistir nada.
212
+ - ✅ **v0.12.0 — la fase del slice llega al hub** (#52) — el hub modela la fase con eventos
213
+ `phase_advanced` de orden estricto y exige `phase == pr` para archivar, pero el cliente no emitía
214
+ ninguna: los slices quedaban «en construcción» con su PR ya mergeado. Subcomando
215
+ `slice-ops.sh phase <to>`, que emite la cadena que falte (idempotente, sin saltos), y **catch-up
216
+ automático** al archivar. Además, `outbox/rejected/` deja de ser invisible: la razón del rechazo
217
+ se persiste en el propio fichero y la muestran `slice-ops.sh status` y `trycore-build status`/`doctor`.
218
+ - ✅ **v0.11.1 — auto-cierre de releases en verde** — el cierre manual de una release con 6/6 gates
219
+ PASS era control-teatro: el hub la cierra solo (`closed_by: AUTO_6_OF_6_PASS`) y los casos
220
+ degradados siguen siendo decisión humana. Arreglado también que `release-ops.sh verdict` devolvía
221
+ **422 en todo reporte** (faltaba el campo `verdict` en mayúsculas junto a `status`).
222
+ - ✅ **v0.11.0 — drift cliente↔hub del primer piloto real** (#36–#44) — la primera migración contra
223
+ el hub en producción reveló un desajuste sistemático entre lo que el cliente emite y lo que el
224
+ hub valida: `claim` devolvía 422 en todo intento, la cola offline **nunca drenaba**, `fact`
225
+ emitía tipos fuera del catálogo, `claim --epic` se ignoraba en silencio y repartía otra épica, y
226
+ el import rechazaba 36 de 41 entradas del bundle histórico. Cada fix verificado contra el código
227
+ real del hub, con regresión anti-drift nueva que valida todo tipo encolable contra una réplica
228
+ literal de su catálogo.
229
+ - ✅ **v0.10.0 — convergencia del inner loop** — ataque a los seis multiplicadores de
187
230
  latencia de la verificación adversarial (iniciativa #31, issues #25–#30) **sin bajar el rigor**:
188
231
  **re-verificación incremental** con `verified_at_sha` por item de `wiring_checklist[]` (las
189
232
  pasadas 2+ re-ejecutan O(items tocados), no O(items totales)); **condición de parada** del bucle
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.0
1
+ 0.14.1
package/docs/commands.md CHANGED
@@ -136,7 +136,7 @@ protocolo del fichero (`/build:slice`) — no rompen el flujo normal si el proye
136
136
 
137
137
  | Slash command | Propósito |
138
138
  |---|---|
139
- | `/build:claim [EP-XXX]` | Pide la siguiente tarea al runtime (`POST /tasks/next`): sincroniza contexto, reporta hashes locales (detección de drift) y reclama por lease. Si trae `CHECKPOINT`, continúa desde ahí — **nunca reinicia** un slice de otro agente. |
139
+ | `/build:claim` | Pide la siguiente tarea al runtime (`POST /tasks/next`): sincroniza contexto, reporta hashes locales (detección de drift) y reclama por lease. Si trae `CHECKPOINT`, continúa desde ahí — **nunca reinicia** un slice de otro agente. **No acepta épica dirigida**: el hub reparte por orden de cola y `claim --epic` falla explícito con `rc 2` (issue #39 — el servidor ignoraba el `epic_code` y entregaba otra épica como si fuera la pedida, dejando un lease huérfano). |
140
140
  | `/build:status` | Informe de solo lectura: modo, slice activo, gates, wiring failing, versión de contexto, lease y cola de eventos pendientes. Nunca transiciona nada. |
141
141
  | `/build:escalate <razón>` | Registra un bloqueo en el runtime (`escalation_raised`) y devuelve la decisión a un humano — recortar, diferir o desbloquear **nunca** lo decide el modelo (mismo principio que la regla 8 de METODOLOGIA §10). |
142
142
  | `/build:epic` | Propone una épica **incremental de construcción** al hub y, tras la aprobación humana, la escribe en `docs/03-backlog/epicas.md` con el `EP-XXX` que asignó el hub (`/build:epic --check`). El agente **no elige** el código: elegirlo mirando el fichero es lo que hace colisionar a dos terminales. La descomposición inicial del backlog sigue siendo de discovery. |
@@ -24,6 +24,14 @@ trycore-build doctor # verifica requisitos y hooks
24
24
 
25
25
  Eso es el ciclo completo. Lo de abajo explica cada paso.
26
26
 
27
+ > **¿Conectado a un IA Hub?** El pipeline es **el mismo**; cambia dónde vive el estado y quién
28
+ > valida las transiciones. Añades cuatro comandos a la caja de herramientas — `/build:claim` (pide
29
+ > trabajo), `/build:status` (informe), `/build:epic` (propone una épica) y `/build:escalate`
30
+ > (registra un bloqueo) — y el arranque lleva las flags de runtime. En `legacy` no estorban:
31
+ > `claim`, `epic` y `escalate` devuelven `rc 3` y te remiten al flujo del fichero, y `status`
32
+ > informa de que la fuente de verdad es `build-state.json`.
33
+ > Instalación y activación → [`docs/runtime/guia-modo-dual-y-migracion.md`](runtime/guia-modo-dual-y-migracion.md).
34
+
27
35
  ---
28
36
 
29
37
  ## Requisitos duros
@@ -48,7 +56,9 @@ npm i -g @fission-ai/openspec @trycore/spec-build-harness
48
56
 
49
57
  ## 2 · `trycore-build init` (terminal)
50
58
 
51
- Desde la raíz de tu proyecto. Es **idempotente** (re-correrlo es seguro) y siembra: **14 agentes**, **16 skills** (`building-a-slice`, `building-a-micro-change`, `releasing-a-version`, `managing-parallel-front`, `setup-architecture`, `prototyping-screens` + 10 `openspec-*`), **10 comandos `/opsx:*`** + **9 comandos `/build:*`** (`onboard`, `reflect`, `architect`, `prototype`, `slice`, `release`, `work`, `resume`, `front`), **13 hooks**, el estado vacío `.claude/state/build-state.json` (gitignored, nunca se sobreescribe) y el bloque `<!-- BEGIN trycore-build-harness -->` en tu `CLAUDE.md` con `{{placeholders}}` sin resolver.
59
+ Desde la raíz de tu proyecto. Es **idempotente** (re-correrlo es seguro) y siembra: **14 agentes**, **16 skills** (`building-a-slice`, `building-a-micro-change`, `releasing-a-version`, `managing-parallel-front`, `setup-architecture`, `prototyping-screens` + 10 `openspec-*`), **10 comandos `/opsx:*`** + **13 comandos `/build:*`** (`onboard`, `reflect`, `architect`, `prototype`, `slice`, `release`, `work`, `resume`, `front` + `claim`, `status`, `escalate`, `epic`, que son la superficie del modo runtime), **19 hooks**, el estado vacío `.claude/state/build-state.json` (gitignored, nunca se sobreescribe) y el bloque `<!-- BEGIN trycore-build-harness -->` en tu `CLAUDE.md` con `{{placeholders}}` sin resolver.
60
+
61
+ > **¿Ya tenías el arnés instalado?** Entonces esto es un `update`, no un `init` — el CLI lo detecta solo por `.claude/.build-harness-version`. Corre `npm i -g @trycore/spec-build-harness@latest` y luego `trycore-build update`; **no** repitas `/build:onboard`, tu dominio ya está parametrizado. La tabla «¿Qué camino me toca?» de [`INSTALL.md`](../INSTALL.md) cubre los cuatro casos.
52
62
 
53
63
  ```bash
54
64
  trycore-build init
@@ -55,16 +55,47 @@ Si `--runtime-mode` no se especifica pero sí `--runtime-url`/`--runtime-token`,
55
55
  `dual` (nunca `runtime` — el corte directo a `runtime` sin pasar por `dual` no está soportado por
56
56
  diseño: es exactamente el "big bang" que el plan de migración evita).
57
57
 
58
- ## 4. Activar `dual` en un proyecto ya instalado (`update`)
58
+ ## 4. Activar `dual` en un proyecto ya instalado
59
59
 
60
- `trycore-build update` no acepta las flags de runtime directamente hoy vuelve a correr `init` con
61
- las mismas flags sobre el proyecto existente; es idempotente (no pisa `build-state.json` ni
62
- `stack-allowlist.json`, solo añade las credenciales y el modo):
60
+ Este es el camino normal: el proyecto ya lleva el arnés en `legacy` (tiene
61
+ `.claude/.build-harness-version` y su `build-state.json` con historial) y quieres que además
62
+ espeje al hub.
63
+
64
+ **`trycore-build update` no acepta las flags de runtime.** Vuelve a correr `init` con ellas sobre
65
+ el proyecto existente: `init` detecta la instalación previa y es **idempotente** — no pisa
66
+ `build-state.json` ni `stack-allowlist.json`, solo añade las credenciales y el modo.
63
67
 
64
68
  ```bash
65
- trycore-build init --runtime-url "..." --runtime-token "..." --runtime-mode dual
69
+ # 0. Parte de un arnés al día (si vienes de una versión vieja, primero esto)
70
+ npm i -g @trycore/spec-build-harness@latest
71
+ cd /ruta/al/proyecto
72
+ trycore-build update
73
+
74
+ # 1. Añade las credenciales y activa el espejo
75
+ trycore-build init --runtime-url "https://tu-runtime.example.com" \
76
+ --runtime-token "<token-del-ADMIN>" \
77
+ --runtime-mode dual
78
+
79
+ # 2. Comprueba
80
+ trycore-build doctor
81
+ trycore-build status
66
82
  ```
67
83
 
84
+ El paso 0 importa: las credenciales y el modo se escriben igual, pero los **hooks del cliente
85
+ runtime** (`session-start`, `event-emitter`, `context-sync`, `heartbeat`, `dual-compare`,
86
+ `session-stop`) solo existen desde la 0.9.0. Si el proyecto arrastra una versión anterior, el modo
87
+ queda escrito y **nada lo ejecuta**: sin ese `update` previo el espejo no espeja nada.
88
+
89
+ > **Salto grande de versión.** `update` refresca assets y schema, nunca tu estado. Si vienes de
90
+ > varias minor por detrás, corre `trycore-build status` después y mira el informe en vez de darlo
91
+ > por bueno: te dirá si la versión instalada quedó sincronizada y si el estado sigue validando.
92
+
93
+ ### ¿Y el historial que ya tienes?
94
+
95
+ Activar `dual` **no** sube tus slices archivados: el espejo solo cubre lo que ocurra de aquí en
96
+ adelante. Para que el hub conozca el pasado, ver §7 (`trycore-build migrate`). Es opcional — el
97
+ piloto funciona igual sin ello, solo que las proyecciones del servidor empiezan vacías.
98
+
68
99
  ## 5. Verificar que quedó bien
69
100
 
70
101
  ```bash
@@ -73,19 +104,68 @@ trycore-build status # resumen + sección "Runtime": conexión, proyección,
73
104
  ```
74
105
 
75
106
  Dentro de una sesión de Claude, `/build:status` da el mismo informe (más `next-step` derivado) y
76
- `/build:claim [EP-XXX]` reclama la siguiente tarea directamente contra el runtime.
107
+ `/build:claim` reclama la siguiente tarea directamente contra el runtime. **`claim` no acepta épica
108
+ dirigida**: el hub reparte por orden de cola y `claim --epic EP-XXX` falla explícito con `rc 2`
109
+ (issue #39 — el servidor ignoraba el `epic_code` en el cuerpo y entregaba la primera épica de su
110
+ cola como si fuera la pedida, dejando un lease huérfano que solo la consola admin libera). Si
111
+ necesitas dirigir una épica concreta, termina el slice en `dual`, pide al ADMIN apagar el espejo y
112
+ reclama lo que la cola sirva.
77
113
 
78
114
  ## 6. Qué cambia en el día a día
79
115
 
80
116
  **Nada que tengas que operar tú a mano.** Las skills (`building-a-slice`, `releasing-a-version`,
81
117
  `managing-parallel-front`) conducen exactamente el mismo pipeline; por debajo, en vez de
82
- leer/escribir `build-state.json` directamente, llaman a `slice-ops.sh`/`release-ops.sh` (13
83
- subcomandos: `claim`, `gate`, `wiring`, `progress`, `checkpoint`, `submit`, `archive`, `fact`,
84
- `propose-asset`, `status`, `escalate`, y del lado release `verdict`/`front-integration`) — nunca
118
+ leer/escribir `build-state.json` directamente, llaman a `slice-ops.sh`/`release-ops.sh` — nunca
85
119
  arman peticiones a mano. En `dual`, cada transición también se **espeja** al servidor con la
86
120
  identidad del fichero (que sigue mandando). Si el espejo falla (red caída, servidor rechaza), el
87
121
  trabajo local **nunca se bloquea** — se avisa y queda como discrepancia para el comparador.
88
122
 
123
+ `slice-ops.sh` tiene **17 subcomandos** (los conducen las skills; los listamos para que puedas
124
+ leer un log o depurar, no para que los teclees):
125
+
126
+ | Subcomando | Para qué |
127
+ |---|---|
128
+ | `mode` | imprime `legacy`\|`dual`\|`runtime` |
129
+ | `claim` | reclama trabajo (el hub reparte por orden de cola; sin épica dirigida) |
130
+ | `next-step` | deriva la siguiente acción desde la caché de proyección |
131
+ | `phase` | reporta la fase del pipeline (emite la cadena que falte; idempotente) |
132
+ | `gate` | reporta un veredicto del slice |
133
+ | `submit` · `archive` | entrega y archiva el slice |
134
+ | `wiring` | siembra y actualiza el checklist de cableado |
135
+ | `progress` · `checkpoint` | bitácora y continuidad tras una caída |
136
+ | `fact` | reporta un hecho de proyecto confirmado |
137
+ | `propose-asset` | propone un documento al plano de contexto (lo publica un ADMIN) |
138
+ | `propose-epic` · `epic-status` · `epic-writeback` | proponen una épica, consultan su estado y proyectan la aprobada a `epicas.md` |
139
+ | `status` · `escalate` | informe local del agente y registro de un bloqueo |
140
+
141
+ Del lado release, `release-ops.sh` añade `verdict` y `front-integration`.
142
+
143
+ **Códigos de salida** (sobre estos ramifica la prosa de las skills): `0` ok · `2` uso · `3` legacy ·
144
+ `4` sin slice · `5` offline · `6` rechazado · `7` sin trabajo.
145
+
146
+ ### Lo que aportó la 0.14.0
147
+
148
+ - **El contexto ya no se queda congelado** (#61). El daemon de heartbeat refresca `GET /agent/context`
149
+ cada `runtime.context_refresh_s` segundos (default **45**; `0` lo desactiva; override
150
+ `TRYCORE_CONTEXT_REFRESH_S`), **solo en modo `runtime`**. Antes, la caché se hidrataba al arrancar
151
+ la sesión y en cada `claim` — y el `claim` ocurre una vez por slice, así que una terminal abierta
152
+ podía trabajar horas contra una foto vieja. Si el refresco falla, la caché anterior **se conserva**
153
+ y se marca stale (lo dice `/build:status`): quedarse sin proyección dejaría al agente huérfano.
154
+ - **Las épicas se proponen, no se escriben** (#62). En `runtime`, el agente ya no elige el `EP-XXX`:
155
+ lo propone al hub con **`/build:epic`**, un humano lo aprueba en la consola, el hub asigna la
156
+ identidad y `/build:epic --check` proyecta la épica aprobada a `docs/03-backlog/epicas.md`. El
157
+ fichero pasa a ser una **proyección del grafo**, nunca una fuente paralela. La propuesta viaja por
158
+ un carril de la cola offline, así que sobrevive a quedarte sin red.
159
+ - **Una terminal desactualizada ya no puede hacer daño** (#63). `claim` y la propuesta de épica
160
+ mandan la versión de grafo que conocen; si está rancia, el hub responde `409` y el cliente
161
+ refresca y reintenta una vez en vez de fallar. `/build:status` avisa si tu versión va por detrás.
162
+
163
+ > **Dependencia externa:** las mitades de servidor de #62 y #63 (`trycore-ia-hub#113`, `#114`, `#115`)
164
+ > **todavía no existen**. Contra un hub que no las implementa, `/build:epic` recibe un `404` y aparta
165
+ > la propuesta diciendo «esta instancia está sin soporte», sin reintentos en bucle; y un hub que no
166
+ > versiona el grafo ve exactamente el mismo cuerpo de `claim` de siempre. El refresco de contexto
167
+ > (#61) **sí funciona hoy**: no necesita nada nuevo del servidor.
168
+
89
169
  **El comparador dual** (`hooks/build/dual-compare.sh`, hook `Stop`) corre después de cada turno:
90
170
  compara la proyección del servidor contra el fichero local (épica, fase, gates ya resueltos) y, si
91
171
  divergen, lo escala vía `/build:escalate` — nunca bloquea el cierre de sesión. Es el instrumento
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trycore/spec-build-harness",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "Arnés agéntico de construcción de Trycore para Claude Code: pipeline de dos loops (slice por épica + release gate) con gates de calidad, estado compartido y OpenSpec. Compañero de @trycore/spec-product-flow. Agnóstico al proyecto.",
5
5
  "type": "module",
6
6
  "bin": {