@trycore/spec-build-harness 0.2.0 → 0.5.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 +24 -5
- package/INSTALL.md +11 -6
- package/METODOLOGIA.md +18 -4
- package/README.md +12 -7
- package/VERSION +1 -1
- package/agents/build/build-orchestrator.md +6 -1
- package/agents/build/dor-dod-gatekeeper.md +13 -6
- package/agents/build/ux-fidelity-reviewer.md +61 -0
- package/commands/build/onboard.md +20 -4
- package/commands/build/reflect.md +163 -0
- package/dist/commands/doctor.js +35 -0
- package/dist/commands/init.js +32 -9
- package/dist/commands/status.js +4 -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/agents.md +2 -1
- 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 +14 -5
- package/docs/hooks.md +30 -8
- package/hooks/build/design-source-guard.sh +52 -0
- package/hooks/build/lint-typecheck.sh +21 -1
- package/hooks/build/reflect-nudge.sh +29 -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 +26 -1
- package/skills/building-a-slice/references/dod.md +4 -0
- package/skills/building-a-slice/references/dor.md +5 -2
- package/skills/building-a-slice/references/gitflow.md +3 -1
- package/skills/building-a-slice/references/mcp-map.md +1 -0
- package/state/README.md +21 -0
- package/state/build-state.schema.json +19 -1
- package/state/build-state.template.json +8 -0
- package/templates/CLAUDE.md.template +8 -1
- package/templates/settings-hooks.template.json +1 -1
- package/docs/superpowers/specs/2026-06-02-scaffold-gate-design.md +0 -87
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trycore/spec-build-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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.
|
|
@@ -50,6 +56,21 @@ camina* se construye **encima** del scaffold ya existente.
|
|
|
50
56
|
3. El gate lo valida también el `dor-dod-gatekeeper` (criterio duro de DoR) y lo respalda el hook
|
|
51
57
|
determinista `scaffold-guard.sh` (bloquea escribir código de slice sin scaffold confirmado).
|
|
52
58
|
|
|
59
|
+
## Fase 0-bis · Fuente de diseño (seguro para slices con UI)
|
|
60
|
+
|
|
61
|
+
Espejo de la Fase 0, para proyectos **con UI**. Antes de abrir el primer slice con UI:
|
|
62
|
+
1. Lee `design_source` en `build-state.json`.
|
|
63
|
+
- `applies` indeterminado (ausente) → pregunta *"¿este proyecto tiene UI?"* y fija `applies`.
|
|
64
|
+
- `applies === false` → N/A, salta esta fase.
|
|
65
|
+
- `confirmed === true` → continúa.
|
|
66
|
+
2. `applies===true && confirmed===false` → **pregunta explícita** (AskUserQuestion): *"¿Existe una
|
|
67
|
+
fuente de diseño declarada (prototipo/export) para la UI de este proyecto?"*
|
|
68
|
+
- **No** → **STOP**. Indica declararla (ruta/URL del prototipo o export). El arnés **NO la genera**.
|
|
69
|
+
No abras el slice con UI.
|
|
70
|
+
- **Sí** → registra `design_source.source`, `confirmed=true`, `confirmed_by`, `confirmed_at`, `notes`.
|
|
71
|
+
3. Lo respalda el hook determinista `design-source-guard.sh` (bloquea código de slice UI sin fuente
|
|
72
|
+
confirmada) y lo valida el `dor-dod-gatekeeper` (criterio duro de DoR).
|
|
73
|
+
|
|
53
74
|
## Pipeline — inner loop (carga la referencia indicada en cada paso)
|
|
54
75
|
|
|
55
76
|
| Fase | Acción | Delega en | Gate | Referencia |
|
|
@@ -57,7 +78,7 @@ camina* se construye **encima** del scaffold ya existente.
|
|
|
57
78
|
| 1 · dor | Validar Definition of Ready | `dor-dod-gatekeeper` | `dor` | `dor.md` |
|
|
58
79
|
| 2 · change | `opsx:new` + bloque `## Trazabilidad`; validar enlace (barato) | `opsx:new`, `change-epic-coherence` | `coherence_link` | `link-change-epic.md` |
|
|
59
80
|
| 3 · tdd | red → green → refactor | `superpowers:test-driven-development` | `tdd` | — |
|
|
60
|
-
| 4 · smoke | Recorrer el journey-hasta-aquí end-to-end | skill `verify` / `run` (+ MCP chrome-devtools) | `journey_smoke` | `mcp-map.md` |
|
|
81
|
+
| 4 · smoke | Recorrer el journey-hasta-aquí end-to-end; **slices con UI:** verificar fidelidad a la fuente de diseño | skill `verify` / `run` (+ MCP chrome-devtools), `ux-fidelity-reviewer` | `journey_smoke`,`fidelity` | `mcp-map.md` |
|
|
61
82
|
| 5 · api/data | contratos + consistencia (si aplican al slice) | `api-contract-tester`, `data-consistency-checker` | `api`,`data` | `newman-tests.md`, `data-consistency.md` |
|
|
62
83
|
| 6 · dod | Definition of Done (por slice, reducido) | `dor-dod-gatekeeper` | `dod` | `dod.md` |
|
|
63
84
|
| 7 · pr | Abrir PR + archivar change en el mismo PR | `opsx:archive`, `opsx:sync` | — | `gitflow.md` |
|
|
@@ -67,6 +88,10 @@ Los gates `stack`, `security`, `smell`, `ux` y la coherencia triple completa **y
|
|
|
67
88
|
aquí**: pertenecen al Release Gate. Las **deps** siguen vigiladas en tiempo real por el hook
|
|
68
89
|
`stack-guard.sh`; lint/tsc/gitflow por sus hooks.
|
|
69
90
|
|
|
91
|
+
El gate `fidelity` (fidelidad a la fuente de diseño) **sí** es de inner loop: es barato (se computa
|
|
92
|
+
en `smoke`, con la app ya levantada) y vivo por-slice; complementa al `ux-krug-reviewer` (usabilidad),
|
|
93
|
+
que sigue en el Release Gate.
|
|
94
|
+
|
|
70
95
|
MCP/LSP por gate: ver `references/mcp-map.md`. Protocolo de estado: `references/state-protocol.md`.
|
|
71
96
|
|
|
72
97
|
## Fase 8 · ¿Release Gate ahora? (default computado, humano decide)
|
|
@@ -11,6 +11,10 @@ pesadas (seguridad, diseño, UX, coherencia triple, arquitectura, integración)
|
|
|
11
11
|
- [ ] **`coherence_link`** — `change-epic-coherence` confirma el enlace change↔épica (bloque `## Trazabilidad`, `openspec validate` ok). Es el chequeo **barato**; la trazabilidad triple completa va al Release Gate.
|
|
12
12
|
- [ ] **`data`** — `data-consistency-checker` valida invariantes de datos (salida de servicios externos validada contra esquema antes de alimentar la capa de decisión determinista del dominio) (si el slice toca datos).
|
|
13
13
|
- [ ] **`api`** — `api-contract-tester` (Newman) 100% verde (o `null` si el slice no tiene endpoints).
|
|
14
|
+
- [ ] **`fidelity` (slices con UI)** — `ux-fidelity-reviewer` devuelve **FIEL** (o DESVIACIONES todas
|
|
15
|
+
justificadas/documentadas) → `gates.fidelity: true`; `null` si el slice no tiene UI. INCONCLUSO
|
|
16
|
+
(MCP no disponible) se registra, **no bloquea**. Es gate **vivo** de inner loop (no la revisión Krug,
|
|
17
|
+
que sigue en el Release Gate).
|
|
14
18
|
- [ ] **OpenSpec**: todas las tasks `[x]`; el archive del change va **en el mismo PR** (no PR aparte).
|
|
15
19
|
- [ ] **Docs/trazabilidad**: back-ref del change añadida en la épica y en cada HU de `hus[]`.
|
|
16
20
|
- [ ] **Hooks verdes (automáticos, no son gates de agente)**: `lint-typecheck.sh` (lint + chequeo de tipos del stack declarado), `stack-guard.sh` (deps en allowlist según la sección de requisitos técnicos del PRD del consumidor, ruta declarada en `stack-allowlist.json#source`), `gitflow-guard.sh` (rama `feature/*`, sin commits directos a `main`).
|
|
@@ -6,12 +6,15 @@ 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`).
|
|
13
13
|
- [ ] **Datos de prueba disponibles** o identificables (p.ej. los datos de ejemplo / fixtures sintéticos del dominio del consumidor).
|
|
14
|
+
- [ ] **Fuente de diseño identificada (slices con UI)**: la fuente visual de verdad del slice
|
|
15
|
+
(el `DESIGN_SOURCE` del dominio) está declarada y confirmada (`design_source.confirmed`), y este
|
|
16
|
+
slice apunta a la(s) pantalla(s) equivalente(s). No se construye UI fuera de la fuente declarada.
|
|
14
17
|
|
|
15
18
|
**Si todo ✓** → `dor-dod-gatekeeper` abre `active_slice` en `build-state.json` con `epica`, `hus[]`,
|
|
16
|
-
`phase: dor`, `gates.dor: true` y el resto en `false` (`
|
|
19
|
+
`phase: dor`, `gates.dor: true` y el resto en `false` (`fidelity`/`api` en `null` si la épica no toca UI/endpoints).
|
|
17
20
|
**Si algo ✗** → no se abre el slice; se reporta qué falta y se vuelve a discovery (Trycore).
|
|
@@ -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.
|
|
@@ -15,6 +15,7 @@ los expone, el gate se cubre con las alternativas CLI/test indicadas.
|
|
|
15
15
|
| Fase / agente | MCP (ejemplo, si habilitado) | Para qué |
|
|
16
16
|
|---|---|---|
|
|
17
17
|
| `review` · `ux-krug-reviewer` | **chrome-devtools** | `take_snapshot` (árbol accesible), `lighthouse_audit` (Accessibility/Best-Practices), `take_screenshot`, `list_console_messages` para verificar la UI **corriendo**. |
|
|
18
|
+
| `smoke` · `ux-fidelity-reviewer` | **chrome-devtools** *(ejemplo opt-in)* | `new_page`/`navigate_page`, `take_screenshot`, `take_snapshot` para comparar **composición/paleta/tipografía** del diseño vs la app corriendo. Requiere la app levantada; úsalo en fase `active`. Si no hay MCP, el agente degrada a comparación estática (INCONCLUSO). |
|
|
18
19
|
| `api` · `api-contract-tester` | — (Newman CLI) | Contratos de endpoints. Newman no es MCP; corre vía Bash. |
|
|
19
20
|
| `data` · `data-consistency-checker` | **postgresql** *(solo si el slice usa Postgres)* | `read_query`/`describe_table` para validar consistencia en BD. Con un almacén embebido/cliente (según el stack declarado en el PRD del consumidor) se valida por tests, sin MCP. |
|
|
20
21
|
| perf (opcional, fuera del DoD) | **k6** | `execute_k6_test` para carga/latencia si una HU de la épica lo exige. |
|
package/state/README.md
CHANGED
|
@@ -51,6 +51,26 @@ 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
|
+
### `design_source` (gate de proyecto, slices con UI)
|
|
55
|
+
|
|
56
|
+
Espejo de `scaffold` para la UI: `applies` (¿el proyecto tiene UI?), `confirmed` (humano confirmó que
|
|
57
|
+
existe una fuente de diseño declarada), `source` (puntero al prototipo/export). El arnés NO genera el
|
|
58
|
+
prototipo. Lo respalda `design-source-guard.sh`.
|
|
59
|
+
|
|
60
|
+
### gate `fidelity` (por-slice, inner loop)
|
|
61
|
+
|
|
62
|
+
`true` = FIEL (o desviaciones justificadas); `false` = desviaciones sin justificar; `null` = slice sin
|
|
63
|
+
UI. Lo computa `ux-fidelity-reviewer` en la fase `smoke` y lo escribe el `build-orchestrator`.
|
|
64
|
+
|
|
65
|
+
### Reflexión post-slice (ciclo autocorrectivo)
|
|
66
|
+
|
|
67
|
+
Tras archivar un slice, su entrada en `history[]` puede llevar `reflected` / `reflected_at`. El
|
|
68
|
+
hook `reflect-nudge.sh` (evento `Stop`, **no bloqueante**) sugiere ejecutar `/build:reflect` mientras
|
|
69
|
+
exista al menos una entrada con `reflected != true`. `/build:reflect` lo ejecuta el **modelo**:
|
|
70
|
+
detecta convención nueva o error recurrente, **propone** un parche al bloque `trycore-build-learnings`
|
|
71
|
+
de `CLAUDE.md` (se aplica **solo tras tu aprobación**) y estampa `reflected: true` → el nudge calla.
|
|
72
|
+
El razonamiento vive en el modelo; el hook solo es un recordatorio determinista.
|
|
73
|
+
|
|
54
74
|
## Quién escribe qué
|
|
55
75
|
|
|
56
76
|
| Campo / gate | Lo escribe | Cadencia |
|
|
@@ -62,4 +82,5 @@ slice (fases `red…data`) mientras `confirmed` no sea `true`. El arnés **no ge
|
|
|
62
82
|
| `gates.api` | `api-contract-tester` | slice |
|
|
63
83
|
| `gates.data` | `data-consistency-checker` | slice |
|
|
64
84
|
| `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 |
|
|
85
|
+
| `history[].reflected` · `history[].reflected_at` | `/build:reflect` | post-slice (tras archivar) |
|
|
65
86
|
| `harness_phase` | `load-build-state.sh` (SessionStart) | — |
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"description": "Señal INFORMATIVA auto-detectada: authoring = aún no hay package.json; active = scaffold de código presente. El gate de avance es `scaffold.confirmed`, NO esta señal."
|
|
15
15
|
},
|
|
16
16
|
"scaffold": { "$ref": "#/$defs/scaffold" },
|
|
17
|
+
"design_source": { "$ref": "#/$defs/design_source" },
|
|
17
18
|
"active_slice": {
|
|
18
19
|
"description": "El slice (épica) en construcción. null si no hay ninguno activo.",
|
|
19
20
|
"oneOf": [
|
|
@@ -45,6 +46,20 @@
|
|
|
45
46
|
"notes": { "type": "string", "description": "Evidencia libre (p.ej. 'build/dev arranca vacío sin error')." }
|
|
46
47
|
}
|
|
47
48
|
},
|
|
49
|
+
"design_source": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"additionalProperties": false,
|
|
52
|
+
"description": "Gate de PROYECTO para slices con UI: existe una fuente de diseño declarada (prototipo/export). Espejo de scaffold: confirmed pasa a true SOLO por confirmación humana, nunca por auto-detección. El arnés NO genera el prototipo. applies=false apaga todo el mecanismo (proyecto sin UI).",
|
|
53
|
+
"required": ["applies", "confirmed"],
|
|
54
|
+
"properties": {
|
|
55
|
+
"applies": { "type": "boolean", "description": "¿el proyecto tiene UI / hay diseño que respetar? false → mecanismo N/A." },
|
|
56
|
+
"confirmed": { "type": "boolean", "description": "true solo tras confirmación humana de que existe una fuente de diseño declarada." },
|
|
57
|
+
"confirmed_by": { "type": ["string", "null"], "description": "Agente/skill que registró la confirmación." },
|
|
58
|
+
"confirmed_at": { "type": ["string", "null"], "format": "date-time" },
|
|
59
|
+
"source": { "type": "string", "description": "Puntero a la fuente: ruta/URL del prototipo o export (el DESIGN_SOURCE del dominio)." },
|
|
60
|
+
"notes": { "type": "string", "description": "Evidencia libre (p.ej. 'prototipo en docs/… revisado y vigente')." }
|
|
61
|
+
}
|
|
62
|
+
},
|
|
48
63
|
"slice": {
|
|
49
64
|
"type": "object",
|
|
50
65
|
"additionalProperties": false,
|
|
@@ -75,6 +90,7 @@
|
|
|
75
90
|
"journey_smoke": { "type": "boolean", "description": "El backbone-hasta-aquí camina end-to-end (verificado con la skill verify/run)." },
|
|
76
91
|
"coherence_link": { "type": "boolean", "description": "Enlace change↔épica válido (change-epic-coherence, barato, por slice)." },
|
|
77
92
|
"data": { "type": "boolean" },
|
|
93
|
+
"fidelity": { "type": ["boolean", "null"], "description": "Gate VIVO de inner loop (slices con UI): fidelidad visual a la fuente de diseño declarada, computado en fase smoke por ux-fidelity-reviewer. true = FIEL (o DESVIACIONES justificadas); false = DESVIACIONES sin justificar; null = N/A (slice sin UI). NO es legado (los legados viven en releases[])." },
|
|
78
94
|
"api": { "type": ["boolean", "null"], "description": "null = N/A (slice sin endpoints)." },
|
|
79
95
|
"ux": { "type": ["boolean", "null"], "description": "null = N/A (slice sin UI). Legado: hoy se audita en releases[]." },
|
|
80
96
|
"stack": { "type": "boolean", "description": "Legado por-slice; deps las cubre el hook stack-guard.sh, la arquitectura se audita en releases[]." },
|
|
@@ -86,7 +102,9 @@
|
|
|
86
102
|
},
|
|
87
103
|
"updated_at": { "type": "string", "format": "date-time" },
|
|
88
104
|
"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)." }
|
|
105
|
+
"notes": { "type": "string", "description": "Nota libre opcional (p.ej. changes/branches adicionales de una épica construida en varios pasos)." },
|
|
106
|
+
"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." },
|
|
107
|
+
"reflected_at": { "type": "string", "format": "date-time", "description": "Cuándo se reflexionó (ISO-8601 UTC). Lo escribe /build:reflect." }
|
|
90
108
|
}
|
|
91
109
|
},
|
|
92
110
|
"release": {
|
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
"confirmed_at": null,
|
|
8
8
|
"notes": ""
|
|
9
9
|
},
|
|
10
|
+
"design_source": {
|
|
11
|
+
"applies": false,
|
|
12
|
+
"confirmed": false,
|
|
13
|
+
"confirmed_by": null,
|
|
14
|
+
"confirmed_at": null,
|
|
15
|
+
"source": "",
|
|
16
|
+
"notes": ""
|
|
17
|
+
},
|
|
10
18
|
"active_slice": null,
|
|
11
19
|
"history": [],
|
|
12
20
|
"releases": []
|
|
@@ -35,7 +35,7 @@ Outer loop (por release): Release Gate (seguridad · diseño · UX · cohe
|
|
|
35
35
|
### Bloque de dominio (lo resuelve `/build:onboard`)
|
|
36
36
|
|
|
37
37
|
Estos puntos de extensión los leen los agentes `security-reviewer`, `stack-guardian`,
|
|
38
|
-
`data-consistency-checker`, `ux-krug-reviewer` y `
|
|
38
|
+
`data-consistency-checker`, `ux-krug-reviewer`, `simple-design-reviewer` y `ux-fidelity-reviewer`:
|
|
39
39
|
|
|
40
40
|
- **PRD técnico (fuente del stack)**: {{PRD_TECH_PATH}}
|
|
41
41
|
- **Capa de servicios externos / IA (frontera)**: {{EXTERNAL_SERVICE_LAYER}}
|
|
@@ -43,6 +43,7 @@ Estos puntos de extensión los leen los agentes `security-reviewer`, `stack-guar
|
|
|
43
43
|
- **Categorías de datos sensibles / PII reguladas**: {{SENSITIVE_DATA_CATEGORIES}}
|
|
44
44
|
- **Secretos server-side**: {{SERVER_SIDE_SECRETS}}
|
|
45
45
|
- **Decisiones de alto impacto que exigen explicabilidad UX**: {{HIGH_STAKES_DECISIONS}}
|
|
46
|
+
- **Fuente de diseño / referencia visual**: {{DESIGN_SOURCE}}
|
|
46
47
|
|
|
47
48
|
(Si aparecen como `{{...}}`, ejecuta `/build:onboard` para parametrizarlos.)
|
|
48
49
|
|
|
@@ -54,4 +55,10 @@ Estos puntos de extensión los leen los agentes `security-reviewer`, `stack-guar
|
|
|
54
55
|
|
|
55
56
|
<!-- END trycore-build-harness -->
|
|
56
57
|
|
|
58
|
+
<!-- BEGIN trycore-build-learnings -->
|
|
59
|
+
## Convenciones aprendidas (mantenido por /build:reflect)
|
|
60
|
+
|
|
61
|
+
<!-- /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. -->
|
|
62
|
+
<!-- END trycore-build-learnings -->
|
|
63
|
+
|
|
57
64
|
<!-- A partir de aquí, el equipo del proyecto puede agregar instrucciones específicas del cliente. -->
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
],
|
|
14
14
|
"PreToolUse": [
|
|
15
15
|
{ "matcher": "Bash", "hooks": [ { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/gitflow-guard.sh\"" } ] },
|
|
16
|
-
{ "matcher": "Write|Edit|MultiEdit", "hooks": [ { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/stack-guard.sh\"" }, { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/scaffold-guard.sh\"" } ] }
|
|
16
|
+
{ "matcher": "Write|Edit|MultiEdit", "hooks": [ { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/stack-guard.sh\"" }, { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/scaffold-guard.sh\"" }, { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/design-source-guard.sh\"" } ] }
|
|
17
17
|
],
|
|
18
18
|
"PostToolUse": [
|
|
19
19
|
{ "matcher": "Write|Edit|MultiEdit", "hooks": [ { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/lint-typecheck.sh\"" }, { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR/.claude}/hooks/build/coherence-flag.sh\"" } ] }
|
|
@@ -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).
|