@trycore/spec-build-harness 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "trycore-spec-build-harness",
4
4
  "displayName": "Trycore — Spec & Build Harness",
5
- "version": "0.11.0",
5
+ "version": "0.11.1",
6
6
  "description": "Arnés de construcción de dos loops (slice por épica + release gate) para Claude Code, con gates de calidad, estado compartido y OpenSpec. Compañero de @trycore/spec-product-flow. Agnóstico al proyecto.",
7
7
  "author": {
8
8
  "name": "Trycore",
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.11.0
1
+ 0.11.1
@@ -70,8 +70,9 @@ reviewer. Es el gate **no negociable**: sin él, no hay release.
70
70
  - Reporta **cada** gate medido con
71
71
  `bash .claude/hooks/build/release-ops.sh verdict <line> <gate> <pass|fail|na> --evidence-file f`
72
72
  (`na` cuando no aplica). El **agregado** lo hace el runtime: **parciales no promueven a `passed`**.
73
- - Con los seis reportados y en verde, recuérdale al humano que **el cierre es suyo**
74
- (`release-ops.sh close-hint <line>`): el agente no cierra releases.
73
+ - Con los seis reportados **en verde, el hub auto-cierra** (`closed_by: AUTO_6_OF_6_PASS`; lo
74
+ anuncia la respuesta del último `verdict`). Si la release queda degradada, el cierre es
75
+ decisión humana en la consola (`release-ops.sh close-hint <line>` imprime la política).
75
76
  - Algo ✗ → los hallazgos bloqueantes se corrigen **como un slice normal**
76
77
  (`/build:slice` / `building-a-slice`) y se **re-corre** el Release Gate.
77
78
 
@@ -83,5 +84,5 @@ reviewer. Es el gate **no negociable**: sin él, no hay release.
83
84
  vez por release (`O(releases)`).
84
85
  - **No dupliques el inner loop**: no se hace TDD ni se cierran gates por slice.
85
86
  - **`integration` con deps reales es obligatorio**; no se acepta con todo stubbeado.
86
- - **El agente reporta, el humano cierra** (spec §2): ninguna llamada de cierre con token de agente.
87
+ - **El agente reporta; el verde se auto-cierra; el degradado lo decide un humano** (política A-con-B): ninguna llamada de cierre con token de agente — el auto-cierre lo ejecuta el runtime al agregar, no el cliente.
87
88
  - Delega en `releasing-a-version`; **no** la reimplementa. Si algo contradice `METODOLOGIA.md`, gana la metodología.
@@ -35,7 +35,7 @@ _rel_evidence_json() {
35
35
 
36
36
  # ── verdict ──────────────────────────────────────────────────────────────────────────
37
37
  cmd_verdict() {
38
- local line="${1:-}" gate="${2:-}" estado="${3:-}" ev="" evfile="" mode payload resp status pfile
38
+ local line="${1:-}" gate="${2:-}" estado="${3:-}" ev="" evfile="" mode payload resp status pfile rel_status closed_by
39
39
  if [ -z "$line" ] || [ -z "$gate" ] || [ -z "$estado" ]; then usage; return $OPS_RC_USAGE; fi
40
40
  shift 3
41
41
  while [ $# -gt 0 ]; do
@@ -57,7 +57,7 @@ cmd_verdict() {
57
57
  esac
58
58
  mode="$(ops_mode)"
59
59
  [ "$mode" = "legacy" ] && { echo "LEGACY: escribe el gate en releases[] del fichero"; return $OPS_RC_LEGACY; }
60
- payload="{\"release_line\":$(ops_json_str "$line"),\"gate\":$(ops_json_str "$gate"),\"status\":$(ops_json_str "$estado"),\"evidence\":$(_rel_evidence_json "$ev" "$evfile")}"
60
+ payload="{\"release_line\":$(ops_json_str "$line"),\"gate\":$(ops_json_str "$gate"),\"status\":$(ops_json_str "$estado"),\"verdict\":$(ops_json_str "$(printf %s "$estado" | tr a-z A-Z)"),\"evidence\":$(_rel_evidence_json "$ev" "$evfile")}"
61
61
  if [ "$mode" = "dual" ]; then
62
62
  pfile="$(ops_tmpjson "$payload")" || return $OPS_RC_OFFLINE
63
63
  ops_mirror release_verdict_reported "$pfile" || echo "⚠ veredicto de release no espejado; el comparador dual lo marcará" >&2
@@ -69,7 +69,30 @@ cmd_verdict() {
69
69
  case "$status" in
70
70
  200|201|202)
71
71
  echo "veredicto de release registrado: $gate=$estado en $line"
72
- echo "El cierre de la release NO lo hace el agente: lo hace un humano en la consola (spec §2)."
72
+ # [A-con-B] El hub auto-cierra la release cuando el ÚLTIMO veredicto de cada uno
73
+ # de los 6 gates canónicos es PASS (closed_by=AUTO_6_OF_6_PASS, queda en el audit
74
+ # trail). Con algún fail/partial el cierre sigue siendo decisión HUMANA en la
75
+ # consola. Fail-open: un hub previo sin release_status cae al recordatorio.
76
+ rel_status="$(printf '%s' "$resp" | python3 -c 'import json,sys
77
+ try:
78
+ print(json.load(sys.stdin).get("release_status") or "")
79
+ except Exception:
80
+ print("")' 2>/dev/null)"
81
+ closed_by="$(printf '%s' "$resp" | python3 -c 'import json,sys
82
+ try:
83
+ print(json.load(sys.stdin).get("closed_by") or "")
84
+ except Exception:
85
+ print("")' 2>/dev/null)"
86
+ case "$rel_status" in
87
+ PASSED)
88
+ echo "✅ release «${line}» CERRADA por el hub: 6/6 gates PASS (closed_by: ${closed_by:-AUTO_6_OF_6_PASS})." ;;
89
+ FAILED)
90
+ echo "⛔ release «${line}» en FAILED en el hub; los hallazgos bloqueantes se corrigen como slice y se re-mide." ;;
91
+ IN_PROGRESS)
92
+ echo "release aún IN_PROGRESS: el hub auto-cierra al llegar el 6/6 PASS; con algún fail/partial el cierre es decisión humana en la consola (spec §2)." ;;
93
+ *)
94
+ echo "El hub auto-cierra con 6/6 PASS; si algún gate queda en fail, el cierre/decisión es humano en la consola (spec §2)." ;;
95
+ esac
73
96
  return $OPS_RC_OK ;;
74
97
  422)
75
98
  echo "⛔ 422 el servidor rechazó el veredicto: $resp — no reintentes; corrige y vuelve a medir." >&2
@@ -96,11 +119,13 @@ cmd_close_hint() {
96
119
  local line="${1:-}"
97
120
  [ -n "$line" ] || { usage; return $OPS_RC_USAGE; }
98
121
  cat <<HINT
99
- El cierre de la release «${line}» es un acto de GOBIERNO HUMANO (spec §2):
122
+ Política de cierre de la release «${line}» (A-con-B):
100
123
  1. El agente reporta los 6 veredictos con \`release-ops.sh verdict $line <gate> <estado>\`.
101
- 2. El runtime agrega: parciales NO promueven a passed.
102
- 3. Una persona con permiso cierra la línea en la consola del hub.
103
- El arnés no cierra releases ni con token de agente: esa superficie exige PDP.
124
+ 2. El runtime agrega y AUTO-CIERRA en verde: con el último veredicto de los 6 gates
125
+ canónicos en PASS la release pasa a PASSED (closed_by=AUTO_6_OF_6_PASS, audit trail).
126
+ 3. Los casos DEGRADADOS (algún fail/partial, deudas por decidir) siguen siendo gobierno
127
+ humano: una persona con permiso decide y cierra en la consola del hub (PDP).
128
+ Parciales no promueven; el gobierno humano vive donde hay decisión real, no en el clic.
104
129
  HINT
105
130
  return $OPS_RC_OK
106
131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trycore/spec-build-harness",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
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": {
@@ -24,9 +24,12 @@ de épicas de la release son las que vas a auditar en bloque.
24
24
  `dual`/`runtime` la release vive en el runtime y cada gate se reporta con
25
25
  `bash .claude/hooks/build/release-ops.sh verdict <line> <gate> <pass|fail|na>`; en `legacy`, en el
26
26
  array `releases[]` del fichero local (protocolo en `building-a-slice/references/state-protocol.md`).
27
- - **Reportar es de agentes; cerrar es de humanos** (spec §2). El agente mide y reporta los seis
28
- veredictos; **la release la cierra una persona en la consola del hub** el token de agente no
29
- puede hacerlo. `release-ops.sh close-hint <line>` imprime el recordatorio.
27
+ - **Reportar es de agentes; el cierre en verde es del runtime; el degradado es humano** (política
28
+ A-con-B). El agente mide y reporta los seis veredictos; con el último veredicto de los 6 gates
29
+ canónicos en PASS **el hub auto-cierra** la release (`closed_by: AUTO_6_OF_6_PASS`, audit trail).
30
+ Si algún gate queda `fail`/`partial` o hay deudas por decidir, **el cierre es decisión humana en
31
+ la consola** — el gobierno humano vive donde hay decisión real, no en el clic de un resultado
32
+ derivable. `release-ops.sh close-hint <line>` imprime la política.
30
33
  - **Sobre el diff acumulado**: el alcance es el rango de commits de todas las épicas de la release
31
34
  (desde el merge anterior a la primera épica de la release hasta `main`).
32
35
  - **Delega en subagentes** (devuelven síntesis, protegen el contexto).
@@ -69,8 +72,8 @@ Checklist de cierre: `references/release-dod.md`.
69
72
  no aplique — p. ej. `ux` en una release sin UI). Un `rc 6` es el servidor rechazando: muestra su
70
73
  razón, no reintentes. Sin red, el veredicto **no** tiene camino por la cola offline (`rc 5`,
71
74
  issue #44): reintenta el mismo comando cuando vuelva la red, antes de pedir el cierre.
72
- 6. Cuando los seis estén reportados, **avisa al humano para el cierre**
73
- (`release-ops.sh close-hint R1-mvp`): el agente no cierra releases. Si algún gate quedó `fail`,
75
+ 6. Con los seis reportados **en verde, el hub cierra solo** (la respuesta del último `verdict`
76
+ anuncia `release CERRADA … closed_by: AUTO_6_OF_6_PASS`) no hay paso manual. Si algún gate quedó `fail`,
74
77
  lista los hallazgos bloqueantes; el usuario los corrige como un slice normal (fix en
75
78
  `building-a-slice`) y se re-corre el Release Gate.
76
79
 
@@ -9,7 +9,7 @@
9
9
  //
10
10
  // READ-ONLY sobre el estado: esta plantilla devuelve un diagnóstico; la skill
11
11
  // releasing-a-version es la ÚNICA que reporta los veredictos (release-ops.sh verdict por gate;
12
- // en modo legacy, una entrada en releases[] del fichero). El agregado y el cierre son del
12
+ // en modo legacy, una entrada en releases[] del fichero). El agregado y el auto-cierre en verde son del
13
13
  // runtime/humano. Parciales NO promueven a passed. Si METODOLOGIA.md (§5) contradice algo aquí,
14
14
  // gana la metodología.
15
15
  //
@@ -150,11 +150,11 @@ const reviewersGreen = reviews.filter(Boolean).every((r) => r.na || r.value ===
150
150
  const status = (reviewersGreen && gates.integration === true) ? 'passed' : 'failed'
151
151
 
152
152
  return {
153
- status, // reportado por releasing-a-version vía release-ops.sh verdict (agregado y cierre son del runtime/humano).
153
+ status, // reportado por releasing-a-version vía release-ops.sh verdict (agregado y auto-cierre en verde son del runtime; el degradado lo decide un humano).
154
154
  gates, // {security, smell, ux, coherence, stack_arch, integration}
155
155
  blocking: [
156
156
  ...reviews.filter((r) => r && r.value === false).map((r) => ({ gate: r.gate, error: r.error || null, findings: r.findings || [] })),
157
157
  ...(gates.integration === true ? [] : [{ gate: 'integration', findings: (integration && integration.findings) || [] }]),
158
158
  ],
159
- note: 'Read-only. releasing-a-version reporta un veredicto por gate (release-ops.sh verdict). El runtime agrega y un humano cierra la release en la consola. Parciales NO promueven a passed.',
159
+ note: 'Read-only. releasing-a-version reporta un veredicto por gate (release-ops.sh verdict). El runtime agrega y AUTO-CIERRA con 6/6 PASS (closed_by: AUTO_6_OF_6_PASS); el caso degradado lo decide un humano en la consola. Parciales NO promueven a passed.',
160
160
  }