@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
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.
|
|
6
|
-
2. Los **slash commands de Claude Code** (`/opsx:*` + `/build:onboard`) — operan el pipeline de dos loops
|
|
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
|
|
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.
|
package/docs/getting-started.md
CHANGED
|
@@ -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
|
-
- **
|
|
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 **
|
|
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 **
|
|
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
|
|
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
|
|
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
|
-
>
|
|
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`,
|
|
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
|
|
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
|
-
|
|
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
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scaffold-guard.sh — PreToolUse · Write/Edit/MultiEdit
|
|
3
|
+
# Backstop determinista del "Paso 1 fundamental": no se escribe código de slice sin
|
|
4
|
+
# scaffold confirmado. Enfoque A (por estado/fase): bloquea (exit 2) SOLO si hay un
|
|
5
|
+
# active_slice en fase de código (red/green/refactor/smoke/api/data) y scaffold.confirmed
|
|
6
|
+
# no es true. Permite crear el scaffold y planificar (sin slice, o fases dor/change).
|
|
7
|
+
# AUTO-ARME: si no existe build-state.json, no hay nada que vigilar -> exit 0.
|
|
8
|
+
set -uo pipefail
|
|
9
|
+
|
|
10
|
+
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "${CLAUDE_PROJECT_DIR:-$(pwd)}")"
|
|
11
|
+
STATE="$ROOT/.claude/state/build-state.json"
|
|
12
|
+
[ -f "$STATE" ] || exit 0
|
|
13
|
+
|
|
14
|
+
INPUT="$(cat)"
|
|
15
|
+
|
|
16
|
+
# Guarda python3 [H4]: si falta, no podemos leer el estado de forma fiable. Fail-closed
|
|
17
|
+
# DIRIGIDO: solo bloqueamos si la edición NO es del propio scaffold/planificación obvia.
|
|
18
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
19
|
+
echo "⛔ scaffold-guard: python3 no disponible; no puedo verificar el gate de scaffold. Instala python3 (trycore-build doctor)." >&2
|
|
20
|
+
exit 2
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
VERDICT="$(STATE="$STATE" python3 <<'PY' 2>/dev/null
|
|
24
|
+
import os, sys, json
|
|
25
|
+
try:
|
|
26
|
+
d = json.load(open(os.environ["STATE"]))
|
|
27
|
+
except Exception:
|
|
28
|
+
sys.exit(0) # estado ilegible -> no bloquear (auto-arme)
|
|
29
|
+
slice_ = d.get("active_slice")
|
|
30
|
+
if not slice_:
|
|
31
|
+
sys.exit(0) # sin slice: se permite crear el scaffold / planificar
|
|
32
|
+
code_phases = {"red", "green", "refactor", "smoke", "api", "data"}
|
|
33
|
+
if slice_.get("phase") not in code_phases:
|
|
34
|
+
sys.exit(0) # fases dor/change/pr/archived: permitido
|
|
35
|
+
if (d.get("scaffold") or {}).get("confirmed") is True:
|
|
36
|
+
sys.exit(0) # scaffold confirmado: permitido
|
|
37
|
+
print("BLOCK")
|
|
38
|
+
PY
|
|
39
|
+
)"
|
|
40
|
+
|
|
41
|
+
if [ "$VERDICT" = "BLOCK" ]; then
|
|
42
|
+
echo "⛔ scaffold-guard (Paso 1 fundamental): el slice está en fase de código pero el scaffold NO está confirmado." >&2
|
|
43
|
+
echo " Confirma primero que existe un scaffold runnable del proyecto (ver building-a-slice Fase 0 / DoR)." >&2
|
|
44
|
+
echo " El arnés NO genera el scaffold: créalo según tu stack (stack-allowlist.json) y confírmalo." >&2
|
|
45
|
+
exit 2
|
|
46
|
+
fi
|
|
47
|
+
exit 0
|
package/hooks/build-harness.json
CHANGED
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
{
|
|
28
28
|
"type": "command",
|
|
29
29
|
"command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/stack-guard.sh\""
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "command",
|
|
33
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/scaffold-guard.sh\""
|
|
30
34
|
}
|
|
31
35
|
]
|
|
32
36
|
}
|
|
@@ -53,6 +57,10 @@
|
|
|
53
57
|
{
|
|
54
58
|
"type": "command",
|
|
55
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\""
|
|
56
64
|
}
|
|
57
65
|
]
|
|
58
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trycore/spec-build-harness",
|
|
3
|
-
"version": "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.
|
|
@@ -33,6 +39,23 @@ inner loop: **≤ ~20 min por épica** y producto que **camina end-to-end en tod
|
|
|
33
39
|
> cada paso sea un stub. Cada épica posterior **engorda** un paso de ese esqueleto y mantiene el
|
|
34
40
|
> `journey_smoke` verde. Nunca se construyen capas horizontales aisladas que "se juntan al final".
|
|
35
41
|
|
|
42
|
+
## Fase 0 · Scaffold (Paso 1 fundamental — precondición restrictiva)
|
|
43
|
+
|
|
44
|
+
Antes de abrir **cualquier** slice, el scaffold runnable del proyecto debe **existir y estar
|
|
45
|
+
confirmado explícitamente**. El arnés **NO genera** el scaffold (es agnóstico al stack), pero
|
|
46
|
+
**bloquea el avance** hasta confirmarlo. Es la precondición del primer slice; el *esqueleto que
|
|
47
|
+
camina* se construye **encima** del scaffold ya existente.
|
|
48
|
+
|
|
49
|
+
1. Lee `build-state.json`. Si `scaffold.confirmed` ya es `true` → continúa a la Fase 1 (dor).
|
|
50
|
+
2. Si es `false` → **pregunta explícitamente** (AskUserQuestion): *"¿Existe un scaffold runnable del
|
|
51
|
+
proyecto (arranca vacío: el script de build/dev corre sin error)?"*
|
|
52
|
+
- **No** → **STOP**. Indica crearlo según el stack permitido (`.claude/config/stack-allowlist.json`
|
|
53
|
+
/ el PRD técnico). **No lo generes tú.** No abras el slice.
|
|
54
|
+
- **Sí** → registra en el estado `scaffold.confirmed=true` (con `confirmed_by`, `confirmed_at`,
|
|
55
|
+
`notes` — p.ej. "build/dev arranca vacío sin error") y continúa.
|
|
56
|
+
3. El gate lo valida también el `dor-dod-gatekeeper` (criterio duro de DoR) y lo respalda el hook
|
|
57
|
+
determinista `scaffold-guard.sh` (bloquea escribir código de slice sin scaffold confirmado).
|
|
58
|
+
|
|
36
59
|
## Pipeline — inner loop (carga la referencia indicada en cada paso)
|
|
37
60
|
|
|
38
61
|
| Fase | Acción | Delega en | Gate | Referencia |
|
|
@@ -66,16 +89,21 @@ El usuario siempre puede sobreescribir el default. Si acepta, invoca la skill
|
|
|
66
89
|
**`releasing-a-version`** sobre la release correspondiente.
|
|
67
90
|
|
|
68
91
|
## Cómo empezar
|
|
69
|
-
1.
|
|
92
|
+
1. **Fase 0 — scaffold**: verifica `scaffold.confirmed` (ver arriba). Si no está confirmado, resuélvelo
|
|
93
|
+
primero (pregunta explícita; STOP si no existe). Sin scaffold confirmado no se abre slice.
|
|
94
|
+
2. Pregunta/identifica la **épica** objetivo (`EP-XXX` en `docs/03-backlog/epicas.md`) y reúne las
|
|
70
95
|
**HU que cubre** (las que tienen `epica: EP-XXX` en `docs/04-historias/`) → poblarán `hus[]`.
|
|
71
|
-
|
|
96
|
+
3. Lee `build-state.json`. Si hay `active_slice`, retoma su primer gate abierto; si es `null`,
|
|
72
97
|
arranca en **dor**.
|
|
73
|
-
|
|
98
|
+
4. Invoca al `build-orchestrator` para conducir el pipeline, o ejecuta fase a fase tú mismo
|
|
74
99
|
respetando los gates.
|
|
75
100
|
|
|
76
101
|
## Reglas duras
|
|
77
|
-
-
|
|
78
|
-
|
|
102
|
+
- **Paso 1 fundamental**: sin `scaffold.confirmed=true` no se abre slice ni se escribe código de
|
|
103
|
+
slice (lo respalda `scaffold-guard.sh`). El arnés exige el scaffold pero **no lo genera**.
|
|
104
|
+
- En `harness_phase` = `authoring` (sin `package.json`) puedes hacer la Fase 0 (crear/confirmar el
|
|
105
|
+
scaffold) + dor + change, pero los gates de código (tdd, journey_smoke, api, data) NO se cierran
|
|
106
|
+
hasta tener el scaffold confirmado.
|
|
79
107
|
- El enlace change↔épica va en `## Trazabilidad` del `proposal.md`, **nunca** en frontmatter YAML
|
|
80
108
|
(rompe `openspec validate`). Ver `link-change-epic.md`.
|
|
81
109
|
- Integración solo por **PR** a `main` (el hook `gitflow-guard.sh` bloquea commits/push directos).
|
|
@@ -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:
|
|
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 → **1–2** (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.
|