@trycore/spec-build-harness 0.1.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.
- package/.claude-plugin/plugin.json +1 -1
- package/GOVERNANCE.md +15 -4
- package/INSTALL.md +10 -5
- package/METODOLOGIA.md +30 -6
- package/README.md +8 -5
- package/VERSION +1 -1
- package/agents/build/dor-dod-gatekeeper.md +10 -1
- package/commands/build/reflect.md +163 -0
- package/dist/commands/doctor.js +50 -0
- package/dist/commands/init.js +32 -9
- package/dist/commands/status.js +5 -0
- package/dist/commands/uninstall.js +4 -1
- package/dist/lib/settings-merge.js +2 -2
- package/dist/lib/state-seed.js +1 -0
- package/docs/commands.md +10 -4
- package/docs/customization/lsp-extensions.md +90 -0
- package/docs/customization/mcp-extensions.md +3 -0
- package/docs/getting-started.md +12 -3
- package/docs/hooks.md +24 -8
- package/hooks/build/lint-typecheck.sh +21 -1
- package/hooks/build/reflect-nudge.sh +29 -0
- package/hooks/build/scaffold-guard.sh +47 -0
- package/hooks/build-harness.json +8 -0
- package/package.json +2 -1
- package/skills/building-a-micro-change/SKILL.md +78 -0
- package/skills/building-a-slice/SKILL.md +33 -5
- package/skills/building-a-slice/references/dor.md +1 -1
- package/skills/building-a-slice/references/gitflow.md +3 -1
- package/state/README.md +20 -1
- package/state/build-state.schema.json +18 -3
- package/state/build-state.template.json +6 -0
- package/templates/CLAUDE.md.template +6 -0
- package/templates/settings-hooks.template.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "trycore-spec-build-harness",
|
|
4
4
|
"displayName": "Trycore — Spec & Build Harness",
|
|
5
|
-
"version": "0.
|
|
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
|
|
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 +
|
|
13
|
-
| Skill | `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.
|
|
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.
|
|
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/` —
|
|
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
|
|
|
@@ -65,6 +65,15 @@ por slice. Y el inner loop **no** dispara reviewers pesados. Cada gate vive en e
|
|
|
65
65
|
|
|
66
66
|
## 2. La regla del "esqueleto que camina"
|
|
67
67
|
|
|
68
|
+
> **Paso 1 fundamental — el scaffold (precondición, NO generada por el arnés).** Antes del primer
|
|
69
|
+
> slice debe **existir** un *scaffold* runnable: el esqueleto del proyecto que arranca vacío (el
|
|
70
|
+
> script de build/dev corre sin error). El arnés lo **exige y bloquea** —gate de proyecto
|
|
71
|
+
> `scaffold.confirmed`, que solo pasa a `true` por **confirmación explícita** (no por auto-detección
|
|
72
|
+
> de `package.json`), respaldado por el hook determinista `scaffold-guard.sh`— pero **no lo genera**
|
|
73
|
+
> (es agnóstico al stack: el equipo lo crea según el PRD/allowlist). El *esqueleto que camina* se
|
|
74
|
+
> construye **encima** del scaffold: el scaffold es el shell vacío que arranca; el walking skeleton
|
|
75
|
+
> es el primer journey real más delgado. Sin scaffold confirmado no se abre ningún slice.
|
|
76
|
+
|
|
68
77
|
El arnés prohíbe construir capas horizontales aisladas que "se juntan al final" (el anti-patrón que
|
|
69
78
|
hace que todos los gates por-slice estén en verde y el producto aun así no funcione de punta a
|
|
70
79
|
punta). En su lugar:
|
|
@@ -81,6 +90,15 @@ Consecuencia operativa: **la unidad de construcción es la épica, no la HU suel
|
|
|
81
90
|
épica = un OpenSpec change = una rama = un PR. Las HU que cubre la épica son su *alcance interno* y
|
|
82
91
|
se listan en `active_slice.hus[]`. Construir por HU individual es sobre-ingeniería.
|
|
83
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
|
+
|
|
84
102
|
---
|
|
85
103
|
|
|
86
104
|
## 3. Pipeline del inner loop, fase por fase (con sus gates)
|
|
@@ -108,7 +126,10 @@ Una épica **no entra a construcción** hasta cumplir todo (lo valida `dor-dod-g
|
|
|
108
126
|
- La épica tiene ≥1 HU; todas las que entran se listan en `hus[]`.
|
|
109
127
|
- Frontmatter completo en cada `docs/04-historias/HU-XXX.md` (`id, titulo, epica, prioridad,
|
|
110
128
|
complejidad, estado`) con `estado: lista`.
|
|
111
|
-
- AC en Given/When/Then por HU
|
|
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.
|
|
112
133
|
- Cada HU pasa los 6 criterios **INVEST**.
|
|
113
134
|
- Dependencias resueltas (las épicas/HU de las que depende están en `history[]` o no bloquean).
|
|
114
135
|
- Cabe en el stack del PRD (no requiere tecnología fuera de `stack-allowlist.json`).
|
|
@@ -142,8 +163,9 @@ vía el hook `stack-guard.sh` en tiempo real, no vía subagente.
|
|
|
142
163
|
|
|
143
164
|
- `ux` y `api` admiten `null` cuando el slice no tiene UI o endpoints. `null` ≠ abierto: **no
|
|
144
165
|
bloquea** el DoD.
|
|
145
|
-
- En `harness_phase: authoring` (sin `package.json`) se puede hacer
|
|
146
|
-
de código (`tdd`, `journey_smoke`, `api`, `data`) **no
|
|
166
|
+
- En `harness_phase: authoring` (sin `package.json`) se puede hacer la Fase 0 (crear/confirmar el
|
|
167
|
+
scaffold) + `dor` + `change`, pero los gates de código (`tdd`, `journey_smoke`, `api`, `data`) **no
|
|
168
|
+
se cierran** hasta tener el scaffold confirmado (gate de proyecto `scaffold.confirmed`, ver §2).
|
|
147
169
|
|
|
148
170
|
---
|
|
149
171
|
|
|
@@ -341,8 +363,10 @@ El arnés **no escribe** en `docs/`; cuando una HU no cumple DoR, devuelve el tr
|
|
|
341
363
|
|
|
342
364
|
## 10. Reglas duras (resumen)
|
|
343
365
|
|
|
344
|
-
1. La **épica** es la unidad de construcción
|
|
345
|
-
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.
|
|
346
370
|
2. El **esqueleto que camina** nunca deja de caminar: nada de capas horizontales que se juntan al
|
|
347
371
|
final; `journey_smoke` verde en cada épica.
|
|
348
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/ ←
|
|
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
|
|
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.
|
|
1
|
+
0.4.0
|
|
@@ -9,6 +9,13 @@ Eres el **gatekeeper DoR/DoD** del arnés de construcción. Eres read-only sobre
|
|
|
9
9
|
proponer la escritura del estado (no editas código de producto).
|
|
10
10
|
|
|
11
11
|
## Definition of Ready (gate `dor`) — antes de construir
|
|
12
|
+
|
|
13
|
+
**Precondición — Paso 1 fundamental (scaffold):** antes de validar nada más, exige
|
|
14
|
+
`scaffold.confirmed=true` en `build-state.json`. Si es `false`, **NO valides el DoR**: instruye
|
|
15
|
+
confirmar primero que existe un scaffold runnable del proyecto (ver `building-a-slice` Fase 0). El
|
|
16
|
+
arnés **no genera** el scaffold; lo exige. (El hook `scaffold-guard.sh` respalda este bloqueo en
|
|
17
|
+
las fases de código.)
|
|
18
|
+
|
|
12
19
|
La unidad es la **épica**. Identifica `EP-XXX` en `docs/03-backlog/epicas.md` y el conjunto de HU
|
|
13
20
|
que la componen (las que tienen `epica: EP-XXX` en `docs/04-historias/`). Pasa SOLO si **todas** se
|
|
14
21
|
cumplen; lista cada una con ✓/✗:
|
|
@@ -16,7 +23,9 @@ cumplen; lista cada una con ✓/✗:
|
|
|
16
23
|
2. Tiene **al menos una HU** asociada y todas se enumeran en `hus[]`.
|
|
17
24
|
3. **Cada HU** de la épica: frontmatter YAML completo (`id, titulo, epica, prioridad, complejidad,
|
|
18
25
|
estado`) y `estado: lista`.
|
|
19
|
-
4. **Cada HU**: AC en formato **Given/When/Then**,
|
|
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.
|
|
20
29
|
5. **Cada HU** pasa los 6 criterios **INVEST** (si dudas, invoca al agente `invest-validator`).
|
|
21
30
|
6. Dependencias declaradas (otras épicas/HU) están en `history[]` del estado o marcadas done.
|
|
22
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**.
|
package/dist/commands/doctor.js
CHANGED
|
@@ -59,6 +59,26 @@ export async function doctor(opts) {
|
|
|
59
59
|
else {
|
|
60
60
|
console.log(' — sin hooks instalados (corre `trycore-build init`)');
|
|
61
61
|
}
|
|
62
|
+
// Gate de scaffold (Paso 1 fundamental)
|
|
63
|
+
console.log('');
|
|
64
|
+
if (fs.existsSync(t.stateFile)) {
|
|
65
|
+
try {
|
|
66
|
+
const st = JSON.parse(fs.readFileSync(t.stateFile, 'utf8'));
|
|
67
|
+
const confirmed = st?.scaffold?.confirmed === true;
|
|
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
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
console.log('Scaffold (Paso 1): ⚠ build-state.json malformado');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log('Scaffold (Paso 1): — (sin build-state.json; corre `trycore-build init`)');
|
|
81
|
+
}
|
|
62
82
|
// Detección de doble canal (CLI settings.json + plugin) [H3]
|
|
63
83
|
const settingsHasHooks = fs.existsSync(t.settingsFile) &&
|
|
64
84
|
/hooks\/build\//.test(fs.readFileSync(t.settingsFile, 'utf8'));
|
|
@@ -68,6 +88,14 @@ export async function doctor(opts) {
|
|
|
68
88
|
console.log(' Si además instalaste el plugin nativo, la cadena de comando es idéntica:');
|
|
69
89
|
console.log(' Claude Code deduplica y el hook dispara UNA vez. No requiere acción.');
|
|
70
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
|
+
}
|
|
71
99
|
console.log('═══════════════════════════════════════');
|
|
72
100
|
if (dep.missingHard.length > 0) {
|
|
73
101
|
console.error(`\n✗ Faltan requisitos duros: ${dep.missingHard.join(', ')}. Instálalos antes de operar el arnés.`);
|
|
@@ -75,3 +103,25 @@ export async function doctor(opts) {
|
|
|
75
103
|
}
|
|
76
104
|
console.log('\n✓ Requisitos satisfechos.');
|
|
77
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
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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);
|
package/dist/commands/status.js
CHANGED
|
@@ -38,6 +38,7 @@ export async function status(opts) {
|
|
|
38
38
|
console.log('');
|
|
39
39
|
console.log('Estado del arnés:');
|
|
40
40
|
console.log(` Fase: ${st.harness_phase ?? '?'}`);
|
|
41
|
+
console.log(` Scaffold: ${st.scaffold?.confirmed === true ? '✓ confirmado (Paso 1)' : '✗ pendiente (Paso 1)'}`);
|
|
41
42
|
const slice = st.active_slice;
|
|
42
43
|
if (slice) {
|
|
43
44
|
const openGates = Object.entries(slice.gates ?? {})
|
|
@@ -50,6 +51,10 @@ export async function status(opts) {
|
|
|
50
51
|
console.log(' Slice activo: ninguno');
|
|
51
52
|
}
|
|
52
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
|
+
}
|
|
53
58
|
}
|
|
54
59
|
catch {
|
|
55
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) {
|
|
@@ -18,9 +18,9 @@ function cmd(script) {
|
|
|
18
18
|
const HOOK_SPECS = [
|
|
19
19
|
{ event: 'SessionStart', matcher: 'startup|clear|compact', scripts: ['load-build-state.sh'] },
|
|
20
20
|
{ event: 'PreToolUse', matcher: 'Bash', scripts: ['gitflow-guard.sh'] },
|
|
21
|
-
{ event: 'PreToolUse', matcher: 'Write|Edit|MultiEdit', scripts: ['stack-guard.sh'] },
|
|
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/dist/lib/state-seed.js
CHANGED
|
@@ -10,6 +10,7 @@ import { ensureDir } from './install-engine.js';
|
|
|
10
10
|
const EMPTY_STATE = {
|
|
11
11
|
version: '1.0',
|
|
12
12
|
harness_phase: 'authoring',
|
|
13
|
+
scaffold: { confirmed: false, confirmed_by: null, confirmed_at: null, notes: '' },
|
|
13
14
|
active_slice: null,
|
|
14
15
|
history: [],
|
|
15
16
|
releases: [],
|