@ingeniomaps/cauce 0.77.0 → 0.78.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,44 @@ 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.78.0] - 2026-09-10
18
+
19
+ ### Corregido
20
+
21
+ - **Las puertas que revisan un recorrido leen los literales de regex.** Tres pruebas analizan cada
22
+ workflow como texto —que su `meta` sea un literal puro, que no llame a nada inexistente y que no use un
23
+ nombre que no declaró— y ninguna sabía qué es un regex. La barra escapada que cierra uno deja un `//`
24
+ literal, que se leía como el arranque de un comentario: a partir de ahí se perdía el resto de la línea,
25
+ en silencio. Dos recorridos del catálogo tenían cuatro líneas así.
26
+
27
+ **Qué cambia para vos**: nada en lo que recibís, y sí en lo que se puede confiar. Un recorrido propio
28
+ con un regex adentro ahora se revisa entero en vez de a medias, y un `meta` con un regex o con una
29
+ interpolación —que hasta acá pasaba siempre, porque esa regla no podía dispararse— se rechaza.
30
+ - **El nombre de un banco de pruebas no trae nada que la prueba no haya pedido.** La suite aísla lo que
31
+ cada prueba mide filtrando la salida de `check`, y esa salida empieza con la ruta del banco: cuando el
32
+ sufijo aleatorio del directorio terminó en `adr`, un error ajeno entró a una prueba de plantillas de
33
+ ADR y la puso en rojo. Falla una vez cada muchas corridas y se lee como un hipo del entorno.
34
+
35
+ **Qué cambia para vos**: nada — es la suite del toolkit, no algo que recibas. Va acá porque una puerta
36
+ que falla al azar enseña a relanzar en vez de a mirar, y eso sí llega a quien la usa.
37
+
38
+ - **`autobuild` lee `blocked` por su valor y no por su verdad.** El campo del contexto es un vocabulario
39
+ de tres valores —vacío, `awaiting-review` y `blocked-on-human`—, y el recorrido lo probaba con un `if`
40
+ a secas. Dos consecuencias, y la que más costaba no era intermitente: una cola trabada por acciones
41
+ humanas frenaba con el motivo del checkpoint de hito y mandaba a mirar `AWAITING_REVIEW.md`, un archivo
42
+ que en ese escenario no existe. Ahora cada bloqueo para con su motivo y nombra su archivo:
43
+ `AWAITING_REVIEW.md` para el checkpoint, `HUMAN_ACTIONS.md` —con las tareas trabadas— para la cola.
44
+
45
+ La otra es la que se ve al azar: el agente que transcribe el contexto a veces entrega el vacío como la
46
+ cadena de dos comillas, y eso frenaba la corrida en la fase 1 sin que hubiera nada que resolver. Medido
47
+ sobre siete corridas de una instancia real: 3 de 24 lecturas llegaron así, y una de cada siete cayó en
48
+ la única lectura que consulta el campo. Las formas equivocadas del vacío se desenvuelven antes de leer.
49
+
50
+ Y lo que no está en el vocabulario ya no se adivina en ninguna de las dos direcciones: no se sigue como
51
+ si no hubiera bloqueo ni se inventa cuál es — para diciendo que el contexto llegó fuera del
52
+ vocabulario. El esquema declara los tres valores, igual que `lane`; eso documenta el contrato, y lo que
53
+ sostiene el arreglo es la lectura.
54
+
17
55
  ## [0.77.0] - 2026-09-10
18
56
 
19
57
  ### Corregido
@@ -38,6 +38,11 @@ const GATE = `${P}/AWAITING_REVIEW.md`
38
38
  const ROADMAP = `${P}/roadmap`
39
39
 
40
40
  // Estado de planning tal como lo emite `ops context --json`; ningún modelo parsea BACKLOG ni WIP.
41
+ // De a pares, y sin regex: una comilla dentro de un literal de regex desincroniza a las dos puertas que
42
+ // leen este archivo sin parsearlo —caso 084—, y de a pares es además lo correcto, porque una comilla
43
+ // suelta no es un envoltorio. Por eso también la escapada en vez de alternar el estilo de comillas.
44
+ const QUOTES = ['\'', '"']
45
+
41
46
  const CONTEXT = {
42
47
  type: 'object', additionalProperties: false,
43
48
  required: ['blocked', 'hasTask', 'wipActive', 'queued', 'cast', 'readOk'],
@@ -46,7 +51,13 @@ const CONTEXT = {
46
51
  // exactamente lo que un modelo completa cuando no tiene qué poner. Sin este campo esa invención se
47
52
  // lee igual que una cola terminada, y Pick la toma como permiso para promover.
48
53
  readOk: { type: 'boolean' },
49
- blocked: { type: 'string' }, hasTask: { type: 'boolean' }, wipActive: { type: 'boolean' },
54
+ // Vocabulario cerrado, igual que `lane` acá abajo, y por la misma razón: el motor emite tres valores
55
+ // y nada más —`ops context` los decide con un `existsSync` y un conteo—, así que dejarlo como texto
56
+ // libre le pedía a quien lo transcribe que acertara una convención invisible. Un modelo que rellena
57
+ // «el valor vacío» puede escribir la cadena vacía o **escribir las comillas**, y las dos satisfacían
58
+ // el esquema: medido en una instancia real, 3 de 24 lecturas llegaron como `"\"\""` (caso 083).
59
+ blocked: { type: 'string', enum: ['', 'awaiting-review', 'blocked-on-human'] },
60
+ hasTask: { type: 'boolean' }, wipActive: { type: 'boolean' },
50
61
  queued: { type: 'integer' }, slug: { type: 'string' }, hito: { type: 'string' },
51
62
  service: { type: 'string' }, acceptance: { type: 'string' }, epic: { type: 'string' },
52
63
  // Sin declararlo acá no llega: `additionalProperties: false` lo descartaría, y el aplanado de la
@@ -364,7 +375,28 @@ if (!planning) return stop('context-unavailable', `no se pudo leer el estado de
364
375
  // Que el agente conteste no significa que haya leído: el schema se completa igual con ceros. Parar acá
365
376
  // cuesta una corrida; seguir sobre una lectura fallida escribe en el BACKLOG, y eso no se revierte solo.
366
377
  if (!planning.readOk) return stop('context-unavailable', `${P} no se pudo leer; revisá la ruta y el cwd`)
367
- if (planning.blocked) return stop('awaiting-human-review', `${GATE} tiene un checkpoint humano sin resolver`)
378
+ // `blocked` se lee por su valor y no por su verdad. Como verdad, **cualquier** cadena no vacía frenaba la
379
+ // corrida con el mismo motivo, y eso mentía dos veces: con `blocked-on-human` —la cola trabada por
380
+ // acciones humanas, que es otra cosa— mandaba a mirar un gate que no existe, y eso no era intermitente;
381
+ // y con la cadena `""` que a veces llega del transcriptor, frenaba sin que hubiera nada que resolver.
382
+ //
383
+ // El vacío se normaliza antes porque sus formas equivocadas no son ambiguas: dos comillas o unos espacios
384
+ // no son ningún bloqueo legítimo. Lo que no se reconoce **no se adivina**: para diciendo que el contexto
385
+ // llegó fuera del vocabulario, que es lo que esta familia de casos —056, 074, 075— pide para lo que no se
386
+ // pudo determinar.
387
+ let blocker = String(planning.blocked || '').trim()
388
+ while (blocker.length > 1 && QUOTES.includes(blocker[0]) && blocker[blocker.length - 1] === blocker[0]) {
389
+ blocker = blocker.slice(1, -1).trim()
390
+ }
391
+ if (blocker === 'awaiting-review') {
392
+ return stop('awaiting-human-review', `${GATE} tiene un checkpoint humano sin resolver`)
393
+ }
394
+ if (blocker === 'blocked-on-human') {
395
+ return stop('blocked-on-human', `toda la cola espera una acción humana. Está en ${HUMAN}`
396
+ + `${(planning.blockedTasks || []).length ? `, sobre ${planning.blockedTasks.join(', ')}` : ''}`)
397
+ }
398
+ if (blocker) return stop('context-unavailable', `${P} contestó blocked=${JSON.stringify(planning.blocked)}, `
399
+ + 'que no es del vocabulario. No se sabe si hay bloqueo, así que no se sigue como si no lo hubiera.')
368
400
 
369
401
  let currentMilestone = planning.wipActive ? planning.hito : ''
370
402
  const completed = []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingeniomaps/cauce",
3
- "version": "0.77.0",
3
+ "version": "0.78.0",
4
4
  "description": "Sistema portable de planificación y ejecución verificable para cualquier proyecto",
5
5
  "keywords": [
6
6
  "planning",