@ingeniomaps/cauce 0.81.0 → 0.83.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 +248 -0
- package/automatization/hooks/README.md +10 -6
- package/automatization/hooks/guard-ops-config-shell.sh +3 -0
- package/automatization/hooks/guard-ops-config.sh +3 -0
- package/automatization/runners/antigravity/rules/cauce.md +7 -2
- package/automatization/runners/claude/CLAUDE.md +1 -4
- package/automatization/runners/codex/AGENTS.md +7 -3
- package/automatization/runners/gemini/GEMINI.md +1 -4
- package/automatization/shared/inbox.js +47 -0
- package/automatization/workflows/agent-eval.js +1 -1
- package/automatization/workflows/autobuild.js +56 -18
- package/automatization/workflows/flow.js +45 -8
- package/automatization/workflows/onboard.js +32 -3
- package/engine/automation/index.js +19 -4
- package/engine/automation/rules.js +122 -0
- package/engine/automation/runners.js +3 -1
- package/engine/cli/instance.js +35 -6
- package/engine/cli/planning.js +16 -2
- package/engine/config/validate.js +53 -3
- package/engine/core/onboarding.js +37 -7
- package/engine/core/ownership.js +64 -1
- package/engine/core/scan.js +23 -9
- package/engine/hooks/approval.js +50 -15
- package/engine/hooks/chat.js +122 -11
- package/engine/hooks/input.js +1 -11
- package/engine/hooks/ops-config.js +110 -0
- package/engine/hooks/push.js +194 -0
- package/engine/hooks/run.js +17 -2
- package/engine/hooks/secrets-shell.js +13 -2
- package/engine/hooks/self-approval.js +93 -13
- package/engine/hooks/shell.js +13 -11
- package/engine/integrations/registry.js +13 -1
- package/engine/planning/inbox.js +36 -0
- package/engine/planning/parser.js +22 -9
- package/engine/planning/recurring.js +13 -2
- package/engine/schemas/ops-config.schema.json +21 -0
- package/package.json +1 -1
- package/template/AGENTS.md +13 -5
- package/template/gitignore +5 -0
- package/template/planning/INBOX.md +2 -1
- package/template/planning/RECURRING.md +6 -5
- package/template/planning/delivery/teamwork.md +1 -1
- package/template/planning/rules/system/commits.md +3 -1
|
@@ -405,18 +405,31 @@ function readWips(dir) {
|
|
|
405
405
|
// La plantilla no traía ningún ejemplo, así que quien escribía viñetas planas veía cero ítems sobre un
|
|
406
406
|
// archivo con doce y nada se lo decía. `skipped` es lo que vuelve visible esa diferencia.
|
|
407
407
|
function readInbox(dir) {
|
|
408
|
-
const
|
|
408
|
+
const { heads, skipped } = inboxSections(dir)
|
|
409
|
+
return { ...Object.fromEntries(Object.entries(heads).map(([key, names]) => [key, names.length])), skipped }
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Los nombres en negrita de cada sección, que es lo que un recorrido necesita para no volver a escribir
|
|
413
|
+
// lo que ya está: el nombre y no la entrada, porque pasar el archivo entero a cada tarea cuesta lo que
|
|
414
|
+
// el INBOX pesa (caso 101).
|
|
415
|
+
function inboxHeads(dir) {
|
|
416
|
+
return inboxSections(dir).heads
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function inboxSections(dir) {
|
|
420
|
+
const heads = { deuda: [], ideas: [], propuestas: [], lecciones: [] }
|
|
421
|
+
let skipped = 0
|
|
409
422
|
for (const part of read(path.join(dir, 'INBOX.md')).split(/^##\s+/m)) {
|
|
410
423
|
const title = part.split('\n')[0]
|
|
411
424
|
const bullets = (part.match(/^[-*]\s+(?:\[[ xX]\]\s+)?/gm) || []).length
|
|
412
|
-
const
|
|
413
|
-
if (/Deuda|Ideas|Visi[oó]n|Propuestas|Lecciones/i.test(title))
|
|
414
|
-
if (/Deuda/i.test(title))
|
|
415
|
-
if (/Ideas|Visi[oó]n/i.test(title))
|
|
416
|
-
if (/Propuestas/i.test(title))
|
|
417
|
-
if (/Lecciones/i.test(title))
|
|
425
|
+
const names = [...part.matchAll(/^[-*]\s+(?:\[[ xX]\]\s+)?\*\*([^*\n]*)/gm)].map((hit) => hit[1].trim())
|
|
426
|
+
if (/Deuda|Ideas|Visi[oó]n|Propuestas|Lecciones/i.test(title)) skipped += bullets - names.length
|
|
427
|
+
if (/Deuda/i.test(title)) heads.deuda = names
|
|
428
|
+
if (/Ideas|Visi[oó]n/i.test(title)) heads.ideas = names
|
|
429
|
+
if (/Propuestas/i.test(title)) heads.propuestas = names
|
|
430
|
+
if (/Lecciones/i.test(title)) heads.lecciones = names
|
|
418
431
|
}
|
|
419
|
-
return
|
|
432
|
+
return { heads, skipped }
|
|
420
433
|
}
|
|
421
434
|
|
|
422
435
|
module.exports = {
|
|
@@ -424,5 +437,5 @@ module.exports = {
|
|
|
424
437
|
TASK_LINE, TASK_LINE_ANY_LANE,
|
|
425
438
|
read, section, withoutComments, frontmatter, readEpics, readBacklog, readDone, readWip, readWips, wipName,
|
|
426
439
|
acceptanceConditions, tableRows, taskFromLine,
|
|
427
|
-
readInbox, readHumanActions,
|
|
440
|
+
readInbox, inboxHeads, readHumanActions,
|
|
428
441
|
}
|
|
@@ -19,6 +19,10 @@ const CADENCES = { mensual: 1, trimestral: 3, semestral: 6, anual: 12 }
|
|
|
19
19
|
|
|
20
20
|
const ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
|
|
21
21
|
const DATE = /^\d{4}-\d{2}-\d{2}$/
|
|
22
|
+
// La fila que el molde trae activa escribe su `Desde` como marcador, porque la fecha depende del día en
|
|
23
|
+
// que se crea la instancia y la reemplazan `init` y `upgrade`. Sin resolver sólo existe en el molde
|
|
24
|
+
// mismo —el que `npm run check` valida en el toolkit—, y ahí no vence: `status` la descarta por fecha.
|
|
25
|
+
const PLACEHOLDER = /^\{\{[A-Z_]+\}\}$/
|
|
22
26
|
// `- **qué** AAAA-MM-DD — razón`. El nombre en negrita adelante es la misma convención del INBOX, y por
|
|
23
27
|
// el mismo motivo: es con lo que se cita la fila desde otro lado.
|
|
24
28
|
const POSTPONEMENT = /^-\s+\*\*([^*]+)\*\*\s+(\S+)\s+[—-]\s+(.+)$/
|
|
@@ -77,7 +81,7 @@ function validate({ exists, rows, postponements }) {
|
|
|
77
81
|
if (!CADENCES[row.cadence]) {
|
|
78
82
|
errors.push(`${at}: cadencia "${row.cadence}" fuera de ${Object.keys(CADENCES).join(' | ')}`)
|
|
79
83
|
}
|
|
80
|
-
if (!DATE.test(row.since)) errors.push(`${at}: Desde debe ser AAAA-MM-DD`)
|
|
84
|
+
if (!DATE.test(row.since) && !PLACEHOLDER.test(row.since)) errors.push(`${at}: Desde debe ser AAAA-MM-DD`)
|
|
81
85
|
// Se juzga la línea armada y no la celda suelta: lo que se promueve es esa línea, y quien la va a
|
|
82
86
|
// leer es el mismo lector de BACKLOG. Una celda que pasa acá y una línea que BACKLOG rechaza es el
|
|
83
87
|
// error que aparece un mes después, con la tarea ya pegada.
|
|
@@ -145,4 +149,11 @@ function warnings(state) {
|
|
|
145
149
|
return lines
|
|
146
150
|
}
|
|
147
151
|
|
|
148
|
-
|
|
152
|
+
// Los marcadores de fecha del molde, resueltos para una instancia que nace hoy. `Desde` es la primera
|
|
153
|
+
// fecha de vencimiento y no el día en que se declara la fila, así que va un período después: con la
|
|
154
|
+
// fecha de hoy la fila nacía «vence hoy» (caso 106). Trimestral es la cadencia de la fila del molde.
|
|
155
|
+
function sinceValues(today) {
|
|
156
|
+
return { '{{INBOX_SINCE}}': addMonths(today, CADENCES.trimestral) }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = { FILE, read, validate, status, warnings, taskLine, sinceValues }
|
|
@@ -83,6 +83,15 @@
|
|
|
83
83
|
},
|
|
84
84
|
"allowPush": {
|
|
85
85
|
"type": "boolean"
|
|
86
|
+
},
|
|
87
|
+
"pushToLiveBranches": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"description": "Las ramas vivas —main, master y la rama por defecto de cada remoto— en las que se puede publicar. Sin nombrarla acá, ni allowPush ni una orden en el chat llegan a una rama viva. Nombres exactos, sin patrones.",
|
|
90
|
+
"items": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"pattern": "^[^\\s*?\\[]+$"
|
|
93
|
+
},
|
|
94
|
+
"uniqueItems": true
|
|
86
95
|
}
|
|
87
96
|
},
|
|
88
97
|
"additionalProperties": false
|
|
@@ -102,6 +111,18 @@
|
|
|
102
111
|
"minItems": 1
|
|
103
112
|
}
|
|
104
113
|
}
|
|
114
|
+
},
|
|
115
|
+
"inbox": {
|
|
116
|
+
"type": "object",
|
|
117
|
+
"description": "El aviso de tamaño del INBOX. `check` advierte —nunca falla— cuando `planning/INBOX.md` pasa de este número de líneas: el INBOX es de la persona, y lo que el aviso pide es recorrerlo, no dejar de escribir.",
|
|
118
|
+
"additionalProperties": false,
|
|
119
|
+
"properties": {
|
|
120
|
+
"warnLines": {
|
|
121
|
+
"type": "integer",
|
|
122
|
+
"minimum": 1,
|
|
123
|
+
"description": "Líneas a partir de las cuales `check` avisa. Por defecto 300."
|
|
124
|
+
}
|
|
125
|
+
}
|
|
105
126
|
}
|
|
106
127
|
},
|
|
107
128
|
"additionalProperties": false
|
package/package.json
CHANGED
package/template/AGENTS.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Este archivo gobierna el qué y el cuándo. `planning/PROTOCOL.md` gobierna el flujo y
|
|
4
4
|
`planning/rules/` el cómo: sus reglas rigen cada tarea y se leen antes de empezar, no cuando algo sale
|
|
5
|
-
mal.
|
|
5
|
+
mal. Este archivo, el protocolo y `planning/rules/system/` los mantiene Cauce y valen para cualquier
|
|
6
|
+
proyecto; las reglas propias viven junto a `system/`, y donde una de ellas sobrescribe, contradice o
|
|
7
|
+
restringe una del sistema, rige la del proyecto. Lo que este proyecto tiene de propio
|
|
6
8
|
—su mapa, sus integraciones, hasta dónde llega la autonomía acá— vive en `organization/workspace.md`.
|
|
7
9
|
|
|
8
10
|
## Qué sabe este proyecto y no este archivo
|
|
@@ -107,7 +109,9 @@ te frena es el peor para elegir bien.
|
|
|
107
109
|
cuando trabaja dentro de un recorrido; lo que vos pedís directo no se frena. Nombrá lo que querés que
|
|
108
110
|
toque —«borrá la prueba de altas», «reescribí la migración 004»— y pasa sin preguntarte de nuevo. Si tu
|
|
109
111
|
pedido no lo nombraba y algo se frena, el agente te dice qué y por qué: contestá «dale» y pasa exactamente
|
|
110
|
-
eso.
|
|
112
|
+
eso. **Con los gates de un commit se pregunta cada vez**, como con publicar: gobernanza, los gates del
|
|
113
|
+
stack y los lockfiles no heredan lo que autorizaste en un mensaje anterior, porque cada commit es otra
|
|
114
|
+
operación. Y `plan-first` no te pide un plan cuando el cambio lo pediste vos: el plan es para el trabajo que va
|
|
111
115
|
por tareas. Funciona en Claude Code, Codex y Gemini, que le avisan a Cauce cuando mandás un mensaje; en
|
|
112
116
|
Antigravity, y cuando nadie está en el chat —CI, un recorrido, un subagente—, queda el archivo de abajo.
|
|
113
117
|
|
|
@@ -250,12 +254,16 @@ aprobación que pide BR-OPS-002 — `context` la nombra para que la vea una pers
|
|
|
250
254
|
`BACKLOG.md` es esa persona.
|
|
251
255
|
|
|
252
256
|
Publicar es lo único de todo eso que este proyecto puede habilitar, y `runner.allowPush` en
|
|
253
|
-
`ops.config.json` es la autorización que R10 pide
|
|
254
|
-
|
|
257
|
+
`ops.config.json` es la autorización que R10 pide para las ramas de trabajo. La rama viva —`main`,
|
|
258
|
+
`master` o la rama por defecto del remoto— no la alcanza si el proyecto no la nombra en
|
|
259
|
+
`runner.pushToLiveBranches`, y un subagente no publica con ningún permiso. Sin la llave, pasa el push
|
|
260
|
+
que la persona pide en el chat nombrando el remoto y la rama, o el que ella aprueba contestando «dale».
|
|
261
|
+
Reescribir historia publicada no entra en el trato: un `push --force` se frena con la llave prendida o
|
|
262
|
+
apagada.
|
|
255
263
|
|
|
256
264
|
Eso rige sin que nadie escriba nada. Lo que este proyecto amplíe o restrinja va en
|
|
257
265
|
`organization/workspace.md`, con su razón; ninguna de esas prohibiciones se amplía ahí, y la
|
|
258
|
-
publicación tampoco se decide ahí: la
|
|
266
|
+
publicación tampoco se decide ahí: la deciden `allowPush` y `pushToLiveBranches`.
|
|
259
267
|
|
|
260
268
|
## Definición de terminado
|
|
261
269
|
|
package/template/gitignore
CHANGED
|
@@ -9,6 +9,11 @@ node_modules/
|
|
|
9
9
|
# máquina. Committearlo sería historia que nadie lee y un conflicto por commit.
|
|
10
10
|
planning/.verify-log
|
|
11
11
|
|
|
12
|
+
# El rastro de los push que pasaron por una aprobación. Local por lo mismo que el de arriba, y encima
|
|
13
|
+
# sólo crece: lo que contesta —quién autorizó qué rama y cuándo— se pregunta en la máquina donde corrió
|
|
14
|
+
# la sesión que publicó.
|
|
15
|
+
planning/.push-log
|
|
16
|
+
|
|
12
17
|
# El plan de cada runner. Es de la máquina que lo corre —existe para recuperar una ejecución
|
|
13
18
|
# interrumpida, y nadie más puede retomarla— y cambia en cada paso, así que compartirlo es un conflicto
|
|
14
19
|
# por commit a cambio de nada. Lo que el equipo sí necesita saber vive en `planning/claims/`.
|
|
@@ -14,7 +14,8 @@ Lo que separa a las cuatro secciones es el sujeto del ítem, no su tamaño ni su
|
|
|
14
14
|
|
|
15
15
|
- **Deuda** — un costo que ya estamos cargando en código nuestro, conocido y no bloqueante.
|
|
16
16
|
- **Ideas** — una pregunta abierta, sin respuesta propuesta.
|
|
17
|
-
- **Propuestas** — un cambio concreto del producto
|
|
17
|
+
- **Propuestas** — un cambio concreto del producto y su fix propuesto. La evidencia no se copia acá: se
|
|
18
|
+
cita dónde vive —el `done/` de la tarea, el informe—.
|
|
18
19
|
- **Lecciones** — sobre cómo trabajamos; es lo que alimenta reglas y propuestas de cargo.
|
|
19
20
|
|
|
20
21
|
Ideas y Propuestas se separan por si hay una respuesta propuesta. Deuda y Propuestas, por si el costo
|
|
@@ -16,7 +16,8 @@ vencimiento se calcula cuando alguien corre el CLI. Promover sigue siendo un act
|
|
|
16
16
|
- **Cada** — vocabulario cerrado: `mensual`, `trimestral`, `semestral`, `anual`. No hay expresiones de
|
|
17
17
|
cron, y esa ausencia es el enunciado: si hiciera falta una, lo que estarías declarando es otra cosa.
|
|
18
18
|
Tampoco hay `semanal`, porque el slug de cada vuelta tiene grano de mes.
|
|
19
|
-
- **Desde** — `AAAA-MM-DD`. Ancla la primera vuelta y después no se toca.
|
|
19
|
+
- **Desde** — `AAAA-MM-DD`. Ancla la primera vuelta y después no se toca. Es la primera fecha en que
|
|
20
|
+
vence, no el día en que se escribe la fila: con la fecha de hoy nace vencida.
|
|
20
21
|
- **Tarea y aceptación** — lo que se va a promover, con su aceptación observable escrita una sola vez y
|
|
21
22
|
con calma. Improvisada en cada vuelta, la misma recurrencia termina significando cosas distintas sin
|
|
22
23
|
que nadie lo decida.
|
|
@@ -47,12 +48,12 @@ slug son un error de `check`, y ese error llega un mes tarde.
|
|
|
47
48
|
|
|
48
49
|
| Qué | Cada | Desde | Tarea y aceptación observable |
|
|
49
50
|
|---|---|---|---|
|
|
51
|
+
| inbox | trimestral | {{INBOX_SINCE}} | Recorrer el INBOX entero. _Aceptación: ninguna viñeta queda sin decisión de promover, dejar o borrar._ (service: planning) |
|
|
50
52
|
|
|
51
53
|
<!--
|
|
52
|
-
| deps | mensual | 2026-09-01 | Actualizar dependencias. _Aceptación: `npm outdated` no deja una versión mayor sin decisión escrita y la puerta queda verde._ |
|
|
53
|
-
| accesos | trimestral | 2026-07-01 | Revisar quién tiene acceso a producción. _Aceptación: cada cuenta activa figura en `organization/`, y las demás están dadas de baja._ |
|
|
54
|
-
| costos | mensual | 2026-09-01 | Revisar el gasto de infraestructura del mes. _Aceptación: cada línea que subió más de 20% tiene una razón escrita._ |
|
|
55
|
-
| inbox | trimestral | 2026-08-01 | Recorrer el INBOX entero. _Aceptación: ninguna viñeta queda sin decisión de promover, dejar o borrar._ |
|
|
54
|
+
| deps | mensual | 2026-09-01 | Actualizar dependencias. _Aceptación: `npm outdated` no deja una versión mayor sin decisión escrita y la puerta queda verde._ (service: .) |
|
|
55
|
+
| accesos | trimestral | 2026-07-01 | Revisar quién tiene acceso a producción. _Aceptación: cada cuenta activa figura en `organization/`, y las demás están dadas de baja._ (service: organization) |
|
|
56
|
+
| costos | mensual | 2026-09-01 | Revisar el gasto de infraestructura del mes. _Aceptación: cada línea que subió más de 20% tiene una razón escrita._ (service: .) |
|
|
56
57
|
-->
|
|
57
58
|
|
|
58
59
|
## Postergaciones
|
|
@@ -11,7 +11,7 @@ choques entre dos personas —o entre dos agentes— salen de tratarlas igual.
|
|
|
11
11
|
|---|---|---|---|
|
|
12
12
|
| **Compartido** | `roadmap/`, `BACKLOG.md`, `INBOX.md`, `HUMAN_ACTIONS.md`, `done/`, reglas y ADR | cualquiera, en actos humanos | baja |
|
|
13
13
|
| **Coordinación** | `claims/`, un archivo por tarea tomada | uno por tarea | dos veces por tarea |
|
|
14
|
-
| **Local** | `wip/<runner>.md`, `.verify-log`, el árbol de trabajo | vos | continua |
|
|
14
|
+
| **Local** | `wip/<runner>.md`, `.verify-log`, `.push-log`, el árbol de trabajo | vos | continua |
|
|
15
15
|
|
|
16
16
|
La regla que los separa: **un archivo con más de un escritor tiene que cambiar poco; uno que cambia mucho
|
|
17
17
|
tiene que tener un solo escritor.** Cuando uno viola las dos a la vez, el equipo se pisa en cada commit.
|
|
@@ -65,7 +65,9 @@ dice qué había que hacer, no qué se apoyaba en lo que había.
|
|
|
65
65
|
|
|
66
66
|
Push, PR, merge, tags, deploy y rollback requieren la autorización configurada para el proyecto.
|
|
67
67
|
|
|
68
|
-
De esos seis, el motor comprueba uno: el push, contra `runner.allowPush
|
|
68
|
+
De esos seis, el motor comprueba uno: el push, contra `runner.allowPush` —que no llega a la rama viva
|
|
69
|
+
sin `runner.pushToLiveBranches`, ni a un subagente— o contra la orden que la persona da en el chat
|
|
70
|
+
nombrando el remoto y la rama. Reescribir historia publicada
|
|
69
71
|
no entra en esa autorización y se frena siempre, igual que `--amend`. Los otros cinco no tienen una
|
|
70
72
|
forma reconocible en un comando —un deploy es `kubectl`, `terraform`, un script o un botón— y los
|
|
71
73
|
sostiene esta regla y el review, no un guard.
|