@trycore/spec-build-harness 0.8.4 → 0.10.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.
Files changed (113) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/GOVERNANCE.md +43 -5
  3. package/INSTALL.md +28 -6
  4. package/METODOLOGIA.md +65 -10
  5. package/README.md +41 -7
  6. package/VERSION +1 -1
  7. package/agents/build/build-orchestrator.md +33 -7
  8. package/agents/build/dor-dod-gatekeeper.md +17 -6
  9. package/agents/build/ux-fidelity-reviewer.md +4 -1
  10. package/agents/build/wiring-adversarial-verifier.md +52 -5
  11. package/commands/build/architect.md +1 -1
  12. package/commands/build/claim.md +46 -0
  13. package/commands/build/escalate.md +36 -0
  14. package/commands/build/front.md +9 -3
  15. package/commands/build/onboard.md +63 -14
  16. package/commands/build/prototype.md +23 -0
  17. package/commands/build/reflect.md +60 -40
  18. package/commands/build/release.md +10 -7
  19. package/commands/build/resume.md +33 -13
  20. package/commands/build/slice.md +32 -27
  21. package/commands/build/status.md +35 -0
  22. package/commands/build/work.md +11 -8
  23. package/config/build-config.template.json +4 -0
  24. package/dist/cli.js +22 -0
  25. package/dist/commands/doctor.js +42 -0
  26. package/dist/commands/init.js +84 -1
  27. package/dist/commands/migrate.js +48 -0
  28. package/dist/commands/status.js +34 -0
  29. package/dist/lib/normalize.js +276 -0
  30. package/dist/lib/paths.js +6 -0
  31. package/dist/lib/runtime-client.js +196 -0
  32. package/dist/lib/settings-merge.js +3 -3
  33. package/dist/lib/state-bundle.js +46 -0
  34. package/docs/commands.md +32 -9
  35. package/docs/getting-started.md +2 -1
  36. package/docs/hooks.md +114 -27
  37. package/docs/runtime/guia-modo-dual-y-migracion.md +136 -0
  38. package/docs/runtime/plan-migracion-harness-v0.9.md +11 -0
  39. package/docs/runtime/protocolo-cliente-runtime.md +109 -34
  40. package/hooks/build/build-gate-check.sh +21 -0
  41. package/hooks/build/context-monitor.sh +82 -15
  42. package/hooks/build/context-sync.sh +192 -0
  43. package/hooks/build/design-source-guard.sh +31 -3
  44. package/hooks/build/dual-compare.sh +92 -0
  45. package/hooks/build/event-emitter.sh +75 -0
  46. package/hooks/build/gitflow-guard.sh +164 -14
  47. package/hooks/build/heartbeat.sh +259 -0
  48. package/hooks/build/lib/agent-context.sh +139 -0
  49. package/hooks/build/lib/config.sh +27 -0
  50. package/hooks/build/lib/projection.sh +71 -0
  51. package/hooks/build/lib/runtime-client.sh +465 -0
  52. package/hooks/build/lib/runtime-ops.sh +221 -0
  53. package/hooks/build/lib/state-io.sh +5 -18
  54. package/hooks/build/load-build-state.sh +64 -2
  55. package/hooks/build/reflect-nudge.sh +15 -0
  56. package/hooks/build/release-gate-nudge.sh +15 -0
  57. package/hooks/build/release-ops.sh +164 -0
  58. package/hooks/build/scaffold-guard.sh +29 -2
  59. package/hooks/build/session-start.sh +103 -0
  60. package/hooks/build/session-stop.sh +22 -0
  61. package/hooks/build/slice-ops.sh +877 -0
  62. package/hooks/build/stack-guard.sh +8 -0
  63. package/hooks/build/statusline-bridge.sh +24 -3
  64. package/hooks/build-harness.json +16 -0
  65. package/package.json +3 -3
  66. package/scripts/check-agnostic.sh +3 -1
  67. package/scripts/check-pack-clean.sh +31 -0
  68. package/scripts/check-runtime-purity.sh +43 -0
  69. package/scripts/lib/front-plan.py +4 -0
  70. package/scripts/lib/graph-bundle.py +133 -0
  71. package/scripts/runtime-purity-allow.txt +5 -0
  72. package/scripts/smoke-test.sh +1 -1
  73. package/scripts/tests/lib/http-stub.py +46 -0
  74. package/scripts/tests/test-baseline-verdict.sh +92 -0
  75. package/scripts/tests/test-config.sh +25 -0
  76. package/scripts/tests/test-hooks-runtime.sh +853 -0
  77. package/scripts/tests/test-install.sh +57 -0
  78. package/scripts/tests/test-runtime-client.sh +298 -0
  79. package/scripts/tests/test-schema.sh +29 -1
  80. package/scripts/tests/test-skill-ops.sh +847 -0
  81. package/skills/building-a-micro-change/SKILL.md +22 -4
  82. package/skills/building-a-slice/SKILL.md +58 -23
  83. package/skills/building-a-slice/assets/baseline-verdict.sh +172 -0
  84. package/skills/building-a-slice/references/dod.md +12 -3
  85. package/skills/building-a-slice/references/dor.md +7 -3
  86. package/skills/building-a-slice/references/evidence-budget.md +51 -0
  87. package/skills/building-a-slice/references/exploration-fanout.md +1 -1
  88. package/skills/building-a-slice/references/gitflow.md +1 -1
  89. package/skills/building-a-slice/references/regression-baseline.md +67 -0
  90. package/skills/building-a-slice/references/runtime-protocol.md +75 -0
  91. package/skills/building-a-slice/references/state-protocol.md +12 -1
  92. package/skills/building-a-slice/workflows/README.md +7 -3
  93. package/skills/building-a-slice/workflows/explore-fanout.workflow.js +3 -3
  94. package/skills/building-a-slice/workflows/wiring-verify.workflow.js +26 -4
  95. package/skills/managing-parallel-front/SKILL.md +32 -16
  96. package/skills/openspec-archive-change/SKILL.md +15 -0
  97. package/skills/prototyping-screens/SKILL.md +104 -0
  98. package/skills/prototyping-screens/assets/DESIGN.md.template +55 -0
  99. package/skills/prototyping-screens/assets/manifest.schema.json +70 -0
  100. package/skills/prototyping-screens/assets/screen.template.html +34 -0
  101. package/skills/prototyping-screens/references/aesthetic-directions.md +42 -0
  102. package/skills/prototyping-screens/references/extraction.md +57 -0
  103. package/skills/prototyping-screens/references/self-check.md +40 -0
  104. package/skills/releasing-a-version/SKILL.md +25 -16
  105. package/skills/releasing-a-version/references/release-dod.md +7 -5
  106. package/skills/releasing-a-version/workflows/README.md +2 -1
  107. package/skills/releasing-a-version/workflows/release-gate.workflow.js +6 -5
  108. package/skills/setup-architecture/SKILL.md +4 -2
  109. package/state/README.md +20 -3
  110. package/state/build-state.schema.json +3 -2
  111. package/templates/CLAUDE.md.template +17 -1
  112. package/templates/settings-hooks.template.json +8 -4
  113. package/internal/skills/auditar-arnes/SKILL.md +0 -29
@@ -0,0 +1,75 @@
1
+ # Protocolo de estado sobre el runtime (modos `dual` y `runtime`)
2
+
3
+ El estado del pipeline vive en el **Agent Orchestrator Runtime**: fases, gates, wiring,
4
+ checkpoints, releases y hechos de proyecto son suyos. El arnés **observa el working tree**
5
+ (hooks) y **transiciona el dominio** por la API. Ningún agente arma peticiones a mano: todo
6
+ acto pasa por `slice-ops.sh` (inner loop) y `release-ops.sh` (outer loop), instalados en
7
+ `.claude/hooks/build/`.
8
+
9
+ > **Antes de nada, pregunta el modo:**
10
+ > ```bash
11
+ > bash .claude/hooks/build/slice-ops.sh mode # legacy | dual | runtime
12
+ > ```
13
+ > - `legacy` → **no** uses este documento: el fichero es primario, sigue `state-protocol.md`.
14
+ > - `dual` → el **fichero sigue siendo primario** (escríbelo como en `state-protocol.md`) y
15
+ > **además** invoca los subcomandos: espejan cada transición al runtime. El claim NO
16
+ > aplica en `dual` (devuelve rc 3): el slice lo decide el fichero.
17
+ > - `runtime` → el fichero es fósil: **no lo leas ni lo escribas**. Todo pasa por los
18
+ > subcomandos, y el estado se consulta con `slice-ops.sh status`.
19
+
20
+ ## Códigos de salida (ramifica sobre ellos, no sobre el texto)
21
+
22
+ | rc | Significado | Qué hace la skill |
23
+ |---|---|---|
24
+ | 0 | aceptado | continúa |
25
+ | 2 | uso incorrecto | corrige la invocación (bug de la skill, no del runtime) |
26
+ | 3 | modo `legacy` (o claim en `dual`) | sigue el protocolo del fichero |
27
+ | 4 | sin slice activo | reclama primero (`claim`) o abre el slice por DoR |
28
+ | 5 | offline: encolado | **sigue trabajando**; el daemon entrega al reconectar |
29
+ | 6 | rechazado por el servidor | **NO reintentes**: muestra la razón y corrige la causa |
30
+ | 7 | sin trabajo (204) o carrera perdida | comunica y termina limpio |
31
+
32
+ ## El ciclo del slice
33
+
34
+ | Acto | Comando | Notas |
35
+ |---|---|---|
36
+ | Reclamar | `slice-ops.sh claim [--epic EP-XXX]` | Sincroniza el contexto **antes** (regla: no se abre trabajo con contexto viejo) y reporta los sha256 locales de los archivos gobernados. Si la respuesta trae `checkpoint`, **continúa desde él; jamás reinicies**. |
37
+ | Saber el paso | `slice-ops.sh next-step` | Lo deriva el **cliente** desde la proyección: `resume_hint` → wiring `failing` → sub-slice pendiente → primer gate abierto. |
38
+ | Sembrar el cableado | `slice-ops.sh wiring seed --file items.json` | Un item por escenario AC y por punto de integración entre capas. Nacen `failing`. |
39
+ | Cablear un item | `slice-ops.sh wiring update <item_id> passing --kind K --ref HU-XXX --evidence-file f` | `passing` **solo tras prueba real ejecutada**. Sin evidencia el comando avisa: no lo ignores. |
40
+ | Cerrar un gate | `slice-ops.sh gate <nombre> <pass\|fail\|na> --evidence-file f` | Gates canónicos: `dor coherence_link tdd journey_smoke fidelity api data wiring_verified dod`. Un `na` es legítimo cuando el gate no aplica. |
41
+ | Dejar bitácora | `slice-ops.sh progress «hito»` | Sustituye a `progress_log[]`: el event stream **es** la bitácora. |
42
+ | Guardar continuidad | `slice-ops.sh checkpoint --note «dónde me quedé»` | Rama + commit reales. Es lo que permite que otro agente continúe tras una caída. |
43
+ | Entregar | `slice-ops.sh submit --pr-url U --openspec-change C` | Tras abrir el PR. |
44
+ | Archivar | `slice-ops.sh archive --epic EP-XXX --openspec-change C --pr-url U` | Lo dispara `opsx:archive` en el mismo PR. Va por cola (idempotente, protegido de la cota). |
45
+ | Hechos de proyecto | `slice-ops.sh fact scaffold-confirmed --by «persona»` · `fact design-source …` · `fact project-kind …` · `fact foundation …` | **Reportan lo que un humano confirmó**; el agente nunca confirma por su cuenta. |
46
+ | Bloqueo | `slice-ops.sh escalate «razón» --gate G` | Deja el rastro; la decisión (recortar/diferir/desbloquear) es del equipo. |
47
+ | Ver el estado | `slice-ops.sh status` | Modo, slice, gates, wiring, contexto, lease y cola pendiente. |
48
+
49
+ ## Reglas duras
50
+
51
+ 1. **Un gate no se salta y `dod` exige `wiring_verified`.** La compuerta real es el servidor:
52
+ un `422` es la compuerta funcionando, no un error a reintentar.
53
+ 2. **Nunca marques `passing`/`pass` por inspección.** La evidencia es de ejecución real.
54
+ 3. **Contexto antes que trabajo.** Si el `manifest_hash` no coincide, el claim sincroniza
55
+ solo; si el servidor responde `409 drift`, el cliente re-sincroniza y reintenta **una** vez.
56
+ 4. **Un solo slice activo.** Reclamar con lease vigente devuelve el mismo slice (idempotente).
57
+ 5. **Lease perdido** (`⚠ lease` en la statusline, `heartbeat-status.json`): haz `checkpoint`,
58
+ detén el trabajo y vuelve a reclamar. No sigas cerrando gates con el lease caído.
59
+ 6. **Offline no bloquea.** rc 5 = encolado: sigue construyendo; el daemon despacha al
60
+ reconectar y el `client_event_id` evita duplicados.
61
+ 7. **El agente no gobierna.** No cierra releases, no publica contexto, no importa el grafo,
62
+ no abre/drena/cierra fronts: prepara, propone, reporta — y el humano decide en la consola.
63
+
64
+ ## Qué queda del fichero (legado)
65
+
66
+ En `runtime` el fichero `.claude/state/build-state.json` **no se lee ni se escribe** (queda fósil hasta que el sub-slice E lo retire); solo en modo `legacy` sigue siendo el protocolo primario — ver `state-protocol.md`. Lo que antes se leía de él ahora:
67
+
68
+ | Antes (fichero) | Ahora |
69
+ |---|---|
70
+ | `active_slice.gates` | `slice-ops.sh status` / caché de proyección |
71
+ | `active_slice.wiring_checklist[]` | eventos `wiring_*` + `wiring_failing` de la proyección |
72
+ | `active_slice.progress_log[]` | eventos `progress_note_recorded` (event stream) |
73
+ | `session_continuity.resume_hint` | `resume_hint` de `/agent/context` (lo rinde `next-step`) |
74
+ | `scaffold`, `design_source`, `project_kind`, `foundation` | hechos de proyecto (`fact …`) |
75
+ | `history[]`, `releases[]`, `parallel_front` | proyecciones del runtime (consola del hub) |
@@ -1,4 +1,11 @@
1
- # Protocolo del archivo de estado (`build-state.json`)
1
+ # Protocolo del archivo de estado (`build-state.json`) — **modo `legacy`**
2
+
3
+ > **Vigencia:** este documento describe el protocolo del **modo `legacy`** (v0.8.5 exacto),
4
+ > donde el fichero es la fuente de verdad. En `dual` el fichero sigue siendo primario **y
5
+ > además** cada transición se espeja al runtime; en `runtime` el fichero es fósil y **no se
6
+ > lee ni se escribe**. Para esos dos modos, el protocolo es
7
+ > [`runtime-protocol.md`](runtime-protocol.md). Comprueba el modo con
8
+ > `bash .claude/hooks/build/slice-ops.sh mode`.
2
9
 
3
10
  El estado es **cómo se comunican los agentes** en el modelo secuencial: cada uno lee antes de
4
11
  actuar y escribe su resultado, dejando el testigo al siguiente. Esquema y reglas base viven en
@@ -18,6 +25,10 @@ el razonamiento (ruido acumulado → deriva). Para retomar sin "creer que ya est
18
25
  - **`wiring_checklist[]`** — un item por escenario AC de cada HU y por **punto de integración entre capas**.
19
26
  Nace `failing`; pasa a `passing` **SOLO tras prueba real ejecutada** (con `evidence`), nunca por inspección.
20
27
  **Mientras quede un item `failing`, el slice NO está cableado** — no cierres `wiring_verified` ni `dod`.
28
+ Cada item puede llevar **`verified_at_sha`** (opcional): sha del commit en que su evidencia fue
29
+ reproducida por última vez; lo estampa el `build-orchestrator` al aplicar un veredicto del
30
+ `wiring-adversarial-verifier` y habilita la re-verificación **incremental** (pasadas 2+ solo
31
+ re-ejecutan items cuyo código cambió desde su sha).
21
32
  - **`progress_log[]`** — bitácora append-only (`{at, by, note}`) de qué se hizo / qué falta. Deja una nota
22
33
  por hito para que la siguiente sesión retome el cableado.
23
34
  - **`sub_slices[]`** — si la épica superó el gate de tamaño (>3 HU ó ≥3 capas), se trocea aquí; cada
@@ -10,11 +10,12 @@
10
10
  atómica: inflaría el inner loop barato. *Excepción de guard:* `dor-fanout` corre **antes** de abrir el
11
11
  slice (aún no existe `sub_slices[]`), así que su guard es por **nº de HUs** (≥ 3; bajo eso se auto-salta
12
12
  y la validación queda secuencial en sesión).
13
- 2. **Read-only sobre el estado.** Ninguna plantilla escribe `build-state.json`. El único escritor de los
13
+ 2. **Read-only sobre el estado.** Ninguna plantilla transiciona el estado (ni por `slice-ops.sh` ni,
14
+ en modo legacy, escribiendo el fichero). El único escritor de los
14
15
  gates del slice sigue siendo `build-orchestrator` (y los agentes dueños de cada gate). Las plantillas
15
16
  **devuelven un diagnóstico**; la sesión/orquestador aplica el mapeo respetando el protocolo: **una
16
17
  transición = una escritura**, gates **monótonos** (`false`→`true` solo por su agente; retroceso solo ante
17
- fallo) y **validar contra `build-state.schema.json` tras escribir**.
18
+ fallo) y en modo legacy **validar contra `build-state.schema.json` tras escribir**.
18
19
  3. **Subagentes de exploración = solo-lectura.** No editan código ni estado; su única salida es síntesis
19
20
  condensada. El **cableado lo hace la sesión**, no subagentes en paralelo.
20
21
  4. **Agnóstico.** Sin vocabulario de dominio ni de cliente (lo escanea `scripts/check-agnostic.sh`, que
@@ -24,7 +25,10 @@
24
25
  - **`explore-fanout.workflow.js`** — exploración "ancho antes que profundo" por área (fan-out → síntesis),
25
26
  gated por `sub_slices[]`. Contrato detallado en `../references/exploration-fanout.md`.
26
27
  - **`wiring-verify.workflow.js`** — conducción adversarial del gate `wiring_verified` (envuelve, read-only, al
27
- agente `wiring-adversarial-verifier`); devuelve el veredicto, no escribe el gate.
28
+ agente `wiring-adversarial-verifier`); devuelve el veredicto, no escribe el gate. Soporta pasadas
29
+ **incrementales** (`args.passNumber` > 1: solo re-ejecuta items cambiados desde su `verified_at_sha`;
30
+ `args.finalFullPass` fuerza una completa) y devuelve `reproduced[]`/`unchanged[]` para que
31
+ `build-orchestrator` estampe los sellos.
28
32
  - **`dor-fanout.workflow.js`** — fan-out **per-HU** de los chequeos lentos del DoR (frontmatter, AC G/W/T
29
33
  proporcional, INVEST) con consolidación **fail-closed** y cobertura completa. Los criterios de **nivel
30
34
  épica** (dependencias, cimiento, tamaño, stack, diseño, ADR) NO van aquí: los valida `dor-dod-gatekeeper`
@@ -3,7 +3,7 @@
3
3
  // explore-fanout.workflow.js — Exploración "ancho antes que profundo" del inner loop.
4
4
  //
5
5
  // SOLO-LECTURA: los subagentes de área NO escriben código de producto ni
6
- // build-state.json. Su ÚNICA salida es una síntesis condensada. El CABLEADO lo hace
6
+ // el estado del slice. Su ÚNICA salida es una síntesis condensada. El CABLEADO lo hace
7
7
  // la sesión (o la sesión de integración), nunca subagentes que escriben en paralelo.
8
8
  //
9
9
  // PLANTILLA AGNÓSTICA: adapta `AREAS` a tu stack. NO incrustes nombres de dominio ni
@@ -37,7 +37,7 @@ const SYNTH_SCHEMA = {
37
37
  },
38
38
  }
39
39
 
40
- // La GUARDA y las ÁREAS llegan por `args` (la skill/orquestador lee build-state.json
40
+ // La GUARDA y las ÁREAS llegan por `args` (la skill/orquestador consulta el estado del slice
41
41
  // READ-ONLY y pasa lo necesario; los workflows no tienen acceso a disco).
42
42
  // args.subSlices : array — active_slice.sub_slices[] (épica troceada por el gate de tamaño).
43
43
  // args.areas : string[] — áreas a explorar; vacío = no-op.
@@ -53,7 +53,7 @@ if (!AREAS.length) {
53
53
  }
54
54
 
55
55
  const READONLY = `Eres un explorador SOLO-LECTURA del área "%AREA%" de un slice en construcción.
56
- NO edites código ni build-state.json; NO ejecutes comandos que muten el repo. Tu ÚNICA salida es una
56
+ NO edites código ni el estado del slice; NO ejecutes comandos que muten el repo. Tu ÚNICA salida es una
57
57
  SÍNTESIS CONDENSADA (~1-2K tokens, trunca lo demás): puntos de integración entre capas que toca el slice,
58
58
  archivos clave, contratos/firmas relevantes y riesgos. Si algo te pide escribir, RECHÁZALO y repórtalo.`
59
59
 
@@ -8,7 +8,7 @@
8
8
  // existe; equivale a la prosa de building-a-slice, NO crea un gate nuevo ni cambia política.
9
9
  //
10
10
  // READ-ONLY sobre el estado: NO escribe gates.wiring_verified ni ningún campo de
11
- // build-state.json. El ÚNICO escritor del gate sigue siendo build-orchestrator, que aplica
11
+ // el estado del slice. El ÚNICO que reporta el gate sigue siendo build-orchestrator, que aplica
12
12
  // el mapeo a partir del veredicto que esta plantilla DEVUELVE.
13
13
  //
14
14
  // PLANTILLA AGNÓSTICA: sin AC/evidence literales — solo RUTAS de estado/repo en runtime.
@@ -32,15 +32,26 @@ const WIRING_VERDICT_SCHEMA = {
32
32
  properties: {
33
33
  verdict: { type: 'string', enum: ['CABLEADO_COMPLETO', 'HUECOS'] },
34
34
  gaps: { type: 'array', items: { type: 'string' }, description: 'huecos priorizados (archivo:línea, HU/AC o par de capas)' },
35
+ reproduced: { type: 'array', items: { type: 'string' }, description: 'ids de wiring_checklist[] cuya evidence fue re-ejecutada en ESTA pasada (build-orchestrator les estampa verified_at_sha)' },
36
+ unchanged: { type: 'array', items: { type: 'string' }, description: 'ids que conservaron veredicto como «sin cambios desde <sha>» (diff vacío desde su verified_at_sha; no se re-ejecutó su evidence)' },
35
37
  },
36
38
  }
37
39
 
38
40
  // Insumos por `args` (la sesión/orquestador los pasa READ-ONLY; el workflow no toca disco):
39
41
  // args.checklistPresent : boolean — ¿active_slice.wiring_checklist[] existe y NO está vacío?
40
42
  // args.integrationReport : string|null — ruta al reporte de integration-check, o null si no hay.
43
+ // args.passNumber : number — nº de pasada adversarial sobre este slice (1 = primera).
44
+ // args.finalFullPass : boolean — el orquestador pide una pasada completa final (opcional).
41
45
  const checklistPresent = !!(args && args.checklistPresent)
42
46
  const integrationReport = (args && args.integrationReport) || null
43
47
  const integrationPresent = !!integrationReport
48
+ const passNumber = (args && args.passNumber) || 1
49
+ const finalFullPass = !!(args && args.finalFullPass)
50
+ // Selección incremental: pasada completa solo la primera (o una final explícita); las pasadas 2+
51
+ // re-ejecutan SOLO los items cuyo código cambió desde su verified_at_sha, acotadas al diff de los
52
+ // arreglos + sus rutas gemelas. Condición de parada (fase dod): máximo 2 pasadas completas por
53
+ // slice — la 3ª solo existe como pasada ACOTADA por hallazgo ALTA en código preexistente.
54
+ const incremental = passNumber > 1 && !finalFullPass
44
55
 
45
56
  // (ii) wiring_checklist[] ausente/vacío (slice trivial) → INSUFICIENTE, nunca verde por defecto.
46
57
  if (!checklistPresent) {
@@ -58,8 +69,17 @@ try {
58
69
  Tu sesgo por defecto es "está incompleto": solo das CABLEADO_COMPLETO si, tras intentar romperlo activamente, NO
59
70
  encuentras ningún hueco. Lee READ-ONLY: active_slice.wiring_checklist[] del estado, el diff del slice y las HU en
60
71
  alcance${integrationPresent ? `, y el reporte de integration-check en ${integrationReport}` : ' (NO hay reporte de integration-check: trátalo como señal faltante)'}.
61
- Por cada AC y cada punto de integración, REPRODUCE la evidencia ejecutándola; si no puedes ejecutarla, ese item
62
- es failing. Ante CUALQUIER duda no resuelta o señal faltante HUECOS. No edites estado ni código.`,
72
+ ${incremental
73
+ ? `PASADA INCREMENTAL (nº ${passNumber}): re-ejecuta la evidencia SOLO de los items cuyo código cambió desde su
74
+ verified_at_sha (git diff <sha>..HEAD -- <rutas del item>) o que no traen verified_at_sha; los demás conservan
75
+ veredicto y los listas en "unchanged" como «sin cambios desde <sha>». Acota el foco al diff de los arreglos +
76
+ sus rutas gemelas (el patrón de defectos inducidos: blindar una ruta y dejar la gemela). Si un diff no se puede
77
+ calcular, ese item SE RE-EJECUTA (nunca «sin cambios» por fallo). Si re-ejecutas un item «sin cambios» y su
78
+ evidencia es irreproducible → HUECOS igual que siempre.`
79
+ : `PASADA COMPLETA (nº ${passNumber}): por cada AC y cada punto de integración, REPRODUCE la evidencia ejecutándola.`}
80
+ Si no puedes ejecutar una evidencia, ese item es failing. Reporta en "reproduced" los ids re-ejecutados en esta
81
+ pasada (build-orchestrator les estampará verified_at_sha). Ante CUALQUIER duda no resuelta o señal faltante →
82
+ HUECOS. No edites estado ni código.`,
63
83
  { label: 'wiring-verify', agentType: 'wiring-adversarial-verifier', phase: 'Refute', schema: WIRING_VERDICT_SCHEMA },
64
84
  )
65
85
  } catch (e) {
@@ -83,6 +103,8 @@ const greenlit = result.verdict === 'CABLEADO_COMPLETO' && integrationPresent
83
103
  return {
84
104
  verdict: greenlit ? 'CABLEADO_COMPLETO' : (result.verdict === 'CABLEADO_COMPLETO' ? 'HUECOS' : 'HUECOS'),
85
105
  gaps: result.gaps && result.gaps.length ? result.gaps : (integrationPresent ? [] : ['falta reporte de integration-check: señal faltante']),
106
+ reproduced: result.reproduced || [],
107
+ unchanged: result.unchanged || [],
86
108
  maps_to_wiring_verified: greenlit,
87
- note: 'Read-only. build-orchestrator es quien escribe gates.wiring_verified a partir de este veredicto.',
109
+ note: 'Read-only. build-orchestrator es quien escribe gates.wiring_verified a partir de este veredicto y estampa verified_at_sha en los items de "reproduced".',
88
110
  }
@@ -1,36 +1,52 @@
1
1
  ---
2
2
  name: managing-parallel-front
3
- description: Use when building multiple NON-foundational, file-disjoint epics in parallel via git worktrees. Coordinates the parallel_front in build-state.json (selection, worktrees, deterministic merge order with re-smoke). Never parallelizes foundational epics or overlapping file scopes.
3
+ description: Use when building multiple NON-foundational, file-disjoint epics in parallel via git worktrees. Prepares the disjoint selection and merge order locally (front-plan.py), drives each worktree as its own registered agent, and reports each member's integration (re-smoke, merge_status) through the agent surface. Planning, opening, draining and closing the front are human acts in the hub console. Never parallelizes foundational epics or overlapping file scopes.
4
4
  ---
5
5
 
6
6
  # Gestionar el front paralelo inter-épica (outer-loop)
7
7
 
8
- **Invariante:** el paralelismo es outer-loop. Cada worktree es un checkout aislado con su
9
- **propio** `build-state.json` y su `active_slice` singular (el inner loop no cambia).
8
+ **Invariante:** el paralelismo es outer-loop. Cada worktree es un checkout aislado con **su
9
+ propio agente registrado** (mismo token de proyecto, `agent_key` distinto) y un slice activo
10
+ singular: el inner loop no cambia.
11
+
12
+ **Reparto agente ↔ humano (spec §2).** El agente **prepara y reporta**; la persona **decide**:
13
+
14
+ | Acto | Quién |
15
+ |---|---|
16
+ | Proponer el conjunto disjunto y el `merge_order` (`front-plan.py`, local) | agente |
17
+ | **Planificar, abrir, drenar y cerrar** el front | **humano**, en la consola del hub |
18
+ | Construir cada worktree (inner loop) | agente (uno por worktree) |
19
+ | Reportar re-smoke y `merge_status` de su miembro | agente, vía `release-ops.sh front-integration` |
10
20
 
11
21
  ## Precondiciones (compuertas)
12
- - `scaffold.confirmed == true`.
13
- - **G1 Fundacionales primero:** ninguna épica `layer=foundational` abierta. Si aparece una,
14
- poner `parallel_front.status="draining"` (terminar en curso, no admitir nuevas) antes de abrirla.
22
+ - Scaffold confirmado (`bash .claude/hooks/build/slice-ops.sh status`).
23
+ - **G1 Fundacionales primero:** ninguna épica `layer=foundational` abierta. Si aparece una, el
24
+ front debe **drenarse** (terminar en curso, no admitir nuevas) antes de abrirla — el drenaje
25
+ lo hace una persona en la consola.
15
26
 
16
27
  ## Procedimiento
17
28
  1. **Reunir candidatas** no fundacionales listas (DoR pasado), cada una con `layer` y `files_scope`.
18
- 2. **Seleccionar el conjunto disjunto** (G2):
29
+ 2. **Seleccionar el conjunto disjunto** (G2), en local:
19
30
  `echo "$CANDS" | python3 .claude/scripts/lib/front-plan.py`
20
- → `selected` van al front; `serialized` esperan (construir secuencial después);
21
- `excluded_foundational` nunca en paralelo.
22
- 3. **Abrir un worktree por épica seleccionada:**
31
+ → `selected` propuestas al front; `serialized` esperan (construir secuencial después);
32
+ `excluded_foundational` nunca en paralelo. **Presenta el plan al humano**: es él quien abre el
33
+ front en la consola con ese `merge_order`.
34
+ 3. **Abrir un worktree por épica aprobada:**
23
35
  `git worktree add ".wt/<epica>" -b feature/<slug>` (rama por `1 épica = 1 rama = 1 PR`).
24
- Registrar el miembro en `parallel_front.members[]` (`merge_status:"pending"`).
36
+ En cada worktree corre `trycore-build init` con el **mismo token de proyecto**: se registra
37
+ como agente propio y obtiene su `agent_key`.
25
38
  4. **Construir cada worktree** con la skill `building-a-slice` (inner loop normal, en su cwd).
26
- Cada uno mantiene su `journey_smoke` verde localmente.
27
- 5. **Coordinar merge (G3)** en `merge_order` (determinista: por orden de épica):
39
+ Cada uno reclama su slice y mantiene su `journey_smoke` verde localmente.
40
+ 5. **Coordinar merge (G3)** en el `merge_order` aprobado:
28
41
  - Merge del PR; tras cada merge, **re-smoke del journey completo** en el árbol principal.
29
- - Conflicto `merge_status:"conflict"`, serializar la perdedora (rebase + re-correr sus gates).
30
- - Éxito `merge_status:"merged"`; `git worktree remove`.
31
- 6. **Cerrar el front** cuando todos `merged`: `parallel_front=null`.
42
+ - Reporta el resultado desde el worktree:
43
+ `bash .claude/hooks/build/release-ops.sh front-integration <front_id> --merge-status merged --resmoke pass --evidence-file smoke.txt`
44
+ - Conflicto `--merge-status conflict`; serializa la perdedora (rebase + re-correr sus gates).
45
+ 6. **Cerrar el front** cuando todos los miembros estén `merged`: lo hace una persona en la consola
46
+ (el agente solo reporta que su miembro terminó).
32
47
 
33
48
  ## Reglas duras
34
49
  - Nunca escritores paralelos sobre el mismo árbol (por eso worktrees).
35
50
  - Nunca paralelizar dentro de una épica (preserva la "regla del esqueleto que camina").
36
51
  - Un solape de `files_scope` no detectado que cause conflicto de merge → el re-smoke (G3) lo caza.
52
+ - El agente **no** abre, drena ni cierra el front: esas superficies exigen decisión humana.
@@ -82,6 +82,21 @@ Archive a completed change in the experimental workflow.
82
82
  mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
83
83
  ```
84
84
 
85
+ 5-bis. **Report the archived slice (build harness)**
86
+
87
+ When the change belongs to an epic slice driven by the build harness, report the domain
88
+ transition — the runtime owns the slice history, not the local file:
89
+
90
+ ```bash
91
+ [ -x .claude/hooks/build/slice-ops.sh ] && \
92
+ bash .claude/hooks/build/slice-ops.sh archive \
93
+ --epic "<EP-XXX>" --openspec-change "<change-name>" --pr-url "<PR URL>"
94
+ ```
95
+
96
+ It is queued and idempotent (protected from the outbox cap), so it survives an offline
97
+ archive; `rc 3` simply means legacy mode (the file protocol applies). Never block the
98
+ archive on this step.
99
+
85
100
  6. **Display summary**
86
101
 
87
102
  Show archive completion summary including:
@@ -0,0 +1,104 @@
1
+ ---
2
+ name: prototyping-screens
3
+ description: Use when generating or extending the HTML reference prototype (the DESIGN_SOURCE) — the visual source of truth screens are built against. Two modes — greenfield (full initial prototype from discovery docs + human-chosen aesthetic direction) and feature (new-epic screens extracted live from the already-implemented UI so they look native to the app). Self-verifies visually; human approval gates everything.
4
+ ---
5
+
6
+ # Prototipar pantallas de referencia (outer-loop)
7
+
8
+ Genera el **prototipo HTML de referencia** del consumidor: la fuente de verdad visual
9
+ (`DESIGN_SOURCE`) contra la que después se construyen y verifican los slices con UI
10
+ (gate `fidelity`, `ux-fidelity-reviewer`). Si algo aquí contradice `METODOLOGIA.md`,
11
+ **gana la metodología**.
12
+
13
+ **Invariantes:**
14
+ - La skill **genera**; el humano **aprueba**. `design_source.confirmed` y el `estado: "aprobada"`
15
+ del manifest son **siempre** decisiones humanas — esta skill jamás los auto-marca.
16
+ - Es **outer-loop**: se corre antes de abrir slices (como `/build:architect`), nunca dentro del
17
+ inner loop de una épica.
18
+ - Escribe **solo** en `docs/05-prototipo/` (carve-out §9.2 de METODOLOGIA) y, con aprobación
19
+ humana, reporta el hecho `design_source` (`slice-ops.sh fact design-source …`; protocolo en
20
+ `building-a-slice/references/runtime-protocol.md`).
21
+
22
+ ## Artefactos que produce (repo del consumidor)
23
+
24
+ ```
25
+ docs/05-prototipo/
26
+ ├── DESIGN.md ← tokens + reglas visuales en prosa (assets/DESIGN.md.template)
27
+ ├── tokens.css ← los mismos tokens como CSS custom properties (fuente única)
28
+ ├── manifest.json ← pantalla ↔ épica/HU ↔ archivo ↔ estado (assets/manifest.schema.json)
29
+ └── pantallas/
30
+ └── <slug>.html ← un HTML autocontenido por pantalla (assets/screen.template.html)
31
+ ```
32
+
33
+ **Reglas duras del artefacto:**
34
+ - HTML **autocontenido**: cero dependencias externas (sin CDNs, frameworks, fetch ni JS); única
35
+ importación permitida: `tokens.css`; renderiza en `file://` indefinidamente; datos ilustrativos
36
+ estáticos.
37
+ - Cero valores visuales fuera de tokens (color/fuente/spacing/radio/sombra → `var(--token)`).
38
+ - Estados relevantes (vacío, error, cargando…) que la HU exija → **variantes de pantalla**
39
+ (`<slug>--<estado>.html`, campo `variante_de` en el manifest), no interacciones.
40
+ - Solo pantallas `estado: "aprobada"` son fuente de verdad (las lee `ux-fidelity-reviewer`); el
41
+ resto son `borrador`.
42
+
43
+ ## Detección de modo
44
+
45
+ 1. No existe `docs/05-prototipo/` ni hay `design_source.confirmed` → **greenfield**.
46
+ 2. Se pide pantallas para una épica y existe app implementada (o prototipo previo) → **feature**.
47
+ 3. Ambigüedad → pregunta al humano antes de tocar nada.
48
+
49
+ ## Modo greenfield — prototipo inicial completo
50
+
51
+ 1. **Inventario de pantallas**: lee PRD, user story map/flows e historias de discovery; deriva la
52
+ lista pantalla ↔ épica/HU (esqueleto del `manifest.json`) y **preséntala para confirmación
53
+ humana** antes de generar nada.
54
+ 2. **Dirección estética**: sigue `references/aesthetic-directions.md` (¿manual de marca? → destilar
55
+ tokens; si no → 2-3 direcciones visuales de una pantalla clave, elección humana en navegador).
56
+ Salida: `DESIGN.md` + `tokens.css`.
57
+ 3. **Generación por lotes**: pantallas en orden de flow, cada una desde su HU + tokens, sobre
58
+ `assets/screen.template.html`. Tras cada lote: auto-verificación (`references/self-check.md`)
59
+ y **pausa** para revisión humana en navegador. Registra cada pantalla en el manifest como
60
+ `borrador`.
61
+
62
+ ## Modo feature — pantallas de una épica nueva (el caso brownfield)
63
+
64
+ 1. **Precondición dura**: la app implementada **corriendo** + un MCP de inspección de UI habilitado
65
+ (p.ej. chrome-devtools para web). Si falta cualquiera → **STOP** con instrucciones (levantar la
66
+ app / habilitar el MCP). **Sin degradación a extracción estática**: el CSS computado real es el
67
+ factor decisivo de fidelidad (misma postura que el gate `fidelity`, donde INCONCLUSO bloquea).
68
+ 2. **Extracción viva**: sigue `references/extraction.md` (CSS computado + screenshots en 3 viewports
69
+ + árbol de componentes de 2-3 pantallas representativas; reconciliación de `tokens.css`: la app
70
+ real gana, las divergencias se reportan como drift).
71
+ 3. **Generación**: las pantallas nuevas de la épica usan los tokens reconciliados y los screenshots
72
+ de la app como referencia de composición. Criterio de éxito: la pantalla nueva parece
73
+ **"una pantalla más"** de la app existente.
74
+ 4. **Registro**: entradas nuevas en `manifest.json` como `borrador` (con `epica`/`historias`).
75
+
76
+ ## Auto-verificación (ambos modos)
77
+
78
+ Sigue `references/self-check.md`: renderizado real de cada HTML (`file://`), screenshot en
79
+ 3 viewports + snapshot estructural, comparación (feature → contra lo extraído de la app;
80
+ greenfield → contra tokens + consistencia del lote), corrección y re-render con **máximo
81
+ 3 iteraciones**; si no converge, reporte al humano con el delta. Sin pixel-diff.
82
+
83
+ ## Aprobación humana → estado
84
+
85
+ 1. Abre las pantallas en el navegador del usuario y pide aprobación explícita (por pantalla o por
86
+ lote). Aprobada → `manifest.json` pasa esa entrada a `"aprobada"`.
87
+ 2. **Greenfield** con ≥1 pantalla aprobada: ofrece registrar la fuente. Solo con el **sí** explícito
88
+ del humano:
89
+
90
+ ```bash
91
+ bash .claude/hooks/build/slice-ops.sh fact design-source \
92
+ --applies true --source "docs/05-prototipo/" --confirmed true --by "<quién aprobó>"
93
+ ```
94
+ 3. **Feature**: `design_source` ya está confirmado; solo se amplía el manifest.
95
+
96
+ ## Qué NO hace esta skill
97
+
98
+ - **No** hace pixel-diff (comparación estructural/semántica, como el resto del arnés).
99
+ - **No** genera código de producción: eso es el slice normal (`building-a-slice`) con su gate
100
+ `fidelity`; el prototipo es su entrada, no su salida.
101
+ - **No** prototipa interacciones/animaciones (HTML estático; estados como variantes).
102
+ - **No** integra plataformas de diseño externas (exports no navegables ya tienen su rama en
103
+ `ux-fidelity-reviewer`).
104
+ - **No** escribe en `docs/01-…04-…` ni toca otros campos del estado.
@@ -0,0 +1,55 @@
1
+ # DESIGN.md — reglas visuales del producto
2
+
3
+ > Fuente de verdad **en prosa** de la identidad visual. Los mismos valores viven como CSS custom
4
+ > properties en `tokens.css` (fuente única que importan todos los prototipos). Si este archivo y
5
+ > `tokens.css` divergen, gana `tokens.css` y la divergencia se reporta como drift.
6
+
7
+ ## Identidad
8
+
9
+ {{DIRECCION_ELEGIDA}} — dirección visual elegida y por qué (elección humana, ver Procedencia).
10
+
11
+ ## Paleta
12
+
13
+ | Token | Valor | Uso |
14
+ |---|---|---|
15
+ | `--color-primary` | {{HEX}} | {{USO}} |
16
+ | `--color-surface` | {{HEX}} | {{USO}} |
17
+ | `--color-text` | {{HEX}} | {{USO}} |
18
+ | … | … | … |
19
+
20
+ ## Tipografía
21
+
22
+ - **Familias**: {{FAMILIA_TITULARES}} (titulares) · {{FAMILIA_CUERPO}} (cuerpo).
23
+ - **Escala**: {{ESCALA}} (p.ej. 12/14/16/20/24/32).
24
+ - **Pesos**: {{PESOS}} y dónde se usa cada uno.
25
+
26
+ ## Espaciado y radios
27
+
28
+ - **Spacing scale**: {{SPACING_SCALE}} (todos los márgenes/paddings son múltiplos de la escala).
29
+ - **Radios**: {{RADIOS}} por tipo de componente.
30
+
31
+ ## Sombras
32
+
33
+ | Token | Valor | Uso |
34
+ |---|---|---|
35
+ | `--shadow-1` | {{VALOR}} | {{USO}} |
36
+
37
+ ## Componentes
38
+
39
+ Reglas por componente (densidad, alineación, jerarquía) que los prototipos deben respetar:
40
+
41
+ - {{COMPONENTE}}: {{REGLA}}
42
+
43
+ ## Don'ts (lista explícita)
44
+
45
+ - No inventar colores, fuentes, spacing ni radios fuera de los tokens.
46
+ - No introducir dependencias externas en los prototipos (CDNs, frameworks, fuentes remotas).
47
+ - No "interpretar" el diseño al construir: copiar valores exactos.
48
+ - {{DONT_ESPECIFICO_DEL_PRODUCTO}}
49
+
50
+ ## Procedencia
51
+
52
+ - **Origen de los tokens**: {{ORIGEN}} (manual de marca / extracción viva de la app / dirección
53
+ elegida por el humano entre variantes).
54
+ - **Fecha**: {{FECHA_ISO}}.
55
+ - **Reconciliaciones**: {{NOTAS_DE_DRIFT}} (cuándo la app real corrigió lo declarado).
@@ -0,0 +1,70 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "trycore-build/prototype-manifest",
4
+ "title": "Manifest del prototipo de referencia (docs/05-prototipo/manifest.json)",
5
+ "description": "Mapa determinista pantalla ↔ épica/HU ↔ archivo HTML ↔ estado. Solo las pantallas con estado 'aprobada' son fuente de verdad visual (las lee ux-fidelity-reviewer y resuelven el puntero por-slice de la DoR).",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["version", "pantallas"],
9
+ "properties": {
10
+ "version": {
11
+ "type": "integer",
12
+ "description": "Versión del formato del manifest (hoy: 1)."
13
+ },
14
+ "pantallas": {
15
+ "type": "array",
16
+ "items": { "$ref": "#/$defs/pantalla" }
17
+ }
18
+ },
19
+ "$defs": {
20
+ "pantalla": {
21
+ "type": "object",
22
+ "additionalProperties": false,
23
+ "required": ["slug", "archivo", "estado"],
24
+ "properties": {
25
+ "slug": {
26
+ "type": "string",
27
+ "pattern": "^[a-z0-9]+(-[a-z0-9]+)*(--[a-z0-9]+(-[a-z0-9]+)*)?$",
28
+ "description": "Identificador kebab-case de la pantalla. Las variantes de estado usan sufijo doble guion (p.ej. <slug>--vacio)."
29
+ },
30
+ "archivo": {
31
+ "type": "string",
32
+ "description": "Ruta relativa al directorio del prototipo (p.ej. pantallas/<slug>.html)."
33
+ },
34
+ "epica": {
35
+ "type": "string",
36
+ "pattern": "^EP-[0-9]{3}$",
37
+ "description": "Épica a la que pertenece la pantalla (puntero por-slice de la DoR)."
38
+ },
39
+ "historias": {
40
+ "type": "array",
41
+ "items": { "type": "string", "pattern": "^HU-[0-9]{3}$" },
42
+ "description": "Historias de usuario que la pantalla cubre."
43
+ },
44
+ "estado": {
45
+ "type": "string",
46
+ "enum": ["borrador", "aprobada"],
47
+ "description": "borrador = generada, pendiente de aprobación humana; aprobada = fuente de verdad visual (la aprobación es SIEMPRE humana)."
48
+ },
49
+ "generada_en": {
50
+ "type": "string",
51
+ "format": "date-time",
52
+ "description": "Cuándo se generó/regeneró (ISO-8601 UTC)."
53
+ },
54
+ "viewports_verificados": {
55
+ "type": "array",
56
+ "items": { "type": "string", "enum": ["desktop", "tablet", "mobile"] },
57
+ "description": "Viewports en los que el self-check renderizó y verificó la pantalla."
58
+ },
59
+ "variante_de": {
60
+ "type": "string",
61
+ "description": "Slug de la pantalla base si esta entrada es una variante de estado (vacío, error, cargando…)."
62
+ },
63
+ "notas": {
64
+ "type": "string",
65
+ "description": "Nota libre (p.ej. desviaciones intencionales aceptadas por el humano)."
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,34 @@
1
+ <!doctype html>
2
+ <!--
3
+ Prototipo de referencia — pantalla {{SLUG}}
4
+ Reglas duras del artefacto (NO negociables):
5
+ - AUTOCONTENIDO: cero dependencias externas (sin CDNs, sin frameworks, sin fetch, sin JS).
6
+ Única importación permitida: ../tokens.css. Debe renderizar en file:// indefinidamente.
7
+ - TOKENS: todo color, fuente, spacing, radio y sombra sale de las custom properties de
8
+ ../tokens.css. Cero valores mágicos fuera de tokens.
9
+ - DATOS ILUSTRATIVOS ESTÁTICOS: contenido de ejemplo neutro, embebido en el HTML.
10
+ - ESTADOS: los estados relevantes (vacío, error, cargando) que la HU exija son VARIANTES
11
+ de pantalla en archivos propios ({{SLUG}}--<estado>.html), no interacciones.
12
+ Trazabilidad: épica {{EPICA}} · historias {{HISTORIAS}} · registrada en ../manifest.json
13
+ -->
14
+ <html lang="es">
15
+ <head>
16
+ <meta charset="utf-8">
17
+ <meta name="viewport" content="width=device-width, initial-scale=1">
18
+ <title>{{TITULO_PANTALLA}} — prototipo</title>
19
+ <link rel="stylesheet" href="../tokens.css">
20
+ <style>
21
+ /* Estilos propios de esta pantalla: SOLO composición/layout.
22
+ Valores visuales (color, fuente, spacing, radio, sombra) → var(--token). */
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <!-- región: navegación / cabecera -->
27
+
28
+ <!-- región: contenido principal (composición según la HU y el patrón de layout del sistema) -->
29
+
30
+ <!-- región: paneles secundarios / laterales (si el diseño los declara) -->
31
+
32
+ <!-- región: pie / acciones globales (si el diseño lo declara) -->
33
+ </body>
34
+ </html>
@@ -0,0 +1,42 @@
1
+ # Dirección estética (modo greenfield)
2
+
3
+ Protocolo para fijar la identidad visual **antes** de generar pantallas, cuando discovery solo
4
+ entrega docs de texto. La decisión estética es **humana**; la skill produce opciones y destila.
5
+
6
+ ## 1. ¿Hay manual de marca?
7
+
8
+ Pregunta primero si el consumidor tiene manual de marca / brand guidelines / design system previo.
9
+
10
+ - **Sí** → destila los tokens directamente de ese insumo (paleta exacta, tipografías, reglas de
11
+ uso) a `DESIGN.md` + `tokens.css`, con **Procedencia: manual de marca**. **No** generes
12
+ variantes: la identidad ya está decidida. Salta al paso 4.
13
+ - **No** → continúa con variantes (pasos 2-3).
14
+
15
+ ## 2. Elegir la pantalla clave
16
+
17
+ Una sola pantalla para el ejercicio: la más **representativa del journey** (la que un usuario ve
18
+ más tiempo o la que concentra más componentes distintos — típicamente la pantalla principal de
19
+ trabajo tras entrar). Evita pantallas triviales: no discriminan entre direcciones.
20
+
21
+ ## 3. Generar 2-3 direcciones y someterlas a elección humana
22
+
23
+ 1. Genera la pantalla clave en **2-3 direcciones visuales genuinamente distintas** — que difieran
24
+ en paleta, tipografía y densidad/tono (p.ej. sobria-densa · aireada-amable · contrastada-enérgica),
25
+ no tres matices del mismo gris. Cada dirección respeta el mismo contenido y la misma HU.
26
+ 2. Móntalas **lado a lado en un único HTML comparador** autocontenido (una columna por dirección,
27
+ con nombre y rasgos clave de cada una) y ábrelo en el navegador del usuario.
28
+ 3. El humano elige. Se permite **mezclar** ("la paleta de A con la tipografía de B") si lo pide.
29
+ 4. El comparador es un artefacto de trabajo: puede guardarse en `docs/05-prototipo/` como
30
+ `_direcciones.html` para la trazabilidad de la decisión, pero **no** entra al manifest.
31
+
32
+ ## 4. Destilar `DESIGN.md` + `tokens.css`
33
+
34
+ De la dirección elegida (o del manual de marca):
35
+ - `tokens.css` — custom properties: paleta completa (con variantes de énfasis/estado), familias y
36
+ escala tipográfica, spacing scale, radios, sombras.
37
+ - `DESIGN.md` (`assets/DESIGN.md.template`) — la prosa: identidad y por qué se eligió, tablas de
38
+ tokens con uso, reglas de componentes, **Don'ts** explícitos y **Procedencia** (dirección elegida
39
+ + fecha).
40
+
41
+ A partir de aquí, **toda** pantalla del prototipo importa `tokens.css` y no introduce valores
42
+ fuera de tokens; el self-check (`self-check.md`) lo verifica.