@trycore/spec-build-harness 0.2.0 → 0.4.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.
@@ -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.2.0",
5
+ "version": "0.4.0",
6
6
  "description": "Arnés de construcción de dos loops (slice por épica + release gate) para Claude Code, con gates de calidad, estado compartido y OpenSpec. Compañero de @trycore/spec-product-flow. Agnóstico al proyecto.",
7
7
  "author": {
8
8
  "name": "Trycore",
package/GOVERNANCE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Gobernanza del arnés de construcción `.claude`
2
2
 
3
- Versión: ver `.claude/.build-harness-version` (`1.0.0`). El arnés es **soporte cognitivo vivo**:
3
+ Versión del paquete: ver `.claude/.build-harness-version`. El arnés es **soporte cognitivo vivo**:
4
4
  evoluciona con el modelo y el proyecto. No es "instalar y olvidar".
5
5
 
6
6
  ## Componentes y dueño
@@ -9,14 +9,15 @@ evoluciona con el modelo y el proyecto. No es "instalar y olvidar".
9
9
  | Contexto | sección Construcción de CLAUDE.md, `openspec/project.md` | raíz / `openspec/` |
10
10
  | Estado | `build-state.json` (+schema, README) | `.claude/state/` |
11
11
  | Agentes | 10 agentes de build | `.claude/agents/build/` |
12
- | Hooks | settings.json + 6 scripts | `.claude/settings.json`, `.claude/hooks/build/` |
13
- | Skill | `building-a-slice` + 10 references | `.claude/skills/building-a-slice/` |
12
+ | Hooks | settings.json + 8 scripts | `.claude/settings.json`, `.claude/hooks/build/` |
13
+ | Skill | `building-a-slice` (+10 refs) · `building-a-micro-change` (carril ligero de mantenimiento) | `.claude/skills/` |
14
14
  | Config | allowlist de stack | `.claude/config/stack-allowlist.json` |
15
15
 
16
16
  ## Fases de activación (`harness_phase`)
17
17
  - **authoring** (actual): aún no hay `package.json`. Activos: `gitflow-guard`, `load-build-state`,
18
18
  `coherence-flag`; agentes de coherencia/stack/DoR/DoD operan sobre texto. Los hooks de
19
- lint/typecheck/stack(package.json)/tests están **armados** pero inertes (`[ -f package.json ] || exit 0`).
19
+ lint/typecheck/stack(package.json)/tests están **armados** pero inertes (`[ -f package.json ] || exit 0`);
20
+ `scaffold-guard` permite (sin slice en fases de código que bloquear) y `reflect-nudge` calla (sin historial que reflexionar).
20
21
  - **active**: al aparecer `package.json`, `load-build-state.sh` cambia la fase y los hooks armados
21
22
  empiezan a disparar solos. Sin intervención manual.
22
23
 
@@ -40,6 +41,16 @@ Editar `stack-allowlist.json` SOLO si la sección de requisitos técnicos del PR
40
41
  ### Bitácora de excepciones de stack
41
42
  - _(vacío)_ — registrar fecha, dependencia, justificación y aprobador.
42
43
 
44
+ ## Bitácora de cambios de metodología
45
+ Cambios a la política de construcción (unidad de trabajo, gates, DoR/DoD). Aprueba el DRI; van por PR.
46
+
47
+ - **2026-06-02 · v0.4.0** — Carril `building-a-micro-change` + DoR proporcional. El mantenimiento que
48
+ no es producto nuevo (typo, bump de dep permitida, copy/config/docs, fix de pocas líneas) deja de
49
+ modelarse como épica y usa un carril ligero (`fix/*`|`chore/*` → PR, sin `active_slice`), con
50
+ límites duros que lo escalan a épica (dep nueva, API nueva, lógica de dominio/datos). El DoR escala
51
+ el nº de escenarios G/W/T con `complejidad` en vez de exigir 3–5 fijos. La regla "épica = unidad"
52
+ se mantiene intacta para producto. Origen: auditoría del arnés vs. crítica de sobre-configuración.
53
+
43
54
  ## Extensiones futuras (no implementadas)
44
55
  - **Construcción en paralelo** de slices con `superpowers:using-git-worktrees` + orquestación
45
56
  multi-worktree (hoy el modelo es **secuencial** por decisión de proyecto). El `build-state.json`
package/INSTALL.md CHANGED
@@ -11,7 +11,7 @@ Es el **compañero** de [`@trycore/spec-product-flow`](https://www.npmjs.com/pac
11
11
  | CLI (bin) | `trycore-build` |
12
12
  | Plugin | `trycore-spec-build-harness` |
13
13
  | Marketplace | `trycore-build` |
14
- | Versión | `0.1.0` |
14
+ | Versión | `0.3.0` |
15
15
 
16
16
  Hay **dos canales** de instalación: el **CLI npm** (canónico, recomendado para operar en un
17
17
  proyecto) y el **plugin nativo** de Claude Code (conveniencia a nivel usuario). Lee el
@@ -28,7 +28,7 @@ npm install -g @trycore/spec-build-harness
28
28
  Esto expone el binario `trycore-build`. Comprueba la versión:
29
29
 
30
30
  ```bash
31
- trycore-build --version # → 0.1.0
31
+ trycore-build --version # → 0.3.0
32
32
  trycore-build --help
33
33
  ```
34
34
 
@@ -85,9 +85,9 @@ Qué hace `init`:
85
85
  1. **Verifica requisitos duros** (a menos que uses `--skip-doctor`).
86
86
  2. **Siembra los assets** en rutas nativas de Claude Code:
87
87
  - `.claude/agents/build/` — 10 agentes.
88
- - `.claude/commands/opsx/` (10 comandos `/opsx:*`) y `.claude/commands/build/` (`/build:onboard`).
88
+ - `.claude/commands/opsx/` (10 comandos `/opsx:*`) y `.claude/commands/build/` (`/build:onboard`, `/build:reflect`).
89
89
  - `.claude/skills/` — 12 skills (`building-a-slice`, `releasing-a-version`, `openspec-*`).
90
- - `.claude/hooks/build/` — 6 hooks bash.
90
+ - `.claude/hooks/build/` — 8 hooks bash.
91
91
  3. **Siembra el estado**: `state/build-state.schema.json` y `state/README.md` se versionan;
92
92
  `state/build-state.json` se siembra **vacío y nunca se sobrescribe** (va al `.gitignore`).
93
93
  4. **Siembra `config/stack-allowlist.json`** (artefacto del consumidor; lo puebla `/build:onboard`).
@@ -184,6 +184,11 @@ Qué resuelve:
184
184
 
185
185
  Si un punto no aplica, se registra explícitamente "no aplica" (no se deja como `{{...}}`).
186
186
 
187
+ > **Durante la construcción** (no en la instalación), el comando `/build:reflect` captura las
188
+ > convenciones aprendidas de cada slice cerrado en el bloque `trycore-build-learnings` de
189
+ > `CLAUDE.md` —con tu aprobación— y lo **sugiere** el hook `reflect-nudge.sh` al cerrar sesión.
190
+ > Detalle en `docs/commands.md`.
191
+
187
192
  ---
188
193
 
189
194
  ## 5. Alternativa: plugin nativo (con caveat de canales)
@@ -228,7 +233,7 @@ Ambos paquetes coexisten en el mismo `.claude/` **sin colisión**, porque usan *
228
233
 
229
234
  | | Discovery — `spec-product-flow` | Construcción — `spec-build-harness` |
230
235
  |---|---|---|
231
- | Comandos | `/trycore:*` | `/opsx:*` + `/build:onboard` |
236
+ | Comandos | `/trycore:*` | `/opsx:*` + `/build:onboard` + `/build:reflect` |
232
237
  | Marca de versión | `.trycore-version` | `.build-harness-version` |
233
238
  | Bloque en `CLAUDE.md` | `<!-- BEGIN trycore-vertical -->` | `<!-- BEGIN trycore-build-harness -->` |
234
239
 
package/METODOLOGIA.md CHANGED
@@ -28,7 +28,7 @@ Ambos paquetes **coexisten en el mismo `.claude/` sin colisión**, con namespace
28
28
 
29
29
  | Eje | Discovery (`spec-product-flow`) | Construcción (`spec-build-harness`) |
30
30
  |---|---|---|
31
- | Comandos | `trycore/` (`/trycore:*`) | `opsx/` + `build/` (`/opsx:*`, `/build:onboard`) |
31
+ | Comandos | `trycore/` (`/trycore:*`) | `opsx/` + `build/` (`/opsx:*`, `/build:onboard`, `/build:reflect`) |
32
32
  | Sentinela de versión | `.trycore-version` | `.build-harness-version` |
33
33
  | Bloque en `CLAUDE.md` | `<!-- BEGIN trycore-vertical -->` | `<!-- BEGIN trycore-build-harness -->` |
34
34
 
@@ -90,6 +90,15 @@ Consecuencia operativa: **la unidad de construcción es la épica, no la HU suel
90
90
  épica = un OpenSpec change = una rama = un PR. Las HU que cubre la épica son su *alcance interno* y
91
91
  se listan en `active_slice.hus[]`. Construir por HU individual es sobre-ingeniería.
92
92
 
93
+ > **Carril micro-change (mantenimiento).** Esta regla rige la **construcción de producto**. El
94
+ > **mantenimiento** que no es capacidad nueva —un typo, un ajuste de copy/config/docs, un bump de
95
+ > dependencia ya permitida, o un fix de pocas líneas sin nueva capacidad— **no se modela como
96
+ > épica**: usa el carril ligero de la skill `building-a-micro-change` (`fix/*`|`chore/*` → cambio →
97
+ > PR, sin abrir `active_slice`). No es una excepción a la regla, sino mantenimiento fuera de su
98
+ > alcance. **Límites duros:** si el cambio añade una dependencia nueva, crea un endpoint/API nuevo, o
99
+ > toca lógica de dominio o el modelo/invariantes de datos, **deja de ser micro-change** y se escala a
100
+ > épica. Ante la duda, es una épica.
101
+
93
102
  ---
94
103
 
95
104
  ## 3. Pipeline del inner loop, fase por fase (con sus gates)
@@ -117,7 +126,10 @@ Una épica **no entra a construcción** hasta cumplir todo (lo valida `dor-dod-g
117
126
  - La épica tiene ≥1 HU; todas las que entran se listan en `hus[]`.
118
127
  - Frontmatter completo en cada `docs/04-historias/HU-XXX.md` (`id, titulo, epica, prioridad,
119
128
  complejidad, estado`) con `estado: lista`.
120
- - AC en Given/When/Then por HU: 3–5 escenarios con happy + error + edge.
129
+ - AC en Given/When/Then por HU, **proporcional a `complejidad`** (cubre los modos de fallo que
130
+ *realmente existen*, no una cuota fija): `trivial`/baja → **1–2** (happy + error/edge crítico si
131
+ existe); `media` → **3** (happy + error + edge); `alta` → **3–5** (cobertura completa). Si existe
132
+ rama de error/edge, debe tener su escenario.
121
133
  - Cada HU pasa los 6 criterios **INVEST**.
122
134
  - Dependencias resueltas (las épicas/HU de las que depende están en `history[]` o no bloquean).
123
135
  - Cabe en el stack del PRD (no requiere tecnología fuera de `stack-allowlist.json`).
@@ -351,8 +363,10 @@ El arnés **no escribe** en `docs/`; cuando una HU no cumple DoR, devuelve el tr
351
363
 
352
364
  ## 10. Reglas duras (resumen)
353
365
 
354
- 1. La **épica** es la unidad de construcción: un slice = una épica = un change = una rama = un PR.
355
- Las HU son alcance interno (`hus[]`).
366
+ 1. La **épica** es la unidad de construcción de **producto**: un slice = una épica = un change = una
367
+ rama = un PR. Las HU son alcance interno (`hus[]`). El **mantenimiento** que no es producto nuevo
368
+ (typo, bump, copy, infra/docs, fix de pocas líneas) usa el carril `building-a-micro-change`, fuera
369
+ de esta regla; cruzar un límite duro (dep nueva, API nueva, dominio/datos) lo escala a épica.
356
370
  2. El **esqueleto que camina** nunca deja de caminar: nada de capas horizontales que se juntan al
357
371
  final; `journey_smoke` verde en cada épica.
358
372
  3. **Dos loops sin duplicación**: gates baratos por épica (inner), gates pesados una vez por release
package/README.md CHANGED
@@ -45,7 +45,7 @@ trycore-build init
45
45
  | `trycore-build update` | Refresca assets y schema tras `npm update -g`. No pisa estado ni allowlist. |
46
46
  | `trycore-build status` | Muestra el estado de la instalación y la fase activa del arnés. |
47
47
  | `trycore-build uninstall` | Quita el arnés. **Preserva** `.claude/state/` y `.claude/config/`. |
48
- | `trycore-build doctor` | Verifica requisitos externos (`openspec`/`python3`/`git`), hooks ejecutables y canales. |
48
+ | `trycore-build doctor` | Verifica requisitos externos (`openspec`/`python3`/`git`), hooks ejecutables y canales; reporta slices sin reflexionar y sugiere **LSP** en stacks tipados. |
49
49
 
50
50
  Flags de `init`: `--copy` (copiar en vez de symlinkear), `--yes` (no interactivo), `--skip-doctor`, `--force-init`, `--stack <deps>`, `--pkg-manager <pm>`, `--runtime <semver>`, `--prd-path <path>`.
51
51
 
@@ -59,7 +59,7 @@ El arnés modela la construcción como dos ciclos anidados, cada uno con su skil
59
59
  DoR → OpenSpec change → TDD → journey-smoke → api/data → DoD → PR + archive
60
60
  ```
61
61
 
62
- Por cada épica del backlog: se valida el **Definition of Ready**, se abre un *change* en OpenSpec, se desarrolla con **TDD**, se corre el smoke del journey, se verifican contratos de API y consistencia de datos, se valida el **Definition of Done** y se cierra con PR + archivado del change.
62
+ Por cada épica del backlog: se valida el **Definition of Ready**, se abre un *change* en OpenSpec, se desarrolla con **TDD**, se corre el smoke del journey, se verifican contratos de API y consistencia de datos, se valida el **Definition of Done** y se cierra con PR + archivado del change. Tras archivar, `/build:reflect` puede capturar las convenciones aprendidas del slice (**ciclo autocorrectivo**, opcional; lo sugiere el hook `reflect-nudge.sh`).
63
63
 
64
64
  ### Outer loop — `releasing-a-version` (una vez por release)
65
65
 
@@ -70,6 +70,7 @@ Gate de release que corre **una sola vez por versión** sobre el conjunto de sli
70
70
  | Command | Propósito |
71
71
  |---|---|
72
72
  | `/build:onboard` | Onboarding capa 2: lee el PRD, pregunta por PII/IA/determinismo/secretos, resuelve `{{placeholders}}` y escribe la auto-memory. |
73
+ | `/build:reflect` | Reflexión post-slice: tras archivar, propone convenciones aprendidas / errores recurrentes al bloque `trycore-build-learnings` de `CLAUDE.md` (tras tu aprobación) y marca el slice como reflexionado. |
73
74
  | `/opsx:explore` | Explora el dominio / specs antes de abrir un change. |
74
75
  | `/opsx:new` | Crea un nuevo OpenSpec change. |
75
76
  | `/opsx:continue` | Retoma un change en curso. |
@@ -91,9 +92,9 @@ trycore-spec-build-harness/
91
92
  ├── agents/build/ ← 10 agentes revisores (segunda opinión, contexto limpio)
92
93
  ├── commands/
93
94
  │ ├── opsx/ ← 10 comandos /opsx:* (ciclo OpenSpec)
94
- │ └── build/ ← /build:onboard
95
+ │ └── build/ ← /build:onboard, /build:reflect
95
96
  ├── skills/ ← 12 skills (building-a-slice, releasing-a-version, 10 openspec-*)
96
- ├── hooks/build/ ← 6 hooks bash (gate-check, gitflow-guard, stack-guard, …)
97
+ ├── hooks/build/ ← 8 hooks bash (gate-check, reflect-nudge, scaffold-guard, gitflow-guard, stack-guard, …)
97
98
  ├── state/ ← máquina de estado: build-state.json + schema + README
98
99
  ├── config/ ← stack-allowlist.template.json (artefacto del consumidor)
99
100
  ├── src/ + dist/ ← CLI trycore-build (init/update/status/uninstall/doctor)
@@ -123,7 +124,9 @@ El core no menciona ningún dominio de cliente. Toda parametrización entra por
123
124
 
124
125
  ## Roadmap
125
126
 
126
- - ✅ **v0.1.0 (actual)** — arnés de dos loops (`building-a-slice` + `releasing-a-version`), 10 agentes, comandos `/opsx:*` + `/build:onboard`, 12 skills, 6 hooks, máquina de estado `build-state.json`, allowlist de stack, CLI `trycore-build` (init/update/status/uninstall/doctor) y plugin nativo. Compañero de `@trycore/spec-product-flow`.
127
+ - ✅ **v0.1.0** — arnés de dos loops (`building-a-slice` + `releasing-a-version`), 10 agentes, comandos `/opsx:*` + `/build:onboard`, 12 skills, 6 hooks, máquina de estado `build-state.json`, allowlist de stack, CLI `trycore-build` (init/update/status/uninstall/doctor) y plugin nativo. Compañero de `@trycore/spec-product-flow`.
128
+ - ✅ **v0.2.0** — scaffold como "Paso 1 fundamental": gate de proyecto `scaffold.confirmed` (confirmación **explícita**, no auto), Fase 0 en `building-a-slice`, criterio duro de DoR y hook `scaffold-guard.sh`. El arnés **exige** el scaffold pero **no lo genera**.
129
+ - ✅ **v0.3.0 (actual)** — **ciclo autocorrectivo** (hook `reflect-nudge.sh` + comando `/build:reflect`: propone convenciones aprendidas al bloque `trycore-build-learnings` de `CLAUDE.md` tras tu aprobación; campos `reflected`/`reflected_at`) y **LSP opt-in** (`docs/customization/lsp-extensions.md` + sugerencia en `doctor` para stacks tipados). Total: **8 hooks**; comandos `/opsx:*` + `/build:onboard` + `/build:reflect`.
127
130
 
128
131
  ## Licencia
129
132
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.4.0
@@ -23,7 +23,9 @@ cumplen; lista cada una con ✓/✗:
23
23
  2. Tiene **al menos una HU** asociada y todas se enumeran en `hus[]`.
24
24
  3. **Cada HU** de la épica: frontmatter YAML completo (`id, titulo, epica, prioridad, complejidad,
25
25
  estado`) y `estado: lista`.
26
- 4. **Cada HU**: AC en formato **Given/When/Then**, 3–5 escenarios con happy + error + edge.
26
+ 4. **Cada HU**: AC en formato **Given/When/Then**, **proporcional a `complejidad`** (`trivial`/baja
27
+ 1–2; `media` → 3; `alta` → 3–5), cubriendo los modos de fallo que existen (happy + error/edge
28
+ reales). No exijas 3–5 a una HU trivial; sí exige el escenario de toda rama de error/edge que exista.
27
29
  5. **Cada HU** pasa los 6 criterios **INVEST** (si dudas, invoca al agente `invest-validator`).
28
30
  6. Dependencias declaradas (otras épicas/HU) están en `history[]` del estado o marcadas done.
29
31
  7. El alcance de la épica cabe en el stack declarado del PRD (allowlist) (no exige tecnología fuera de la allowlist).
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: "BUILD: Reflect"
3
+ description: Reflexión post-slice (ciclo autocorrectivo). Tras cerrar/archivar un slice, detecta convenciones aprendidas y errores recurrentes, PROPONE viñetas para el bloque de aprendizajes de CLAUDE.md (se aplican SOLO con tu aprobación) y estampa el slice como reflexionado. Lo dispara el hook reflect-nudge.sh, pero puedes invocarlo cuando quieras.
4
+ category: Workflow
5
+ tags: [reflexion, conocimiento, ciclo-autocorrectivo, build-harness, trycore]
6
+ ---
7
+
8
+ Captura el **conocimiento tribal** de un slice recién cerrado antes de que se pierda. Un hook bash no
9
+ puede razonar qué se aprendió; **tú (el modelo) sí**. Tu trabajo: detectar convención nueva o error
10
+ recurrente, **proponérselo al usuario**, y —solo con su aprobación— escribirlo al bloque
11
+ `trycore-build-learnings` de `CLAUDE.md`. Nunca escribes sin confirmación.
12
+
13
+ Esta es la mejora del **pilar "ciclo autocorrectivo"**: el hook `reflect-nudge.sh` (evento `Stop`)
14
+ solo **sugiere** ejecutar esto cuando hay slices archivados sin reflexionar; el razonamiento vive
15
+ aquí, en el modelo.
16
+
17
+ ---
18
+
19
+ ## Preflight
20
+
21
+ ```bash
22
+ test -f .claude/.build-harness-version || echo "NOT_INSTALLED"
23
+ command -v python3 >/dev/null 2>&1 || echo "NO_PYTHON3"
24
+ ```
25
+
26
+ **Si `NOT_INSTALLED`:**
27
+
28
+ > Este proyecto no tiene el arnés de construcción instalado. Ejecuta primero `trycore-build init`.
29
+
30
+ Stop aquí si no está instalado o si falta `python3`.
31
+
32
+ ---
33
+
34
+ ## Fase 1: Identificar slices sin reflexionar
35
+
36
+ Lee `.claude/state/build-state.json` y filtra las entradas de `history[]` con `reflected != true`:
37
+
38
+ ```bash
39
+ python3 - <<'PY'
40
+ import json
41
+ d = json.load(open(".claude/state/build-state.json"))
42
+ pend = [h for h in (d.get("history") or []) if h.get("reflected") is not True]
43
+ for h in pend:
44
+ print(h.get("epica"), "·", h.get("openspec_change"), "·", h.get("branch"), "·", ",".join(h.get("hus") or []))
45
+ print("TOTAL", len(pend))
46
+ PY
47
+ ```
48
+
49
+ **Si `TOTAL 0`:** informa "No hay slices pendientes de reflexión ✅" y termina. No inventes trabajo.
50
+
51
+ Si hay varios, procésalos **de uno en uno** (el más reciente primero), o pregunta al usuario cuál.
52
+
53
+ ---
54
+
55
+ ## Fase 2: Recolectar señal del slice
56
+
57
+ Para el slice elegido, reúne evidencia **real** (no especules):
58
+
59
+ 1. **El change OpenSpec**: `openspec show <openspec_change>` y/o los archivos del change
60
+ (proposal/tasks). ¿Qué se decidió y qué tasks costaron más?
61
+ 2. **El diff de la rama**: `git log --oneline <branch>` y `git diff main...<branch> --stat` (si la
62
+ rama o sus commits existen). ¿Qué patrón de código se repitió? ¿Qué se reescribió varias veces?
63
+ 3. **La sesión actual**: qué hooks se dispararon más de una vez (p.ej. `stack-guard.sh`,
64
+ `gitflow-guard.sh`, `lint-typecheck.sh`), qué gates retrocedieron, qué correcciones se repitieron.
65
+
66
+ Con esa evidencia, detecta **0–3** aprendizajes candidatos, de estas categorías:
67
+
68
+ - **Convención nueva**: un patrón de código/estructura/nombrado que se repitió y conviene fijar para
69
+ el equipo (ej. "los handlers validan en el borde con el esquema X antes de tocar el dominio").
70
+ - **Error recurrente**: algo que un gate o hook atrapó >1 vez y vale la pena prevenir
71
+ (ej. "recordar correr migraciones antes del journey-smoke").
72
+ - **Fricción del propio arnés**: si el aprendizaje es sobre el arnés (un gate confuso, un paso que
73
+ sobra), NO lo escribas a CLAUDE.md — anótalo como candidato para `internal/skills/auditar-arnes`
74
+ y dilo al usuario.
75
+
76
+ Si no hay nada que valga la pena fijar, es válido **no proponer aprendizajes** (solo estampar, Fase 5).
77
+
78
+ ---
79
+
80
+ ## Fase 3: Proponer (con aprobación explícita)
81
+
82
+ Por **cada** aprendizaje candidato, usa **AskUserQuestion** para que el usuario lo apruebe, edite o
83
+ descarte. Presenta cada uno como una **viñeta concreta y accionable**, lista para CLAUDE.md. Ejemplo
84
+ de opciones: "Agregar tal cual" (Recomendado) · "Editar" · "Descartar".
85
+
86
+ **Reglas:**
87
+ - Una viñeta = una convención. Concreta, verificable, en presente imperativo. Nada vago.
88
+ - No propongas conocimiento que ya está en el bloque (léelo antes). No dupliques.
89
+ - Si un aprendizaje es **personal/cross-proyecto** (no de equipo), ofrécelo para **auto-memory**
90
+ (`type: project`/`user`) en vez de CLAUDE.md — pregunta cuál destino.
91
+
92
+ ---
93
+
94
+ ## Fase 4: Aplicar SOLO lo aprobado al bloque de aprendizajes
95
+
96
+ Para las viñetas aprobadas, **inserta** (no reemplaces) dentro del bloque marcado de `CLAUDE.md`,
97
+ **justo antes** de `<!-- END trycore-build-learnings -->`:
98
+
99
+ ```markdown
100
+ - (EP-XXX) <viñeta aprobada>
101
+ ```
102
+
103
+ - Prefija cada viñeta con la épica del slice `(EP-XXX)` para trazabilidad.
104
+ - **NO toques nada fuera del bloque** `trycore-build-learnings`. En particular, NO modifiques el
105
+ bloque `trycore-build-harness` ni sus `{{placeholders}}` de dominio (eso es de `/build:onboard`).
106
+ - Si el bloque `trycore-build-learnings` no existe (instalación vieja), pide correr
107
+ `trycore-build update` antes de continuar.
108
+ - Las viñetas son cambio de equipo: se revisan en PR como cualquier otra línea de `CLAUDE.md`.
109
+
110
+ ---
111
+
112
+ ## Fase 5: Estampar el slice como reflexionado
113
+
114
+ Marca el/los slice(s) procesado(s) en `history[]` para que el nudge calle. Usa la hora UTC real:
115
+
116
+ ```bash
117
+ NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
118
+ python3 - "$NOW" "<openspec_change>" <<'PY'
119
+ import json, sys
120
+ now, change = sys.argv[1], sys.argv[2]
121
+ p = ".claude/state/build-state.json"
122
+ d = json.load(open(p))
123
+ for h in d.get("history") or []:
124
+ if h.get("openspec_change") == change:
125
+ h["reflected"] = True
126
+ h["reflected_at"] = now
127
+ json.dump(d, open(p, "w"), indent=2, ensure_ascii=False)
128
+ print("estampado:", change)
129
+ PY
130
+ ```
131
+
132
+ Estampa **aunque no haya habido aprendizajes** (reflexionar y no encontrar nada también cierra el
133
+ ciclo). Repite por cada slice procesado.
134
+
135
+ ---
136
+
137
+ ## Fase 6: Confirmación
138
+
139
+ ```
140
+ ## ✓ Reflexión completada
141
+
142
+ Slice: <EP-XXX> (<openspec_change>)
143
+ Aprendizajes: <N agregados a CLAUDE.md> · <M a auto-memory> · <0 si solo se estampó>
144
+ Estampado: reflected=true
145
+
146
+ Pendientes de reflexión restantes: <K>
147
+ ```
148
+
149
+ Si quedan slices pendientes (`K > 0`), ofrece continuar con el siguiente.
150
+
151
+ ---
152
+
153
+ ## Guardrails
154
+
155
+ - **Aprobación explícita siempre.** Nunca escribes a CLAUDE.md sin que el usuario apruebe cada
156
+ viñeta. Espeja la regla del scaffold: el arnés propone, el humano confirma.
157
+ - **Evidencia, no invención.** Cada aprendizaje sale del change/diff/sesión reales. Si no hay
158
+ evidencia, no propongas.
159
+ - **No reescribas.** Inserta viñetas; jamás borres ni reemplaces aprendizajes previos del equipo.
160
+ - **No toques el paquete.** No modifiques agentes/skills/hooks del arnés (eso es mantenimiento, ver
161
+ `internal/skills/auditar-arnes`).
162
+ - **Estampa siempre** al terminar, para no re-molestar con el mismo slice.
163
+ - Si algo aquí contradice `METODOLOGIA.md`, **gana la metodología**.
@@ -66,6 +66,11 @@ export async function doctor(opts) {
66
66
  const st = JSON.parse(fs.readFileSync(t.stateFile, 'utf8'));
67
67
  const confirmed = st?.scaffold?.confirmed === true;
68
68
  console.log(`Scaffold (Paso 1): ${confirmed ? '✓ confirmado' : '✗ pendiente — confírmalo en building-a-slice (Fase 0) antes de fases de código'}`);
69
+ const hist = Array.isArray(st?.history) ? st.history : [];
70
+ const pend = hist.filter((h) => h?.reflected !== true).length;
71
+ if (pend > 0) {
72
+ console.log(`Reflexión: ${pend} slice(s) archivado(s) sin reflexionar — ejecuta /build:reflect`);
73
+ }
69
74
  }
70
75
  catch {
71
76
  console.log('Scaffold (Paso 1): ⚠ build-state.json malformado');
@@ -83,6 +88,14 @@ export async function doctor(opts) {
83
88
  console.log(' Si además instalaste el plugin nativo, la cadena de comando es idéntica:');
84
89
  console.log(' Claude Code deduplica y el hook dispara UNA vez. No requiere acción.');
85
90
  }
91
+ // Sugerencia LSP para stacks tipados (informativa, NO es requisito) [pilar LSP]
92
+ const typed = detectTypedStack(targetDir);
93
+ if (typed) {
94
+ console.log('');
95
+ console.log(`ℹ Stack tipado detectado (${typed}). Considera activar LSP para precisión`);
96
+ console.log(' semántica (seguir símbolos/referencias como un compilador, mejor ROI que grep');
97
+ console.log(' en código tipado) — ver docs/customization/lsp-extensions.md');
98
+ }
86
99
  console.log('═══════════════════════════════════════');
87
100
  if (dep.missingHard.length > 0) {
88
101
  console.error(`\n✗ Faltan requisitos duros: ${dep.missingHard.join(', ')}. Instálalos antes de operar el arnés.`);
@@ -90,3 +103,25 @@ export async function doctor(opts) {
90
103
  }
91
104
  console.log('\n✓ Requisitos satisfechos.');
92
105
  }
106
+ /** Señales de stack tipado donde LSP rinde (informativo; nunca un requisito). */
107
+ function detectTypedStack(dir) {
108
+ const direct = [
109
+ ['pom.xml', 'Java/Maven'],
110
+ ['build.gradle', 'Java/Gradle'],
111
+ ['build.gradle.kts', 'Kotlin/Gradle'],
112
+ ['composer.json', 'PHP/Composer'],
113
+ ['tsconfig.json', 'TypeScript'],
114
+ ];
115
+ for (const [f, label] of direct) {
116
+ if (fs.existsSync(path.join(dir, f)))
117
+ return label;
118
+ }
119
+ try {
120
+ if (fs.readdirSync(dir).some((e) => e.endsWith('.csproj') || e.endsWith('.sln')))
121
+ return 'C#/.NET';
122
+ }
123
+ catch {
124
+ /* dir ilegible: sin sugerencia */
125
+ }
126
+ return null;
127
+ }
@@ -15,6 +15,8 @@ import { captureStack, renderAllowlist } from '../lib/stack-prompt.js';
15
15
  import { checkDeps } from './doctor.js';
16
16
  const BEGIN = '<!-- BEGIN trycore-build-harness ';
17
17
  const END = '<!-- END trycore-build-harness -->';
18
+ const LEARN_BEGIN = '<!-- BEGIN trycore-build-learnings -->';
19
+ const LEARN_END = '<!-- END trycore-build-learnings -->';
18
20
  const GI_BEGIN = '# ── trycore-build-harness (auto)';
19
21
  const GI_END = '# ── /trycore-build-harness';
20
22
  export async function init(opts) {
@@ -87,20 +89,41 @@ function syncClaudeMdBlock(targetDir, version) {
87
89
  const template = fs
88
90
  .readFileSync(ASSETS.templateClaudeMd, 'utf8')
89
91
  .replace(/\{\{BUILD_HARNESS_VERSION\}\}/g, version);
90
- const lines = template.split('\n');
91
- const b = lines.findIndex((l) => l.includes(BEGIN));
92
- const e = lines.findIndex((l) => l.includes(END));
93
- if (b === -1 || e === -1)
94
- throw new Error('CLAUDE.md.template sin markers BEGIN/END trycore-build-harness');
95
- const blockContent = lines.slice(b, e + 1).join('\n');
92
+ // CLAUDE.md nuevo: el template completo ya incluye ambos bloques (arnés + aprendizajes).
96
93
  if (!fs.existsSync(t.claudeMd)) {
97
94
  fs.writeFileSync(t.claudeMd, template, 'utf8');
98
95
  console.log(' ✓ CLAUDE.md creado desde template');
96
+ return;
97
+ }
98
+ // Bloque del arnés: siempre upsert (refresca versión/contenido en cada init/update).
99
+ const harnessBlock = extractBlock(template, BEGIN, END);
100
+ const action = upsertMarkedBlock({ beginMarker: BEGIN, endMarker: END, blockContent: harnessBlock, filePath: t.claudeMd });
101
+ console.log(` ✓ Bloque trycore-build-harness ${action === 'replaced' ? 'actualizado' : 'anexado'} en CLAUDE.md`);
102
+ // Bloque de aprendizajes: SOLO sembrar si falta. NUNCA reemplazar — un update no debe
103
+ // borrar las convenciones que /build:reflect acumuló.
104
+ const current = fs.readFileSync(t.claudeMd, 'utf8');
105
+ if (!current.includes(LEARN_BEGIN)) {
106
+ const learnBlock = extractBlock(template, LEARN_BEGIN, LEARN_END);
107
+ upsertMarkedBlock({ beginMarker: LEARN_BEGIN, endMarker: LEARN_END, blockContent: learnBlock, filePath: t.claudeMd });
108
+ console.log(' ✓ Bloque trycore-build-learnings sembrado en CLAUDE.md');
99
109
  }
100
- else {
101
- const action = upsertMarkedBlock({ beginMarker: BEGIN, endMarker: END, blockContent, filePath: t.claudeMd });
102
- console.log(` ✓ Bloque trycore-build-harness ${action === 'replaced' ? 'actualizado' : 'anexado'} en CLAUDE.md`);
110
+ }
111
+ /** Extrae el bloque [begin..end] inclusive de un texto (para sembrar desde el template). */
112
+ function extractBlock(template, begin, end) {
113
+ const lines = template.split('\n');
114
+ const b = lines.findIndex((l) => l.includes(begin));
115
+ if (b === -1)
116
+ throw new Error(`CLAUDE.md.template sin marker de inicio: ${begin}`);
117
+ let e = -1;
118
+ for (let i = b; i < lines.length; i++) {
119
+ if (lines[i].includes(end)) {
120
+ e = i;
121
+ break;
122
+ }
103
123
  }
124
+ if (e === -1)
125
+ throw new Error(`CLAUDE.md.template sin marker de fin: ${end}`);
126
+ return lines.slice(b, e + 1).join('\n');
104
127
  }
105
128
  function syncGitignoreBlock(targetDir, mode) {
106
129
  const t = targetPaths(targetDir);
@@ -51,6 +51,10 @@ export async function status(opts) {
51
51
  console.log(' Slice activo: ninguno');
52
52
  }
53
53
  console.log(` Historial: ${(st.history ?? []).length} slice(s) · Releases: ${(st.releases ?? []).length}`);
54
+ const sinReflexionar = (st.history ?? []).filter((h) => h?.reflected !== true).length;
55
+ if (sinReflexionar > 0) {
56
+ console.log(` Sin reflexionar: ${sinReflexionar} slice(s) — ejecuta /build:reflect`);
57
+ }
54
58
  }
55
59
  catch {
56
60
  console.log(' ⚠ build-state.json malformado');
@@ -30,8 +30,9 @@ export async function uninstall(opts) {
30
30
  // 2) Marca de versión
31
31
  if (fs.existsSync(t.versionFile))
32
32
  fs.rmSync(t.versionFile, { force: true });
33
- // 3) Bloques marcados
33
+ // 3) Bloques marcados (arnés + aprendizajes + .gitignore)
34
34
  removeMarkedBlock(t.claudeMd, BEGIN, END);
35
+ removeMarkedBlock(t.claudeMd, LEARN_BEGIN, LEARN_END);
35
36
  removeMarkedBlock(t.gitignore, GI_BEGIN, GI_END);
36
37
  // 4) Hooks + permisos del arnés en settings.json (por cadena exacta)
37
38
  removeHarnessSettings(t.settingsFile);
@@ -40,6 +41,8 @@ export async function uninstall(opts) {
40
41
  }
41
42
  const BEGIN = '<!-- BEGIN trycore-build-harness ';
42
43
  const END = '<!-- END trycore-build-harness -->';
44
+ const LEARN_BEGIN = '<!-- BEGIN trycore-build-learnings -->';
45
+ const LEARN_END = '<!-- END trycore-build-learnings -->';
43
46
  const GI_BEGIN = '# ── trycore-build-harness (auto)';
44
47
  const GI_END = '# ── /trycore-build-harness';
45
48
  function isSymlink(p) {
@@ -20,7 +20,7 @@ const HOOK_SPECS = [
20
20
  { event: 'PreToolUse', matcher: 'Bash', scripts: ['gitflow-guard.sh'] },
21
21
  { event: 'PreToolUse', matcher: 'Write|Edit|MultiEdit', scripts: ['stack-guard.sh', 'scaffold-guard.sh'] },
22
22
  { event: 'PostToolUse', matcher: 'Write|Edit|MultiEdit', scripts: ['lint-typecheck.sh', 'coherence-flag.sh'] },
23
- { event: 'Stop', matcher: '.*', scripts: ['build-gate-check.sh'] },
23
+ { event: 'Stop', matcher: '.*', scripts: ['build-gate-check.sh', 'reflect-nudge.sh'] },
24
24
  ];
25
25
  /** Permisos MÍNIMOS y enumerados [H11]. Nunca permisos amplios (mcp__*, additionalDirectories…). */
26
26
  const MIN_PERMISSIONS = [
package/docs/commands.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Esta referencia cubre los **dos planos de operación** del arnés de construcción:
4
4
 
5
- 1. El **CLI `trycore-build`** (binario Node, paquete `@trycore/spec-build-harness` v0.1.0) — instala, actualiza, diagnostica y desinstala el arnés en el proyecto consumidor. Captura el **stack mecánico**.
6
- 2. Los **slash commands de Claude Code** (`/opsx:*` + `/build:onboard`) — operan el pipeline de dos loops y resuelven la parametrización **semántica** del dominio.
5
+ 1. El **CLI `trycore-build`** (binario Node, paquete `@trycore/spec-build-harness` v0.3.0) — instala, actualiza, diagnostica y desinstala el arnés en el proyecto consumidor. Captura el **stack mecánico**.
6
+ 2. Los **slash commands de Claude Code** (`/opsx:*` + `/build:onboard` + `/build:reflect`) — operan el pipeline de dos loops, resuelven la parametrización **semántica** del dominio y capturan el conocimiento aprendido por slice.
7
7
 
8
8
  > **División de responsabilidades del onboarding (dos capas).** Un binario Node **no puede** escribir la auto-memory de Claude. Por eso `trycore-build init` siembra archivos y captura el stack mecánico (lenguaje/deps, package manager, runtime, ruta del PRD), y el slash command `/build:onboard` —ejecutado por Claude— lee el PRD, pregunta por PII / capa de servicios externos-IA / capa determinista / secretos / decisiones de alto impacto, resuelve los `{{placeholders}}` del bloque marcado de `CLAUDE.md` y escribe la auto-memory.
9
9
 
@@ -52,7 +52,7 @@ Solo `init` y `update` aceptan flags. `status`, `uninstall` y `doctor` toman ún
52
52
 
53
53
  ## 2. Slash commands de Claude Code
54
54
 
55
- El canal CLI namespacea por subcarpeta: `.claude/commands/opsx/` → `/opsx:*` y `.claude/commands/build/` → `/build:onboard`.
55
+ El canal CLI namespacea por subcarpeta: `.claude/commands/opsx/` → `/opsx:*` y `.claude/commands/build/` → `/build:onboard` y `/build:reflect`.
56
56
 
57
57
  ### `/opsx:*` — pipeline OpenSpec
58
58
 
@@ -75,6 +75,12 @@ El canal CLI namespacea por subcarpeta: `.claude/commands/opsx/` → `/opsx:*` y
75
75
  |---|---|
76
76
  | `/build:onboard` | Parametriza el dominio del arnés: capa de servicios externos/IA, lógica determinista, PII, secretos y decisiones de alto impacto. Lee el PRD, pregunta vía AskUserQuestion, rellena el bloque marcado de `CLAUDE.md` y escribe la auto-memory. **Complementa** a `trycore-build init` (que ya sembró archivos y el stack mecánico). |
77
77
 
78
+ ### `/build:reflect` — reflexión post-slice (ciclo autocorrectivo)
79
+
80
+ | Slash command | Propósito |
81
+ |---|---|
82
+ | `/build:reflect` | Tras archivar un slice, detecta **convenciones aprendidas** / errores recurrentes (a partir del change, el diff y la sesión) y **propone** viñetas para el bloque `trycore-build-learnings` de `CLAUDE.md`; las aplica **solo tras tu aprobación** y estampa el slice como reflexionado (`reflected: true`). Lo **sugiere** el hook `reflect-nudge.sh` al cerrar sesión, pero puedes invocarlo cuando quieras. No toca el bloque de dominio ni sus `{{placeholders}}` (eso es de `/build:onboard`). |
83
+
78
84
  ---
79
85
 
80
86
  ## 3. Caveat de canales (CLI vs. plugin)
@@ -84,7 +90,7 @@ El arnés se distribuye por **dos canales** que coexisten, pero **namespacean di
84
90
  | Aspecto | Canal **CLI** (canónico) | Canal **Plugin** nativo |
85
91
  |---|---|---|
86
92
  | Instalación | `npm i -g @trycore/spec-build-harness` → `trycore-build init` | `/plugin marketplace add <repo-github>` → `/plugin install trycore-spec-build-harness@trycore-build` |
87
- | Namespace de comandos | Por subcarpeta: `/opsx:*` y `/build:onboard` | Por nombre del plugin: `/trycore-spec-build-harness:*` (por diseño de Claude Code) |
93
+ | Namespace de comandos | Por subcarpeta: `/opsx:*`, `/build:onboard` y `/build:reflect` | Por nombre del plugin: `/trycore-spec-build-harness:*` (por diseño de Claude Code) |
88
94
  | Referencia a agentes | Por su nombre (p. ej. `build-orchestrator`) | Bajo el nombre del plugin |
89
95
  | Cross-references internas | ✔ Escritas para este canal (skills invocan `/opsx:*`, agentes por nombre) | Pueden no resolver según están escritas |
90
96
  | Recomendación | **Usar este canal para operar un proyecto** | Conveniencia a nivel usuario |
@@ -0,0 +1,90 @@
1
+ # Extensión LSP del arnés (opt-in, agnóstico)
2
+
3
+ > Guía de personalización para **consumidores** de `@trycore/spec-build-harness`. Explica cómo
4
+ > aprovechar **LSP** (Language Server Protocol) para que Claude Code navegue tu código con
5
+ > **precisión de símbolo** en vez de búsqueda de texto. **Todo lo de aquí es opcional.** El core es
6
+ > 100 % agnóstico: ningún gate, skill ni agente **depende** de LSP. Sin LSP, el arnés funciona igual
7
+ > navegando con `grep`/`glob` + lectura dirigida.
8
+
9
+ ## TL;DR
10
+
11
+ - **LSP = precisión de compilador** para Claude Code: seguir la definición exacta de un símbolo,
12
+ distinguir homónimos, listar referencias cruzadas. En monorepos tipados, `grep` devuelve ruido y
13
+ falsos positivos; LSP no.
14
+ - **Mayor ROI en lenguajes tipados**: Java/Kotlin, C#, C/C++, PHP, TypeScript. `trycore-build doctor`
15
+ detecta señales de estos stacks y **sugiere** activarlo (es una nota informativa, **no** un
16
+ requisito).
17
+ - **Complementa, no reemplaza** a MCP: LSP es precisión **intra-repo** (símbolos del código); MCP es
18
+ conectividad a sistemas **externos** (Jira, BD, telemetría). Ver `mcp-extensions.md`.
19
+ - Regla de oro: **opt-in**. Lo habilitas **tú** en tu entorno; el arnés solo lo recomienda donde
20
+ rinde.
21
+
22
+ ## Por qué LSP y no solo `grep`
23
+
24
+ El arnés define cada gate por su **resultado verificable**, no por la herramienta que lo produce
25
+ (misma filosofía que las extensiones MCP). Pero la fase de **exploración/navegación** del código —que
26
+ alimenta a los agentes de coherencia, diseño y arquitectura— mejora drásticamente con precisión
27
+ semántica:
28
+
29
+ | Tarea de navegación | Con `grep`/`glob` | Con LSP habilitado |
30
+ |---|---|---|
31
+ | "¿Dónde se define este método?" | Coincidencias de texto; homónimos mezclados. | La **definición exacta**, sin ruido. |
32
+ | "¿Quién usa este símbolo?" | Falsos positivos (comentarios, strings, nombres parecidos). | Referencias **reales** del compilador. |
33
+ | "Trazar símbolo → test" | Lectura manual encadenada. | Salto directo definición↔referencias. |
34
+ | "¿Hay duplicación / código muerto?" | Difícil de afirmar con texto. | Referencias vacías = candidato a muerto. |
35
+
36
+ Por eso, en stacks tipados grandes, LSP es la inversión de mayor ROI para la fase de exploración: el
37
+ subagente que **lee** (mapea el subsistema) devuelve una síntesis más fiel, protegiendo la ventana de
38
+ contexto para la fase de **edición**.
39
+
40
+ ## Cuándo rinde (por stack)
41
+
42
+ | Stack | Señal que detecta `doctor` | Por qué rinde LSP |
43
+ |---|---|---|
44
+ | Java / Kotlin | `pom.xml`, `build.gradle(.kts)` | Tipado fuerte + dependencias profundas (Spring): `grep` se ahoga. |
45
+ | C# / .NET | `*.csproj`, `*.sln` | Símbolos y namespaces densos; refactors guiados por tipo. |
46
+ | C / C++ | (no autodetectado; CMake/Make ambiguos) | Macros y headers hacen inútil la búsqueda de texto. |
47
+ | PHP | `composer.json` | Autoload + magia dinámica: seguir definiciones reales ayuda. |
48
+ | TypeScript | `tsconfig.json` | Tipos estructurales; el LSP de TS distingue lo que `grep` no. |
49
+
50
+ En lenguajes **dinámicos sin tipos** (p.ej. scripts sueltos), el ROI de LSP es menor; ahí `grep`/`glob`
51
+ suele bastar y el arnés no lo sugiere.
52
+
53
+ ## Cómo activarlo (en TU entorno)
54
+
55
+ LSP es una capacidad **del cliente Claude Code**, no algo que el arnés instale o versione:
56
+
57
+ 1. Instala el **language server** de tu stack en tu máquina/imagen de dev (cada lenguaje tiene el
58
+ suyo; sigue la doc del servidor que elijas).
59
+ 2. Habilita la integración LSP en **tu** configuración de Claude Code (a nivel proyecto o usuario),
60
+ no en el arnés.
61
+ 3. A partir de ahí, Claude Code puede navegar con precisión semántica durante la fase de exploración
62
+ de `building-a-slice` y para los agentes `coherence-three-way`, `simple-design-reviewer` y
63
+ `stack-guardian`.
64
+
65
+ > El arnés nunca levanta el servidor por ti ni versiona su configuración: la frontera (qué servidor,
66
+ > con qué permisos) es **tuya**.
67
+
68
+ ## Relación con MCP
69
+
70
+ No son lo mismo y conviven:
71
+
72
+ - **LSP** → precisión de **símbolo** dentro del repo (definiciones, referencias, jerarquía de tipos).
73
+ - **MCP** → conectividad a **sistemas externos** fuera del repo (tickets, BD, navegador, carga).
74
+
75
+ Un mismo proyecto puede usar ambos: LSP para entender el código tipado, MCP (opt-in) para acelerar
76
+ gates que necesitan evidencia externa. Detalle de MCP por gate en `mcp-extensions.md` y el mapa
77
+ operativo en `skills/building-a-slice/references/mcp-map.md`.
78
+
79
+ ## Reglas de uso
80
+
81
+ 1. **Opt-in.** El arnés solo **sugiere** LSP (vía `doctor`); habilitarlo es decisión del consumidor.
82
+ 2. **No es dependencia.** Ningún gate exige LSP. Si no está, la navegación cae a `grep`/`glob` y los
83
+ gates se cierran igual por su resultado verificable.
84
+ 3. **Portabilidad.** No escribas en agentes/skills/hooks del arnés instrucciones que **requieran**
85
+ LSP: manténlo como acelerador documentado.
86
+
87
+ ---
88
+
89
+ **Fuente de verdad.** Si algo aquí contradice la metodología Trycore (`METODOLOGIA.md`), **gana la
90
+ metodología**. Para extensiones MCP (y la relación MCP↔LSP por gate), ver `mcp-extensions.md`.
@@ -6,6 +6,9 @@
6
6
  > gate, skill ni agente **depende** de un MCP concreto. Si tu entorno no expone ningún MCP, el arnés
7
7
  > funciona igual cubriendo cada fase con sus alternativas CLI/test.
8
8
 
9
+ > **LSP tiene guía dedicada.** Este documento cubre sobre todo **MCP**. Para **LSP** (precisión de
10
+ > símbolo en stacks tipados: por qué, cuándo y cómo), ver **`lsp-extensions.md`**.
11
+
9
12
  ## TL;DR
10
13
 
11
14
  - Los gates **pueden apoyarse** en MCP/LSP como **aceleradores**, nunca como dependencia dura.
@@ -76,9 +76,9 @@ trycore-build init
76
76
  - **10 agentes** en `.claude/agents/build/` (build-orchestrator, dor-dod-gatekeeper,
77
77
  security-reviewer, simple-design-reviewer, ux-krug-reviewer, coherence-three-way, stack-guardian,
78
78
  api-contract-tester, data-consistency-checker, change-epic-coherence).
79
- - **Comandos** `/opsx:*` (10) en `.claude/commands/opsx/` + `/build:onboard` en `.claude/commands/build/`.
79
+ - **Comandos** `/opsx:*` (10) en `.claude/commands/opsx/` + `/build:onboard` y `/build:reflect` en `.claude/commands/build/`.
80
80
  - **12 skills** en `.claude/skills/` (`building-a-slice`, `releasing-a-version`, 10 `openspec-*`).
81
- - **6 hooks** bash en `.claude/hooks/build/` + permisos mínimos en `settings.json`.
81
+ - **8 hooks** bash en `.claude/hooks/build/` + permisos mínimos en `settings.json`.
82
82
  - **Estado**: `.claude/state/build-state.json` (sembrado **vacío** y **nunca** sobreescrito; va al
83
83
  `.gitignore`), más el schema y el README versionados.
84
84
  - **Config**: `.claude/config/stack-allowlist.json` (artefacto del consumidor; lo siembra el CLI y
@@ -136,10 +136,14 @@ trycore-build doctor
136
136
  Comprueba:
137
137
 
138
138
  - Los **3 requisitos duros** (`git`, `python3`, `openspec`) — **falla con exit 1** si falta alguno.
139
- - Que los **6 hooks** tengan bit ejecutable (si no, sugiere `trycore-build init --copy` o `chmod +x`).
139
+ - Que los **8 hooks** tengan bit ejecutable (si no, sugiere `trycore-build init --copy` o `chmod +x`).
140
140
  - **Doble canal**: si detecta hooks del arnés en `settings.json` (canal CLI) y además instalaste el
141
141
  plugin, te recuerda que la cadena de comando es idéntica en ambos canales y Claude Code
142
142
  **deduplica** → el hook dispara **una sola vez**. No requiere acción.
143
+ - **Reflexión y LSP (informativo, no falla):** reporta cuántos slices archivados están **sin
144
+ reflexionar** (sugiere `/build:reflect`) y, si detecta un stack tipado (`pom.xml`,
145
+ `build.gradle`, `*.csproj`, `tsconfig.json`…), sugiere activar **LSP**
146
+ (ver `docs/customization/lsp-extensions.md`).
143
147
 
144
148
  Si ves `✓ Requisitos satisfechos.`, continúa.
145
149
 
@@ -211,6 +215,10 @@ Notas clave del inner loop:
211
215
  (bloquea commits/push directos a `main` — integras solo por **PR**).
212
216
  - **El enlace change↔épica** va en el bloque `## Trazabilidad` del `proposal.md`, **nunca** en
213
217
  frontmatter YAML (rompe `openspec validate`).
218
+ - **Reflexión (ciclo autocorrectivo).** Tras archivar el slice, `/build:reflect` puede capturar las
219
+ convenciones aprendidas / errores recurrentes en el bloque `trycore-build-learnings` de
220
+ `CLAUDE.md` (con tu aprobación). Lo **sugiere** el hook `reflect-nudge.sh` al cerrar la sesión;
221
+ es opcional y no bloquea.
214
222
  - Objetivo: **≤ ~20 min por épica**, y producto que **camina end-to-end en todo momento**.
215
223
 
216
224
  ---
@@ -252,6 +260,7 @@ trycore-build doctor # paso 3
252
260
  # en Claude Code:
253
261
  /build:onboard # paso 4
254
262
  skill building-a-slice # EP-XXX: DoR → /opsx:new → TDD → smoke → DoD → PR # paso 5
263
+ /build:reflect # opcional: captura aprendizajes del slice recién archivado
255
264
  # y cuando cierres una línea de release del Story Map:
256
265
  skill releasing-a-version # paso 6
257
266
  ```
package/docs/hooks.md CHANGED
@@ -1,23 +1,25 @@
1
1
  # Hooks del arnés de construcción
2
2
 
3
- Este documento describe los **6 hooks** que `@trycore/spec-build-harness` instala en el proyecto del consumidor. Todos viven en `hooks/build/` (un script bash por hook) y se cablean a los eventos de Claude Code mediante una **cadena de comando única**, idéntica en ambos canales de instalación.
3
+ Este documento describe los **8 hooks** que `@trycore/spec-build-harness` instala en el proyecto del consumidor. Todos viven en `hooks/build/` (un script bash por hook) y se cablean a los eventos de Claude Code mediante una **cadena de comando única**, idéntica en ambos canales de instalación.
4
4
 
5
- Los hooks son el sistema nervioso del arnés: vigilan GitFlow y el stack declarado (bloqueantes), inyectan el estado del build al iniciar la sesión, delegan estilo/typecheck a herramientas, y recuerdan validar trazabilidad y gates abiertos. No reemplazan a los agentes ni a las skills; los **complementan** liberando capacidad de razonamiento del modelo y poniendo barandillas mecánicas donde un olvido cuesta caro.
5
+ Los hooks son el sistema nervioso del arnés: vigilan GitFlow, el stack declarado y el scaffold (bloqueantes), inyectan el estado del build al iniciar la sesión, delegan estilo/typecheck a herramientas, y recuerdan validar trazabilidad, gates abiertos y reflexionar al cerrar un slice. No reemplazan a los agentes ni a las skills; los **complementan** liberando capacidad de razonamiento del modelo y poniendo barandillas mecánicas donde un olvido cuesta caro.
6
6
 
7
7
  ---
8
8
 
9
- ## Resumen de los 6 hooks
9
+ ## Resumen de los 8 hooks
10
10
 
11
11
  | Hook | Evento | Matcher | Qué hace | ¿Bloqueante? |
12
12
  |---|---|---|---|---|
13
13
  | `load-build-state.sh` | `SessionStart` | `startup\|clear\|compact` | Inyecta al contexto la rama, la fase del arnés, el slice activo y los gates abiertos; sincroniza `harness_phase`. | No |
14
14
  | `gitflow-guard.sh` | `PreToolUse` | `Bash` | Enforce GitHub Flow estricto sobre `git commit` / `git push`. | **Sí (exit 2)** |
15
15
  | `stack-guard.sh` | `PreToolUse` | `Write\|Edit\|MultiEdit` | Bloquea dependencias en `package.json` fuera de la allowlist del stack del PRD. | **Sí (exit 2)** |
16
+ | `scaffold-guard.sh` | `PreToolUse` | `Write\|Edit\|MultiEdit` | Bloquea escribir código de slice (fases `red…data`) si el scaffold no está confirmado (`scaffold.confirmed`). | **Sí (exit 2)** |
16
17
  | `lint-typecheck.sh` | `PostToolUse` | `Write\|Edit\|MultiEdit` | Corre prettier/eslint/tsc sobre el archivo `.ts`/`.tsx` editado. | No |
17
18
  | `coherence-flag.sh` | `PostToolUse` | `Write\|Edit\|MultiEdit` | Recuerda validar la trazabilidad de un `proposal.md` de OpenSpec recién tocado. | No |
18
19
  | `build-gate-check.sh` | `Stop` | `.*` | Al cerrar el turno, avisa si el slice activo tiene gates abiertos. | No |
20
+ | `reflect-nudge.sh` | `Stop` | `.*` | Al cerrar el turno, sugiere `/build:reflect` si hay slice(s) archivado(s) sin reflexionar (`reflected != true`). | No |
19
21
 
20
- > Dos bloqueantes (`gitflow-guard`, `stack-guard`) y cuatro informativos. Los bloqueantes usan `exit 2`: Claude Code devuelve el `stderr` al modelo y aborta la herramienta; el resto siempre sale `0`.
22
+ > Tres bloqueantes (`gitflow-guard`, `stack-guard`, `scaffold-guard`) y cinco informativos. Los bloqueantes usan `exit 2`: Claude Code devuelve el `stderr` al modelo y aborta la herramienta; el resto siempre sale `0`.
21
23
 
22
24
  ---
23
25
 
@@ -58,7 +60,11 @@ Tras editar un archivo `.ts`/`.tsx`, delega el estilo a las herramientas para no
58
60
 
59
61
  - `prettier --write` sobre el archivo.
60
62
  - `eslint --fix` sobre el archivo (primeras 20 líneas de salida a `stderr`).
61
- - `tsc --noEmit` y filtra los errores que mencionan el archivo editado.
63
+ - `tsc --noEmit --incremental` con un `tsBuildInfoFile` persistente, y filtra los errores que mencionan el archivo editado.
64
+
65
+ **Typecheck incremental (desde v0.3.x).** `tsc` siempre recorre todo el grafo de tipos del proyecto, no solo el archivo editado. Para que ese coste no escale con el tamaño del repo en cada edición, el hook usa `--incremental` con un `tsBuildInfoFile` en `node_modules/.cache/trycore-build/tsbuildinfo`: el **primer** typecheck de la sesión paga `O(repo)` y cada edición posterior paga `~O(delta)`. El cache vive bajo `node_modules/` (que el consumidor casi siempre tiene gitignored), así que no contamina el repo. El typecheck va envuelto en `timeout 60` **si** `timeout`/`gtimeout` (coreutils) está disponible; si no, corre sin límite (al ser no bloqueante, agotar el tiempo solo omite el reporte de esa edición).
66
+
67
+ > **Caveat `composite`:** en proyectos con `composite: true` en `tsconfig.json`, el typecheck canónico es `tsc -b`. Aquí `--noEmit` prevalece (igual que antes) y los posibles errores de configuración se suprimen (`|| true`); el reporte de tipos puede quedar vacío. No es una regresión respecto al comportamiento previo.
62
68
 
63
69
  Nunca bloquea (siempre `exit 0`); reporta a `stderr` como información. Inerte si no hay `package.json` o si el archivo no es `.ts`/`.tsx`.
64
70
 
@@ -70,6 +76,14 @@ Cuando se edita un `openspec/changes/**/proposal.md`, imprime un recordatorio: c
70
76
 
71
77
  Al cerrar el turno, si hay un slice activo con gates en `false`, avisa por `stderr` qué gates quedan abiertos y recuerda **no archivar ni abrir PR** hasta cerrarlos (ver skill `building-a-slice` / `dod.md`). Inerte si no hay `package.json`, ni estado, ni `python3`.
72
78
 
79
+ ### 7. `scaffold-guard.sh` — `PreToolUse` · Write/Edit/MultiEdit · **bloqueante** (desde v0.2.0)
80
+
81
+ Refuerza el **scaffold como "Paso 1 fundamental"**. Bloquea con `exit 2` la escritura de **código de slice** —cuando `active_slice.phase` ∈ `red`/`green`/`refactor`/`smoke`/`api`/`data`— mientras `scaffold.confirmed` no sea `true` en `build-state.json`. **Permite** crear el scaffold (sin slice activo, o en fases `dor`/`change`). El arnés **exige** el scaffold pero **no lo genera**; la confirmación es **explícita** (vía `building-a-slice` Fase 0 / `dor-dod-gatekeeper`), nunca auto-detectada. Guarda `python3` fail-closed dirigido (solo bloquea si la entrada parece código de slice).
82
+
83
+ ### 8. `reflect-nudge.sh` — `Stop` · no bloqueante (desde v0.3.0)
84
+
85
+ Cierra el **ciclo autocorrectivo**. Al terminar el turno, si en `history[]` hay slice(s) archivado(s) con `reflected != true`, imprime un *nudge* sugiriendo ejecutar `/build:reflect` para capturar las convenciones aprendidas (y errores recurrentes) en el bloque `trycore-build-learnings` de `CLAUDE.md`. **Nunca bloquea** el cierre de sesión: si falta `python3` o el estado, sale `0` en silencio (**fail-open**). El razonamiento —qué se aprendió— vive en el comando `/build:reflect`, no en el hook; este solo recuerda. Tras reflexionar y estampar `reflected: true`, el nudge calla.
86
+
73
87
  ---
74
88
 
75
89
  ## La cadena de comando única (sin doble disparo entre canales)
@@ -93,8 +107,8 @@ Como la cadena es **carácter por carácter idéntica** en ambos canales, si el
93
107
 
94
108
  Los hooks parsean el JSON del evento con `python3`. Qué pasa si **falta** `python3` depende de si el hook es bloqueante:
95
109
 
96
- - **Bloqueantes** (`gitflow-guard`, `stack-guard`) → **fail-closed dirigido**: si no pueden analizar el comando/edición, solo bloquean (`exit 2`) cuando la entrada *cruda* parece relevante (un `git commit`/`push`, o una edición que menciona `package.json`); en cualquier otro caso salen `0`. Esto evita falsos negativos peligrosos sin frenar el trabajo no relacionado. El mensaje pide instalar `python3` (verificable con `trycore-build doctor`).
97
- - **No bloqueantes** (`load-build-state`, `lint-typecheck`, `coherence-flag`, `build-gate-check`) → si falta `python3`, simplemente **omiten** su trabajo y salen `0`.
110
+ - **Bloqueantes** (`gitflow-guard`, `stack-guard`, `scaffold-guard`) → **fail-closed dirigido**: si no pueden analizar el comando/edición, solo bloquean (`exit 2`) cuando la entrada *cruda* parece relevante (un `git commit`/`push`, una edición que menciona `package.json`, o código de slice sin scaffold confirmado); en cualquier otro caso salen `0`. Esto evita falsos negativos peligrosos sin frenar el trabajo no relacionado. El mensaje pide instalar `python3` (verificable con `trycore-build doctor`).
111
+ - **No bloqueantes** (`load-build-state`, `lint-typecheck`, `coherence-flag`, `build-gate-check`, `reflect-nudge`) → si falta `python3`, simplemente **omiten** su trabajo y salen `0` (fail-open).
98
112
 
99
113
  > `python3` es un **requisito duro**: `trycore-build init` y `trycore-build doctor` **fallan** si no está presente, justo porque toda la cadena de hooks depende de él para leer el JSON del evento.
100
114
 
@@ -111,7 +125,9 @@ Los hooks que tocan el código construido se **auto-arman**: permanecen **inerte
111
125
  | `stack-guard.sh` | Inerte si no existe la allowlist; sin `package.json` que comparar, no hay violación que detectar. |
112
126
  | `lint-typecheck.sh` | Inerte (sale `0` de inmediato). |
113
127
  | `coherence-flag.sh` | Funciona siempre (depende de OpenSpec, no del código). |
128
+ | `scaffold-guard.sh` | Permite (sin slice en fases de código no hay nada que bloquear; también permite crear el scaffold). |
114
129
  | `build-gate-check.sh` | Inerte (sale `0` de inmediato). |
130
+ | `reflect-nudge.sh` | Silencioso (en `authoring` no hay slices archivados que reflexionar). |
115
131
 
116
132
  Así el arnés convive sin fricción con la fase de *discovery* y se "enciende" cuando empieza la construcción real.
117
133
 
@@ -125,7 +141,7 @@ Una sola definición de hooks, expresada en dos archivos espejo según el canal:
125
141
 
126
142
  `trycore-build init` hace un **merge idempotente y aditivo** en el `settings.json` del consumidor (lógica en `src/lib/settings-merge.ts`; espejo documental en `templates/settings-hooks.template.json`):
127
143
 
128
- - Agrega las 5 agrupaciones de hooks (las 6 invocaciones: `PostToolUse` agrupa `lint-typecheck` + `coherence-flag`) sin pisar lo que ya exista.
144
+ - Agrega las 5 agrupaciones de hooks (las 8 invocaciones: `PreToolUse·Write` agrupa `stack-guard` + `scaffold-guard`; `PostToolUse·Write` agrupa `lint-typecheck` + `coherence-flag`; `Stop` agrupa `build-gate-check` + `reflect-nudge`) sin pisar lo que ya exista.
129
145
  - Agrega **permisos mínimos y enumerados** (sin `mcp__*` ni rutas absolutas):
130
146
 
131
147
  ```
@@ -3,6 +3,9 @@
3
3
  # AUTO-ARME: inerte mientras no exista package.json (fase authoring).
4
4
  # No bloquea: reporta lint/format/typecheck del archivo editado para liberar
5
5
  # capacidad de razonamiento del modelo (estilo delegado a herramientas).
6
+ # El typecheck es INCREMENTAL (--incremental + tsBuildInfoFile persistente): el primer
7
+ # run de la sesión paga O(repo) y cada edición posterior paga ~O(delta), para que el
8
+ # coste por edición no escale con el tamaño del repo.
6
9
  set -uo pipefail
7
10
 
8
11
  ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "${CLAUDE_PROJECT_DIR:-$(pwd)}")"
@@ -29,5 +32,22 @@ HAS() { [ -d node_modules ] && [ -x "node_modules/.bin/$1" ]; }
29
32
 
30
33
  if HAS prettier; then node_modules/.bin/prettier --write "$FILE" >/dev/null 2>&1 || true; fi
31
34
  if HAS eslint; then node_modules/.bin/eslint --fix "$FILE" 2>&1 | sed -n '1,20p' >&2 || true; fi
32
- if HAS tsc; then node_modules/.bin/tsc --noEmit 2>&1 | grep -F "$FILE" | sed -n '1,20p' >&2 || true; fi
35
+
36
+ # Typecheck INCREMENTAL del proyecto. `tsc` siempre recorre todo el grafo de tipos, pero con
37
+ # --incremental + un tsBuildInfoFile persistente, tras el primer run cada edición paga solo el delta.
38
+ # El .tsbuildinfo vive bajo node_modules/ (que el consumidor casi siempre tiene gitignored) → no
39
+ # contamina el repo. Filtramos la salida al archivo editado (grep -F) y la capamos a 20 líneas.
40
+ # timeout OPCIONAL: si coreutils está disponible acota un tsconfig patológico; si no, corre sin
41
+ # límite (el hook nunca bloquea, así que agotar el timeout solo omite el reporte de esta edición).
42
+ # Caveat: proyectos con `composite: true` deben usar `tsc -b`; aquí --noEmit prevalece como hoy
43
+ # y los errores se suprimen (|| true), igual que antes de v0.3.x.
44
+ if HAS tsc; then
45
+ TSBI="node_modules/.cache/trycore-build/tsbuildinfo"
46
+ mkdir -p "$(dirname "$TSBI")" 2>/dev/null || true
47
+ TSC_TIMEOUT=""
48
+ if command -v timeout >/dev/null 2>&1; then TSC_TIMEOUT="timeout 60"
49
+ elif command -v gtimeout >/dev/null 2>&1; then TSC_TIMEOUT="gtimeout 60"; fi
50
+ $TSC_TIMEOUT node_modules/.bin/tsc --noEmit --incremental --tsBuildInfoFile "$TSBI" 2>&1 \
51
+ | grep -F "$FILE" | sed -n '1,20p' >&2 || true
52
+ fi
33
53
  exit 0
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+ # reflect-nudge.sh — Stop
3
+ # Ciclo autocorrectivo (reflexión post-sesión): NUNCA bloquea el cierre de sesión.
4
+ # Sugiere /build:reflect SOLO si hay slice(s) archivado(s) sin reflexionar (reflected != true).
5
+ # Determinista y barato: el razonamiento (qué se aprendió) lo hace el MODELO en /build:reflect.
6
+ set -uo pipefail
7
+
8
+ ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "${CLAUDE_PROJECT_DIR:-$(pwd)}")"
9
+ STATE="$ROOT/.claude/state/build-state.json"
10
+ [ -f "$STATE" ] || exit 0
11
+ command -v python3 >/dev/null 2>&1 || exit 0 # fail-open: jamás impide cerrar sesión
12
+
13
+ # Mensaje a STDOUT (no stderr): un Stop hook con exit 0 no debe bloquear el cierre;
14
+ # el `2>/dev/null` suprime SOLO trazas de python, nunca el nudge.
15
+ python3 - "$STATE" <<'PY' 2>/dev/null || true
16
+ import json, sys
17
+ try:
18
+ d = json.load(open(sys.argv[1]))
19
+ except Exception:
20
+ sys.exit(0)
21
+ hist = d.get("history") or []
22
+ pend = [h for h in hist if isinstance(h, dict) and h.get("reflected") is not True]
23
+ if pend:
24
+ n = len(pend)
25
+ plural = "s" if n != 1 else ""
26
+ print(f"💡 Reflexión pendiente: {n} slice{plural} archivado{plural} sin capturar aprendizajes.")
27
+ print(" Ejecuta /build:reflect para proponer convenciones aprendidas a CLAUDE.md (se aplican tras tu aprobación).")
28
+ PY
29
+ exit 0
@@ -57,6 +57,10 @@
57
57
  {
58
58
  "type": "command",
59
59
  "command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/build-gate-check.sh\""
60
+ },
61
+ {
62
+ "type": "command",
63
+ "command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/reflect-nudge.sh\""
60
64
  }
61
65
  ]
62
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trycore/spec-build-harness",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
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": {
@@ -18,6 +18,7 @@
18
18
  "templates/",
19
19
  "internal/",
20
20
  "docs/",
21
+ "!docs/superpowers",
21
22
  "scripts/",
22
23
  "METODOLOGIA.md",
23
24
  "GOVERNANCE.md",
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: building-a-micro-change
3
+ description: Use for genuine maintenance that is NOT new product capability — a typo, a copy/string tweak, a dependency version bump within the stack allowlist, an infra/config/docs change, or a small bug fix of a few lines that adds no new capability. Lightweight lane — branch fix/*|chore/* → change → regression test only if behavior changes → PR — WITHOUT opening active_slice, an epic (EP-XXX), or an OpenSpec change. HARD LIMITS: escalate to building-a-slice (a full epic) if the change adds a new dependency, creates a new public API/endpoint, or changes domain logic or the data model/invariants. The epic stays the unit for product construction; this lane is out-of-band maintenance only.
4
+ ---
5
+
6
+ # Micro-change (mantenimiento) — carril ligero
7
+
8
+ Carril para **mantenimiento que no es construcción de producto nueva**. Espeja la filosofía del
9
+ arnés: la épica es la unidad de **construcción**, pero un typo o un bump de dependencia **no son
10
+ construcción** — forzarlos por las 8 fases de `building-a-slice` es ceremonia desproporcionada. Este
11
+ carril les da una vía corta **sin** diluir los guardarraíles deterministas.
12
+
13
+ > **Si una regla aquí contradice la metodología Trycore (`METODOLOGIA.md`), gana la metodología.**
14
+
15
+ ## Paso 0 · Decision gate (obligatorio) — ¿es esto un micro-change?
16
+
17
+ Un cambio califica como micro-change **solo si cumple TODO**:
18
+
19
+ - **No añade capacidad de producto nueva.** Corrige, ajusta o mantiene algo que ya existe.
20
+ - **Alcance acotado:** pocas líneas / una sola preocupación. No toca múltiples módulos a la vez.
21
+ - **Proyecto en fase `active`** (el scaffold runnable ya existe y está confirmado). El carril **no**
22
+ es para arrancar proyectos (eso es la Fase 0 de `building-a-slice`).
23
+
24
+ Ejemplos típicos (neutros): corregir un typo o un texto visible; ajustar un valor de configuración;
25
+ actualizar la versión de una dependencia **ya presente en la allowlist**; cambios de docs; un fix de
26
+ una a pocas líneas que repara un comportamiento sin introducir nada nuevo.
27
+
28
+ ### Límites DUROS — si el cambio cruza **cualquiera**, STOP: esto es una épica
29
+
30
+ Escala a `building-a-slice` (abre una épica `EP-XXX` con su DoR) si el cambio:
31
+
32
+ 1. **Añade una dependencia nueva** (fuera de `stack-allowlist.json`). *Lo bloquea además
33
+ `stack-guard.sh` de forma determinista.*
34
+ 2. **Crea un endpoint o una API pública nueva.**
35
+ 3. **Cambia lógica de dominio** o el **modelo/invariantes de datos**.
36
+ 4. **Desborda el alcance acotado** (introduce capacidad, toca muchos archivos, mezcla preocupaciones).
37
+
38
+ Ante la duda, **es una épica**. El carril micro-change nunca es un atajo para esquivar gates de
39
+ producto.
40
+
41
+ ## Pipeline ligero
42
+
43
+ 1. **Rama tipada.** Crea `fix/<slug>` (corrección) o `chore/<slug>` (infra/config/docs/bump).
44
+ `gitflow-guard.sh` ya exige rama tipada y prohíbe commit/push directo a `main`.
45
+ 2. **Aplica el cambio acotado.** Mantente dentro de los límites duros. Si al implementar descubres
46
+ que cruzas uno, **detente y escala** a `building-a-slice`.
47
+ 3. **Test de regresión — solo si cambia comportamiento.** Si el micro-change repara un bug,
48
+ añade/ajusta un test que falle antes y pase después (red→green del fix, delega en
49
+ `superpowers:test-driven-development`). Para cambios **no conductuales** (typo en copy, docs,
50
+ config) **no** se exige test.
51
+ 4. **PR a `main`.** Abre el Pull Request (`gitflow-guard.sh` impide la integración por push directo).
52
+ En la descripción del PR indica que es un micro-change y por qué califica (qué límite NO cruza).
53
+
54
+ ## Qué se mantiene y qué se salta
55
+
56
+ | Se mantiene (gratis, vía hooks deterministas) | Se salta (por diseño) |
57
+ |---|---|
58
+ | `gitflow-guard` (rama tipada + PR) | DoR formal (escenarios G/W/T, INVEST) |
59
+ | `stack-guard` (no dependencia nueva) | OpenSpec change + bloque `## Trazabilidad` |
60
+ | `lint-typecheck` (estilo + typecheck incremental) | `journey_smoke`, `api`, `data`, DoD reducido |
61
+ | | Apertura de `active_slice` / decisión de Release Gate |
62
+
63
+ `scaffold-guard` no aplica: no hay slice activo y el carril exige proyecto en fase `active` (scaffold
64
+ ya confirmado).
65
+
66
+ ## Estado y trazabilidad
67
+
68
+ El micro-change **no escribe** `build-state.json` — es mantenimiento fuera de banda, trazado por el
69
+ historial de git y el PR. No entra a `history[]`, así que `reflect-nudge.sh` **no** sugiere
70
+ reflexionar por él (no hay aprendizaje de épica que capturar en un typo).
71
+
72
+ ## Reglas duras
73
+
74
+ - **El decision gate es obligatorio.** Si dudas si algo es micro-change o épica, **es épica**.
75
+ - **Límites duros = STOP, no excepción.** Cruzar un límite obliga a escalar a `building-a-slice`;
76
+ jamás se "fuerza" un micro-change para evitar el DoR.
77
+ - **Integración solo por PR** a `main` (lo respalda `gitflow-guard.sh`).
78
+ - Si una regla aquí contradice `METODOLOGIA.md`, **gana la metodología**.
@@ -14,6 +14,12 @@ el avance en el estado.
14
14
  > rama = un PR. Las HU de la épica (que siguen viviendo en `docs/04-historias/`) son el **alcance
15
15
  > interno** del change y se listan en `active_slice.hus[]`. Construir por HU suelta es sobre-ingeniería.
16
16
 
17
+ > **¿Mantenimiento, no producto nuevo?** Un typo, un bump de dependencia ya permitida, un ajuste de
18
+ > copy/config/docs o un fix de pocas líneas **sin nueva capacidad** NO abren una épica: usa la skill
19
+ > **`building-a-micro-change`** (carril ligero `fix/*`|`chore/*` → cambio → PR). Si ese micro-change
20
+ > cruza un **límite duro** (dependencia nueva, API/endpoint nuevo, lógica de dominio o datos), escala
21
+ > **aquí** y ábrelo como épica.
22
+
17
23
  ## Principio de operación
18
24
  - **Una sola fuente de verdad**: `.claude/state/build-state.json` (schema + protocolo en
19
25
  `.claude/state/README.md`). Lee antes de actuar; escribe una vez por transición.
@@ -6,7 +6,7 @@ la épica; lo de abajo aplica a la épica y a **cada HU** que cubre (`hus[]`). L
6
6
  - [ ] **Épica válida**: `EP-XXX` existe en `docs/03-backlog/epicas.md` con trazabilidad a objetivos del PRD.
7
7
  - [ ] **HU enumeradas**: la épica tiene ≥1 HU; todas las que entran se listan en `hus[]`.
8
8
  - [ ] **Frontmatter completo** en cada `docs/04-historias/HU-XXX.md`: `id, titulo, epica, prioridad, complejidad, estado` y `estado: lista`.
9
- - [ ] **AC en Given/When/Then** por HU: 35 escenarios, con **happy + error + edge** (regla dura Trycore).
9
+ - [ ] **AC en Given/When/Then** por HU, **proporcional a `complejidad`** (cubre los modos de fallo que *realmente existen*, no una cuota fija): `trivial`/baja → **12** (happy + el error/edge crítico si existe); `media` → **3** (happy + error + edge); `alta` → **3–5** (cobertura completa). Regla dura Trycore: si existe una rama de error/edge, **debe** tener su escenario (lo que se elimina es fabricar 3–5 para una HU trivial).
10
10
  - [ ] **INVEST** por HU: pasa los 6 criterios (Independent, Negotiable, Valuable, Estimable, Small, Testable). Ante duda, invocar `invest-validator`.
11
11
  - [ ] **Dependencias resueltas**: las épicas/HU de las que depende están archivadas (`history[]`) o explícitamente no bloquean.
12
12
  - [ ] **Cabe en el stack** del PRD §7 (no requiere tecnología fuera de `stack-allowlist.json`).
@@ -16,7 +16,9 @@ gh pr create --base main --head feature/<slug> --fill # integración por PR
16
16
 
17
17
  ## Convenciones
18
18
  - **Rama**: `feature/<slug-kebab>` (nuevo valor), `fix/<slug>` (corrección), `chore/<slug>` (infra/docs).
19
- Una rama por **épica** (= un slice): `feature/ep-003-pricing-engine`.
19
+ Una rama por **épica** (= un slice): `feature/ep-003-pricing-engine`. Las ramas `fix/*` y `chore/*`
20
+ son también el carril de la skill `building-a-micro-change` (mantenimiento que no es producto nuevo:
21
+ va a PR sin abrir épica ni `active_slice`; ver sus límites duros).
20
22
  - **Commits**: Conventional Commits (`feat:`, `fix:`, `test:`, `refactor:`, `chore:`, `docs:`).
21
23
  - **PR**: título claro, descripción enlazando la épica, sus HU (`hus[]`) y el OpenSpec change; checks
22
24
  (lint, types, tests, newman) en verde antes de merge; squash recomendado.
package/state/README.md CHANGED
@@ -51,6 +51,15 @@ Arranca en `confirmed: false` y **solo** pasa a `true` por **confirmación expl
51
51
  restrictiva para abrir cualquier slice: el hook `scaffold-guard.sh` bloquea escribir código de
52
52
  slice (fases `red…data`) mientras `confirmed` no sea `true`. El arnés **no genera** el scaffold.
53
53
 
54
+ ### Reflexión post-slice (ciclo autocorrectivo)
55
+
56
+ Tras archivar un slice, su entrada en `history[]` puede llevar `reflected` / `reflected_at`. El
57
+ hook `reflect-nudge.sh` (evento `Stop`, **no bloqueante**) sugiere ejecutar `/build:reflect` mientras
58
+ exista al menos una entrada con `reflected != true`. `/build:reflect` lo ejecuta el **modelo**:
59
+ detecta convención nueva o error recurrente, **propone** un parche al bloque `trycore-build-learnings`
60
+ de `CLAUDE.md` (se aplica **solo tras tu aprobación**) y estampa `reflected: true` → el nudge calla.
61
+ El razonamiento vive en el modelo; el hook solo es un recordatorio determinista.
62
+
54
63
  ## Quién escribe qué
55
64
 
56
65
  | Campo / gate | Lo escribe | Cadencia |
@@ -62,4 +71,5 @@ slice (fases `red…data`) mientras `confirmed` no sea `true`. El arnés **no ge
62
71
  | `gates.api` | `api-contract-tester` | slice |
63
72
  | `gates.data` | `data-consistency-checker` | slice |
64
73
  | `releases[]` (`security`, `smell`, `ux`, `coherence`, `stack_arch`, `integration`, `status`) | `releasing-a-version` (delega en `security-reviewer`, `simple-design-reviewer`, `ux-krug-reviewer`, `coherence-three-way`, `stack-guardian`) | release |
74
+ | `history[].reflected` · `history[].reflected_at` | `/build:reflect` | post-slice (tras archivar) |
65
75
  | `harness_phase` | `load-build-state.sh` (SessionStart) | — |
@@ -86,7 +86,9 @@
86
86
  },
87
87
  "updated_at": { "type": "string", "format": "date-time" },
88
88
  "updated_by": { "type": "string", "description": "Agente o hook que escribió el estado." },
89
- "notes": { "type": "string", "description": "Nota libre opcional (p.ej. changes/branches adicionales de una épica construida en varios pasos)." }
89
+ "notes": { "type": "string", "description": "Nota libre opcional (p.ej. changes/branches adicionales de una épica construida en varios pasos)." },
90
+ "reflected": { "type": "boolean", "description": "True si /build:reflect ya capturó los aprendizajes de este slice archivado (ciclo autocorrectivo). Mientras sea false/ausente, reflect-nudge.sh sugiere reflexionar al cerrar sesión." },
91
+ "reflected_at": { "type": "string", "format": "date-time", "description": "Cuándo se reflexionó (ISO-8601 UTC). Lo escribe /build:reflect." }
90
92
  }
91
93
  },
92
94
  "release": {
@@ -54,4 +54,10 @@ Estos puntos de extensión los leen los agentes `security-reviewer`, `stack-guar
54
54
 
55
55
  <!-- END trycore-build-harness -->
56
56
 
57
+ <!-- BEGIN trycore-build-learnings -->
58
+ ## Convenciones aprendidas (mantenido por /build:reflect)
59
+
60
+ <!-- /build:reflect propone aquí viñetas concretas (convención nueva o error recurrente) tras cerrar un slice; se agregan SOLO con tu aprobación. Revísalas en PR como cualquier cambio de equipo. -->
61
+ <!-- END trycore-build-learnings -->
62
+
57
63
  <!-- A partir de aquí, el equipo del proyecto puede agregar instrucciones específicas del cliente. -->
@@ -1,87 +0,0 @@
1
- # Diseño: Scaffold como Paso 1 fundamental (scaffold gate)
2
-
3
- Fecha: 2026-06-02 · Target: `@trycore/spec-build-harness` v0.2.0 · Estado: aprobado (diseño)
4
-
5
- ## Contexto y problema
6
-
7
- Hoy el arnés es **permisivo** respecto al scaffold del proyecto (el esqueleto runnable: `package.json` + framework que arranca vacío). Lo trata como un evento implícito: `harness_phase` pasa de `authoring` a `active` cuando `load-build-state.sh` detecta `package.json`, y los gates de código "no se cierran" en `authoring`, pero nada **exige ni bloquea** explícitamente. La decisión del usuario:
8
-
9
- > Es requerido que exista un scaffold. **No lo forcemos** (el arnés NO lo genera — sigue agnóstico al stack), **pero nuestro setup debe ser restrictivo para avanzar**. Es el **Paso 1 fundamental**. Y debe **preguntarse explícitamente** (no inferirse en silencio de `package.json`).
10
-
11
- Objetivo: convertir "scaffold presente" en una **precondición explícita y restrictiva** — confirmada por el equipo, registrada en el estado, y respaldada por un bloqueo determinista — sin que el arnés genere el scaffold.
12
-
13
- ## Decisiones tomadas (brainstorming)
14
-
15
- 1. **Condición = confirmación explícita**, no inferencia silenciosa de `package.json`.
16
- 2. **Enforcement = skill/agente (Fase 0 de DoR) + gate en estado + hook determinista de respaldo.**
17
- 3. **Regla del hook = Enfoque A (por estado/fase):** bloquea solo cuando un slice está en fase de código sin el scaffold confirmado; permite crear el scaffold (fases `dor`/`change` o sin slice).
18
- 4. **No se genera el scaffold.** El arnés exige, bloquea e instruye; el equipo lo crea (guiado por `stack-allowlist.json`/PRD).
19
-
20
- ## Diseño
21
-
22
- ### 1. Estado (`state/build-state.json` + `build-state.schema.json`)
23
- Nuevo gate **a nivel proyecto** (no por-slice), explícito, default cerrado:
24
-
25
- ```jsonc
26
- "scaffold": {
27
- "confirmed": false,
28
- "confirmed_by": null, // agente/skill que confirmó
29
- "confirmed_at": null, // ISO 8601 UTC
30
- "notes": "" // ej. "Next.js app arranca vacía; npm run dev ok"
31
- }
32
- ```
33
-
34
- - Top-level, junto a `harness_phase`/`active_slice`/`history`/`releases`.
35
- - `harness_phase` (authoring/active) se mantiene como señal **informativa** auto-detectada; el **gate de avance** es `scaffold.confirmed`.
36
- - Solo pasa a `true` por **confirmación explícita** (nunca auto). Una transición = una escritura, con `confirmed_by`/`confirmed_at` (protocolo de `state/README.md`).
37
- - Schema: `scaffold` se añade a `properties` y a `required` del objeto raíz; `build-state.template.json` lo incluye con `confirmed:false`.
38
-
39
- ### 2. Skill `building-a-slice` — Fase 0 (precondición de DoR)
40
- Antes de abrir cualquier slice:
41
- - Si `scaffold.confirmed` ya es `true` → continúa al DoR normal.
42
- - Si es `false` → **pregunta explícitamente** (AskUserQuestion): *"¿Existe un scaffold runnable del proyecto (arranca vacío: build/dev corre sin error)?"*
43
- - **No** → **STOP**. Instruye crearlo según `stack-allowlist.json#source` (PRD) y el stack permitido; **no lo genera**. No abre el slice.
44
- - **Sí** → registra `scaffold.confirmed=true` (con `confirmed_by`, `confirmed_at`, `notes`) y continúa.
45
- - Documenta la relación con el walking skeleton (ver §5).
46
-
47
- ### 3. Agente `dor-dod-gatekeeper`
48
- Añade **"scaffold confirmado"** como criterio **duro** de DoR (primer ítem). No deja pasar a fases de código sin `scaffold.confirmed=true`. Es el dueño de escribir el gate cuando valida el DoR.
49
-
50
- ### 4. Hook determinista `hooks/build/scaffold-guard.sh` (PreToolUse · Write|Edit) — Enfoque A
51
- Backstop ineludible si el agente se salta la Fase 0:
52
- - Resuelve `ROOT` por `git rev-parse --show-toplevel` (+ fallback `${CLAUDE_PROJECT_DIR}`); lee `$ROOT/.claude/state/build-state.json`.
53
- - Guarda python3 fail-closed dirigida (igual patrón que `stack-guard.sh`).
54
- - **Regla de bloqueo:** si `active_slice` existe y `active_slice.phase ∈ {red, green, refactor, smoke, api, data}` **y** `scaffold.confirmed != true` → **exit 2** con mensaje: *"⛔ scaffold-guard: confirma el scaffold (Paso 1) antes de escribir código de slice. Ver building-a-slice / DoR."* En cualquier otro caso (sin slice, o fase `dor`/`change`) → exit 0 (permite crear el scaffold y planificar).
55
- - Auto-arme: si no existe `build-state.json` → exit 0.
56
-
57
- ### 5. Metodología (`METODOLOGIA.md`) — clarificación walking skeleton
58
- - **Paso 1 — Scaffold (precondición, NO generada por el arnés):** esqueleto runnable mínimo del proyecto (build/dev arranca vacío). El equipo lo crea, guiado por el stack del PRD/allowlist; el arnés lo **exige, lo pregunta explícitamente y lo bloquea** hasta confirmarlo.
59
- - **Primer slice — Walking skeleton:** sobre el scaffold confirmado, construye el journey end-to-end más delgado. El scaffold (shell vacío) precede al walking skeleton (primer journey real delgado).
60
-
61
- ### 6. Wiring de canales y CLI
62
- - `src/lib/settings-merge.ts`: añade `scaffold-guard.sh` al `HOOK_SPECS` (PreToolUse · `Write|Edit|MultiEdit`), misma cadena única.
63
- - `hooks/build-harness.json`: añade la misma entrada (canal plugin).
64
- - `src/commands/doctor.ts` y `status.ts`: reportan `scaffold: ✓ confirmado / ✗ pendiente` leyendo el estado.
65
- - `state/README.md`: documenta el gate `scaffold` y su protocolo de confirmación.
66
-
67
- ### 7. Versión
68
- `0.1.0 → 0.2.0` (feature). Sincronizar `VERSION` / `package.json` / `.claude-plugin/plugin.json`. Incluye el fix pendiente de `state/README.md` ("scaffold Next.js" → "scaffold de código del proyecto"). Entrada en `CHANGELOG.md`. Nota de cadencia/uso en `GOVERNANCE.md` si aplica.
69
-
70
- ## Casos borde
71
- - **Scaffold ya existe al instalar** (proyecto brownfield): la Fase 0 pregunta igual; el usuario confirma `Sí` una vez y queda registrado. No se re-pregunta mientras `scaffold.confirmed=true`.
72
- - **El equipo crea el scaffold dentro de un slice ya abierto**: desaconsejado por diseño (scaffold es Paso 1, antes del primer slice); el hook bloquea fases de código hasta confirmar. La creación del scaffold debe ocurrir con `active_slice=null` o en `dor`/`change`.
73
- - **Regresión de scaffold** (alguien borra package.json): `harness_phase` volvería a `authoring` (informativo); `scaffold.confirmed` sigue `true` salvo que se resetee manualmente. Aceptable para v0.2.0; documentar que el gate es una confirmación, no un chequeo continuo.
74
- - **python3 ausente**: el hook bloquea dirigido (no puede leer el estado de forma fiable) con mensaje claro, consistente con los otros hooks.
75
-
76
- ## Verificación (end-to-end)
77
- 1. `tsc` build limpio; `check-version-sync` (0.2.0 en los 3) / `check-agnostic` / `check-state-clean` en verde.
78
- 2. `build-state.template.json` incluye `scaffold.confirmed=false`; el schema valida.
79
- 3. Instalación en repo temporal: `scaffold-guard.sh` instalado y ejecutable; `settings.json` y `build-harness.json` contienen su entrada con la cadena única (sin doble disparo).
80
- 4. Test del hook por estado: con un `build-state.json` que tenga `active_slice.phase="green"` y `scaffold.confirmed=false`, un `Write` simulado → exit 2; con `scaffold.confirmed=true` → exit 0; con `active_slice=null` → exit 0.
81
- 5. `doctor`/`status` reportan el estado del scaffold.
82
- 6. Lectura de `building-a-slice`/`dor-dod-gatekeeper`: el ask de Fase 0 y el criterio duro están presentes y son agnósticos (pasa `check-agnostic`).
83
-
84
- ## Fuera de alcance
85
- - Generar el scaffold (decisión explícita: no lo forzamos).
86
- - Chequeo continuo de salud del scaffold (es una confirmación puntual, no un monitor).
87
- - Cambiar el modelo `harness_phase` (se mantiene como señal informativa).