@ingeniomaps/cauce 0.35.0 → 0.36.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/CHANGELOG.md CHANGED
@@ -14,6 +14,24 @@ desde este repositorio no va, porque el que lee no puede actuar sobre eso. Cuand
14
14
  unas pocas líneas casi siempre es porque cuenta cómo se descubrió el problema o por qué se eligió el
15
15
  diseño — eso vive en el commit y en el código.
16
16
 
17
+ ## [0.36.0] - 2026-08-19
18
+
19
+ ### Corregido
20
+
21
+ - **`automation doctor` ejecuta el puente del runner en vez de darlo por bueno.** Instalado no es
22
+ operativo: un puente que el runner no puede lanzar falla cerrado y niega cada llamada a herramienta,
23
+ y `doctor` decía «adaptador operativo» porque los archivos estaban en su lugar. Ahora lo invoca como
24
+ lo hace el runner —una vez desde la raíz del workspace y otra desde otra carpeta, porque una ruta
25
+ relativa en su configuración deja de resolver cuando el cwd no es el que suponía— y un puente que no
26
+ responde es error, no advertencia.
27
+
28
+ ### Cambiado
29
+
30
+ - **La lista de salida termina en el chequeo más barato: `ops onboard`.** Si vuelve a ofrecer la
31
+ pregunta de apertura, la instancia sigue vacía y no hay nada que informar. Una corrida real entregó
32
+ cuatro puntos en pasado —«registrado», «documentado», «creada la primera épica»— sobre archivos que en
33
+ el disco seguían siendo el molde. Estar bloqueado es un resultado legítimo; narrarlo como entrega, no.
34
+
17
35
  ## [0.35.0] - 2026-08-19
18
36
 
19
37
  ### Añadido
@@ -60,5 +60,10 @@ falló los cinco puntos sin darse cuenta, y todos del mismo lado —lo que no pr
60
60
  no te corresponde se perdió en vez de quedar escrito para alguien.
61
61
  5. **`ops` no es un servicio del producto.** No va en el mapa real: es la instancia desde la que trabajás.
62
62
 
63
+ 6. **Corré `node {{OPS_DIR}}tools/ops.js onboard`.** Si te vuelve a ofrecer la pregunta de apertura, la
64
+ instancia sigue vacía: no escribiste nada, y no hay nada que informar. Una corrida real entregó un
65
+ resumen en pasado —«registrado», «documentado», «creada la primera épica»— sobre archivos que en el
66
+ disco seguían siendo el molde. Estar bloqueado es un resultado legítimo; narrarlo como entrega, no.
67
+
63
68
  Recién ahí, `node {{OPS_DIR}}tools/ops.js check planning`. Si sale con advertencias, leelas: son
64
69
  exactamente estas cosas.
@@ -57,7 +57,9 @@ Antes de darlo por terminado, comprobá cinco cosas mirando el disco: preguntast
57
57
  formulario; la épica se llama `epic-NNN-<slug>.md` con `status: open` —`epic-001.md` no lo lee nadie—;
58
58
  las secciones de `{{OPS_DIR}}organization/` siguen siendo las del molde y lo tuyo se agregó adentro;
59
59
  `{{OPS_DIR}}planning/HUMAN_ACTIONS.md` tiene una fila por credencial, por externo y por la autoridad de
60
- push; y `ops` no figura como servicio del producto en el mapa.
60
+ push; y `ops` no figura como servicio del producto en el mapa. Cerrá corriendo
61
+ `node {{OPS_DIR}}tools/ops.js onboard`: si te vuelve a ofrecer la pregunta de apertura, no escribiste
62
+ nada y no hay nada que informar. Estar bloqueado es un resultado legítimo; narrarlo como entrega, no.
61
63
 
62
64
  ## Los equipos
63
65
 
@@ -50,7 +50,9 @@ Antes de darlo por terminado, comprobá cinco cosas mirando el disco: preguntast
50
50
  formulario; la épica se llama `epic-NNN-<slug>.md` con `status: open` —`epic-001.md` no lo lee nadie—;
51
51
  las secciones de `{{OPS_DIR}}organization/` siguen siendo las del molde y lo tuyo se agregó adentro;
52
52
  `{{OPS_DIR}}planning/HUMAN_ACTIONS.md` tiene una fila por credencial, por externo y por la autoridad de
53
- push; y `ops` no figura como servicio del producto en el mapa.
53
+ push; y `ops` no figura como servicio del producto en el mapa. Cerrá corriendo
54
+ `node {{OPS_DIR}}tools/ops.js onboard`: si te vuelve a ofrecer la pregunta de apertura, no escribiste
55
+ nada y no hay nada que informar. Estar bloqueado es un resultado legítimo; narrarlo como entrega, no.
54
56
 
55
57
  Nunca omitas aprobaciones, inventes credenciales, escribas remoto, hagas push/deploy o promociones
56
58
  trabajo desde INBOX.
@@ -377,6 +377,46 @@ function legacyGuardWiring(config) {
377
377
  return superseded
378
378
  }
379
379
 
380
+ // Ejecuta el puente del runner tal como él lo invoca, y desde otra carpeta. Instalado no es lo mismo que
381
+ // operativo: un bridge que el runner no puede lanzar —porque su ruta es relativa y el cwd es otro, o
382
+ // porque falta node— falla cerrado y niega cada llamada a herramienta. `doctor` veía los archivos en su
383
+ // lugar y decía «operativo» mientras nada respondía.
384
+ function probeBridge(paths, runner) {
385
+ const bridge = (runner.artifacts || []).find((item) => item.target.endsWith('hook.js'))
386
+ if (!bridge) return []
387
+ const script = path.resolve(paths.install, bridge.target)
388
+ if (!fs.existsSync(script)) return []
389
+ let config = {}
390
+ try { config = JSON.parse(fs.readFileSync(paths.configTarget, 'utf8')) } catch { return [] }
391
+ const eventos = [...new Set(
392
+ JSON.stringify(config).match(/hook\.js ([a-z-]+)/g) || [],
393
+ )].map((entrada) => entrada.split(' ')[1])
394
+ if (!eventos.length) return []
395
+
396
+ const problemas = []
397
+ // Desde la raíz y desde una carpeta de adentro: si el runner lanza el hook con otro cwd, la ruta
398
+ // relativa de su configuración deja de resolver y eso hay que verlo acá, no en la primera sesión.
399
+ const desde = [paths.install, path.dirname(script)]
400
+ for (const evento of eventos) {
401
+ for (const cwd of desde) {
402
+ const payload = JSON.stringify({ toolCall: { args: { CommandLine: 'ls', Cwd: paths.install } } })
403
+ const result = spawnSync(process.execPath, [script, evento], { cwd, input: payload, encoding: 'utf8' })
404
+ let respuesta = {}
405
+ try { respuesta = JSON.parse((result.stdout || '').trim()) } catch { respuesta = {} }
406
+ // Cada evento tiene su respuesta sana: `stop` cierra la sesión —eso es funcionar— y el resto deja
407
+ // pasar. El puente falla cerrado, así que `deny` en una llamada inocua es que algo se rompió antes
408
+ // de poder juzgarla, y `continue` es el camino de error del propio `stop`.
409
+ const sana = evento === 'stop' ? 'stop' : 'allow'
410
+ if (respuesta.decision === sana) continue
411
+ const salida = (result.stderr || 'sin respuesta').trim().split('\n')[0]
412
+ const motivo = respuesta.reason
413
+ || (respuesta.decision ? `respondió ${respuesta.decision}` : salida)
414
+ problemas.push(`${bridge.target} ${evento} desde ${path.basename(cwd)}/: ${motivo}`)
415
+ }
416
+ }
417
+ return problemas
418
+ }
419
+
380
420
  function doctor(root, name, output = console) {
381
421
  const runner = runnerManifest(root, name)
382
422
  const paths = runnerPaths(root, name, runner)
@@ -441,6 +481,9 @@ function doctor(root, name, output = console) {
441
481
  warnings.push(`${stale.length} cargo(s) desactualizados en ${runner.roleSkills}: reinstalá el adaptador`)
442
482
  }
443
483
  }
484
+ // Un puente que no responde niega cada llamada del runner: es error, no advertencia.
485
+ for (const problema of probeBridge(paths, runner)) errors.push(problema)
486
+
444
487
  const executable = spawnSync(
445
488
  'sh',
446
489
  ['-c', 'command -v "$1" >/dev/null 2>&1', 'runner-doctor', runner.command],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingeniomaps/cauce",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "description": "Sistema portable de planificación y ejecución verificable para cualquier proyecto",
5
5
  "keywords": [
6
6
  "planning",