@ingeniomaps/cauce 0.80.0 → 0.82.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 (48) hide show
  1. package/CHANGELOG.md +177 -0
  2. package/automatization/hooks/README.md +41 -11
  3. package/automatization/hooks/guard-chat.sh +5 -0
  4. package/automatization/hooks/guard-secrets-shell.sh +3 -0
  5. package/automatization/runners/antigravity/rules/cauce.md +7 -2
  6. package/automatization/runners/claude/CLAUDE.md +1 -4
  7. package/automatization/runners/claude/README.md +5 -4
  8. package/automatization/runners/claude/manifest.json +26 -1
  9. package/automatization/runners/claude/settings.json +8 -24
  10. package/automatization/runners/codex/AGENTS.md +7 -3
  11. package/automatization/runners/codex/README.md +4 -0
  12. package/automatization/runners/codex/hooks.json +7 -0
  13. package/automatization/runners/gemini/GEMINI.md +1 -4
  14. package/automatization/runners/gemini/README.md +5 -2
  15. package/automatization/runners/gemini/settings.json +11 -1
  16. package/automatization/shared/inbox.js +28 -0
  17. package/automatization/workflows/agent-eval.js +1 -1
  18. package/automatization/workflows/autobuild.js +52 -18
  19. package/automatization/workflows/flow.js +33 -8
  20. package/automatization/workflows/onboard.js +25 -3
  21. package/engine/automation/index.js +28 -5
  22. package/engine/automation/rules.js +122 -0
  23. package/engine/automation/runners.js +3 -1
  24. package/engine/cli/instance.js +35 -6
  25. package/engine/cli/planning.js +16 -2
  26. package/engine/config/validate.js +53 -3
  27. package/engine/core/onboarding.js +37 -7
  28. package/engine/core/ownership.js +64 -1
  29. package/engine/core/scan.js +23 -9
  30. package/engine/hooks/approval.js +40 -9
  31. package/engine/hooks/chat.js +171 -0
  32. package/engine/hooks/files.js +28 -16
  33. package/engine/hooks/input.js +8 -12
  34. package/engine/hooks/push.js +147 -0
  35. package/engine/hooks/run.js +23 -3
  36. package/engine/hooks/secrets-shell.js +62 -0
  37. package/engine/hooks/self-approval.js +31 -0
  38. package/engine/hooks/shell.js +37 -31
  39. package/engine/integrations/registry.js +13 -1
  40. package/engine/planning/inbox.js +36 -0
  41. package/engine/planning/parser.js +22 -9
  42. package/engine/planning/recurring.js +13 -2
  43. package/engine/schemas/ops-config.schema.json +21 -0
  44. package/package.json +1 -1
  45. package/template/AGENTS.md +23 -7
  46. package/template/planning/INBOX.md +2 -1
  47. package/template/planning/RECURRING.md +6 -5
  48. package/template/planning/rules/system/commits.md +3 -1
package/CHANGELOG.md CHANGED
@@ -14,6 +14,183 @@ 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.82.0] - 2026-09-11
18
+
19
+ ### Cambiado
20
+
21
+ - **`allowPush: true` ya no publica en la rama viva ni desde un subagente.** La llave dejaba pasar cualquier
22
+ push, `main` incluido, y el de un subagente igual que el tuyo. Ahora no alcanza a `main`, `master` ni a la
23
+ rama por defecto del remoto, y un subagente no publica con ningún permiso. Tampoco la alcanza una orden en
24
+ el chat.
25
+
26
+ **Qué cambia para vos**: si publicabas en la rama viva con `allowPush: true`, agregá
27
+ `"pushToLiveBranches": ["main"]` en `runner` de `ops.config.json`, o pegá `push origin main` en
28
+ `planning/.ops-approval` para un push puntual.
29
+
30
+ - **R10 y el párrafo de publicación de `AGENTS.md` se reescribieron.** Los dos decían que el push se
31
+ comprueba contra `runner.allowPush` y nada más; ahora dicen lo que de verdad comprueba el motor: esa llave
32
+ —que no llega a la rama viva sin `runner.pushToLiveBranches`, ni a un subagente— o la orden que la persona
33
+ da en el chat nombrando el remoto y la rama. Los dos archivos los mantiene Cauce, así que `upgrade` los
34
+ reemplaza enteros.
35
+
36
+ **Qué cambia para vos**: nada que hacer. Si tu equipo cita R10 en algún documento propio, el texto que
37
+ baja ahora es más largo y dice qué sostiene un guard y qué sostiene una persona.
38
+
39
+ ### Corregido
40
+
41
+ - **Nombrar algo en el chat ya no lo autoriza: hay que pedirlo.** Desde 0.81.0, lo que nombrabas en el chat
42
+ pasaba los guards aunque no lo pidieras: «¿para qué sirven las credentials?» dejaba leer `credentials`, y
43
+ «el .env tiene algo raro?» dejaba leer el `.env`. Ahora la frase que lo nombra tiene que pedir una acción
44
+ —«leé», «abrime», «borrá», «read»…—, y la negación sigue frenando como antes.
45
+
46
+ **Qué cambia para vos**: si pedís algo sin un verbo que Cauce reconozca, el agente te dice qué se frenó y
47
+ con un «dale» pasa.
48
+
49
+ - **`upgrade` ya no pisa un guard tuyo que se llama como uno nuevo del paquete.** Cuando una versión empezaba
50
+ a traer un nombre que ya usabas en `automatization/hooks/`, lo reemplazaba sin decir nada: 0.81.0 lo hizo
51
+ con `guard-chat.sh`. Ahora lo conserva y lo avisa; `--check` lo cuenta, y `--force` lo reemplaza diciéndolo.
52
+
53
+ **Qué cambia para vos**: si ves «ya existía y Cauce no lo entregó», renombrá tu guard y repetí `upgrade`;
54
+ mientras tanto, el guard del paquete con ese nombre no está instalado.
55
+
56
+ - **Un guard propio ya no aparece como «editado localmente».** `upgrade` registraba todo lo que había en
57
+ `automatization/hooks/`, así que editar un guard tuyo lo volvía una edición del molde: `upgrade --check`
58
+ salía con 1, `check` lo contaba como congelado, y `--force` anunciaba que lo descartaba sin tocarlo. Ahora
59
+ sólo registra lo que trae el paquete, y el primer `upgrade` suelta lo que una versión anterior registró de
60
+ más.
61
+
62
+ **Qué cambia para vos**: nada que hacer; tus guards dejan de aparecer en esos avisos.
63
+
64
+ - **El aviso de credenciales sin dueño avisa sólo credenciales.** `check` listaba toda variable que el
65
+ proyecto declaraba y ningún contrato nombraba: en un proyecto real fueron ciento ocho nombres de build, y
66
+ la única credencial quedó en «y 1 más». Ahora cuenta las que tienen nombre de secreto —`…_PASSWORD`,
67
+ `…_TOKEN`, `…_KEY`, `…_DSN`…—, dice en qué servicio está cada una y dónde se escribe su dueño, y avisa
68
+ aparte cuando un servicio pasó el tope de variables que se revisan.
69
+
70
+ **Qué cambia para vos**: la declaración de secretos y la configuración de una integración ahora también
71
+ rechazan una clave que termine en `_key`, `dsn` o `credentials`, igual que ya rechazaban `…token`. Si
72
+ tenés una, cambiala por la referencia a una variable de entorno. Y una credencial con nombre de
73
+ configuración —`STRIPE_LIVE`— no aparece en el aviso: el criterio es el nombre.
74
+
75
+ - **Una variable ya no se da por cargada porque otra la contiene en su nombre.** Con `API_SECRET_ROTATION`
76
+ escrita en `organization/workspace.md`, `API_SECRET` dejaba de avisarse aunque nadie la nombrara. Ahora
77
+ cuenta sólo el nombre entero.
78
+
79
+ **Qué cambia para vos**: puede aparecer en el aviso una credencial que antes quedaba tapada; escribí su
80
+ dueño y se va.
81
+
82
+ - **Un push que pedís en el chat ya no se frena.** Con `allowPush: false`, `git push` se frenaba aunque lo
83
+ hubieras pedido con todas las letras. Ahora pasa si tu mensaje nombra el remoto y la rama —«subí feat/x a
84
+ origin», `git push origin feat/x`—, y si no los nombra, el agente te dice qué se frenó y con un «dale»
85
+ pasa ese push y ningún otro. `planning/.ops-approval` también acepta la línea `push <remoto> <rama>`, tal
86
+ cual y sin patrones. Un `git push origin +rama` ahora se frena como el `--force` que es.
87
+
88
+ **Qué cambia para vos**: para publicar una rama de trabajo alcanza con pedirlo nombrando remoto y rama, o
89
+ con contestar «dale».
90
+
91
+ - **La sesión carga las reglas que rigen tu proyecto, no las cuatro de `system/` fijas.** El archivo de
92
+ instrucciones de cada runner importaba siempre `planning/rules/system/`: cargaba la regla que tu proyecto
93
+ había sobrescrito y ninguna de las tuyas. Ahora `automation install` pone las vigentes —las tuyas y las de
94
+ `system/` que no sobrescribiste— en Claude, Gemini, Codex y Antigravity, y un `CLAUDE.md` que editaste
95
+ recibe el bloque nuevo sin perder lo tuyo. Como `upgrade` no reinstala, `check` y `automation doctor`
96
+ avisan cuando lo instalado quedó atrás. `AGENTS.md` dice ahora que, donde una regla tuya y una del
97
+ sistema chocan, rige la tuya.
98
+
99
+ **Qué cambia para vos**: después de actualizar, o cada vez que escribas o sobrescribas una regla,
100
+ reinstalá el adaptador (`make install-claude`, o el de tu runner); `check` te dice cuándo hace falta. Si
101
+ tu `CLAUDE.md` es tuyo y no tiene nada de Cauce, poné las marcas `<!-- cauce:reglas inicio -->` y
102
+ `<!-- cauce:reglas fin -->` donde quieras el bloque y reinstalá.
103
+
104
+ - **`autobuild` trabaja con las reglas de tu proyecto y ya no cita una que retiraste.** Los subagentes
105
+ planificaban, construían y revisaban sin conocer tus reglas, y la fila que pedía partir una tarea citaba
106
+ R17 aunque la hubieras retirado. Ahora `context` dice qué reglas rigen —línea `RULES`, campo `rules` en
107
+ `--json`—, cada fase recibe esa lista, y Review tiene que nombrar contra cuáles revisó o la corrida para
108
+ con `review-unbacked`. Ningún prompt cita una regla por número.
109
+
110
+ **Qué cambia para vos**: nada que hacer.
111
+
112
+ - **Los recorridos ya no llenan el INBOX sin medida.** `autobuild` volcaba en Propuestas todo lo que Review
113
+ anotaba sin frenar, entero, y `flow` y `onboard` escribían sin tope ni forma. Ahora cada recorrido escribe
114
+ como mucho tres entradas por tarea o por corrida, de una línea, con la forma del molde y sin repetir un
115
+ nombre que ya está. Lo que pasa del tope no se pierde: `autobuild` lo cuenta en el `done/` de la tarea,
116
+ `flow` lo deja en el informe y `onboard` lo nombra al cerrar. Y `check` avisa, sin fallar, cuando
117
+ `INBOX.md` pasa de 300 líneas.
118
+
119
+ **Qué cambia para vos**: nada que hacer. Si querés otro umbral para el aviso, poné
120
+ `"inbox": { "warnLines": N }` en `ops.config.json`. El molde de `INBOX.md` ya no pide la evidencia dentro
121
+ de una propuesta; tu `INBOX.md` no se toca.
122
+
123
+ - **La recurrencia que recorre el INBOX viene activa, y descomentar los ejemplos de `RECURRING.md` ya no
124
+ rompe `check`.** Una instancia nueva trae la fila `inbox`, trimestral, que vence por primera vez tres meses
125
+ después de `init`. Las filas de ejemplo declaran el `(service: …)` que `check` exige. Y `check` avisa una
126
+ entrada del INBOX que se llama como una tarea de `done/`: probablemente se promovió y quedó ahí.
127
+
128
+ **Qué cambia para vos**: `RECURRING.md` es tuyo y `upgrade` no lo toca. Para tener la recurrencia en una
129
+ instancia que ya existe, agregá debajo de la tabla
130
+ `| inbox | trimestral | AAAA-MM-DD | Recorrer el INBOX entero. _Aceptación: ninguna viñeta queda sin decisión de promover, dejar o borrar._ (service: planning) |`
131
+ con una fecha futura: `Desde` es cuándo vence por primera vez, no el día en que escribís la fila.
132
+
133
+ - **Dos repositorios que terminan en una carpeta con el mismo nombre ya no salen con el mismo nombre.** Con
134
+ varias raíces declaradas, el inventario nombraba cada servicio por la **carpeta** de su raíz, así que
135
+ `../gouduet/keycloak` y `../hypixo/keycloak` salían los dos `keycloak` en `check`, en `onboard` y en
136
+ `scan`, y no había con qué saber de qué repositorio era cada credencial. Ahora los nombra el `name` que
137
+ cada raíz declara en `ops.config.json`, y `check` avisa cuando dos raíces se llaman igual, diciendo
138
+ cuáles son las dos.
139
+
140
+ **Qué cambia para vos**: si dos raíces de tu `ops.config.json` comparten `name`, `check` te lo avisa y
141
+ sigue pasando —renombrá una cuando puedas—. Y donde el `name` y la carpeta difieren, los servicios pasan
142
+ a listarse con el `name` —el que escribiste vos— en vez de con la carpeta; `onboard --json` sigue
143
+ emitiendo `roots` como lista de rutas.
144
+
145
+ ## [0.81.0] - 2026-09-11
146
+
147
+ ### Cambiado
148
+
149
+ - **Lo que pedís en el chat ya no te frena.** Los guards veían la herramienta y nada de la conversación,
150
+ así que frenaban igual lo que pediste con todas las letras y lo que el agente decidía solo. Ahora, en
151
+ Claude Code, Codex y Gemini, un hook registra tu mensaje y los guards lo leen: si nombraste lo que se
152
+ iba a frenar —«borrá la prueba de altas», «reescribí la migración 004»—, pasa; si no, el agente te dice
153
+ qué se frenó y un «dale» aprueba exactamente eso. `plan-first` deja de pedir un plan cuando el cambio lo
154
+ pediste vos. Lo que el agente hace por su cuenta, dentro de un recorrido o en un subagente, se sigue
155
+ frenando igual.
156
+
157
+ **Qué cambia para vos**: corré `automation install` para que tu runner registre el hook nuevo
158
+ (`UserPromptSubmit` en Claude y Codex, `BeforeAgent` en Gemini); en Codex, confialo con `/hooks`. Tu
159
+ mensaje se guarda en el temporal del sistema, no en el repositorio, y sólo el último de cada sesión.
160
+ Antigravity sigue con el archivo de aprobación.
161
+
162
+ - **Leer una credencial por shell se frena en los cuatro runners, y en Claude ya no te frena a vos.** Un
163
+ guard nuevo, `secrets-shell`, frena el comando que muestra una credencial —`cat .env`, `head`, `grep`,
164
+ `source`, una redirección `<`, un `node -e`— en Claude, Codex, Gemini y Antigravity. Hasta ahora sólo
165
+ Claude lo frenaba, con reglas nativas `permissions.deny` que tampoco dejaban pasar tu pedido; esas reglas
166
+ se retiran, y como con el resto de los guards, si lo pedís en el chat, pasa.
167
+
168
+ **Qué cambia para vos**: corré `automation install`; en Claude quita las reglas que Cauce había puesto y
169
+ conserva las tuyas. Si querés un bloqueo nativo total, escribilo como regla propia.
170
+
171
+ ### Corregido
172
+
173
+ - **El bloqueo de `verify` cita la prueba que falló también con jest, vitest, mocha y pytest.** Con esas
174
+ herramientas citaba el archivo, el resumen o —con `pytest -v`— una prueba verde cuyo nombre decía «error».
175
+ Ahora reconoce cómo marca cada una la prueba que falla, comprobado contra la salida real de cada una.
176
+
177
+ **Qué cambia para vos**: el mensaje apunta a la prueba que hay que mirar.
178
+
179
+ - **El agente ya no puede escribirse la aprobación.** `planning/.ops-approval` se escribía con la misma
180
+ herramienta que usa el agente y ningún guard lo miraba, así que una aprobación suya destrababa igual que
181
+ una tuya. Los guards de límites lo frenan ahora —por la herramienta de escritura y por el destino evidente
182
+ de un comando—, salvo que se lo hayas pedido nombrándolo en el chat.
183
+
184
+ **Qué cambia para vos**: nada si el archivo lo editás vos. Si el agente lo escribía por pedido tuyo,
185
+ ahora se lo pedís en el chat.
186
+
187
+ - **En sidecar, el bloqueo dice en qué archivo aprobar.** Mandaba a `planning/.ops-approval`, y desde la
188
+ carpeta del workspace, donde se abre la sesión, ése no es el `planning/` de la instancia: pegar donde
189
+ decía no destrababa nada. Ahora nombra la ruta que el guard lee —`acme-ops/planning/.ops-approval`—; en
190
+ modo embebido sigue diciendo `planning/.ops-approval`.
191
+
192
+ **Qué cambia para vos**: nada que hacer; el mensaje dice dónde.
193
+
17
194
  ## [0.80.0] - 2026-09-10
18
195
 
19
196
  ### Agregado
@@ -47,17 +47,43 @@ una cadena — permisos del runner, alcance del token, aprobación de un PR.
47
47
 
48
48
  ### Leer una credencial
49
49
 
50
- `secrets-read` frena leer, con la herramienta de lectura del runner, un archivo que `secrets` frenaría al
51
- escribir —los nombres conocidos y las identidades que declara `organization/secrets.json`—. Qué alcanza en
52
- cada runner es distinto, y conviene saberlo:
53
-
54
- - **Claude Code**: el guard corre en `Read`, y además la instalación agrega reglas `permissions.deny`
55
- `Read(...)` por los nombres conocidos. Esas reglas las aplica el propio Claude Code a `Read`, a `cat`,
56
- `head`, `tail`, `sed` y a las redirecciones, pero no a un `grep -r` ni a un subproceso que abra el archivo
57
- por su cuenta. Van por nombre exacto: `.env.*` también negaría `.env.example`.
58
- - **Gemini CLI**: el guard corre en `read_file`. Un `cat` por `run_shell_command` no lo ve.
59
- - **Codex y Antigravity**: sin guard de lectura. Sus adaptadores sólo enganchan shell y edición, y leer por
60
- shell no pasa por ningún matcher de archivo.
50
+ Dos guards frenan leer un archivo que `secrets` frenaría al escribir —los nombres conocidos y las
51
+ identidades que declara `organization/secrets.json`—: `secrets-read` en las herramientas de lectura y de
52
+ búsqueda del runner (`Read` y `Grep` en Claude, `read_file` y `grep_search` en Gemini) y `secrets-shell` en
53
+ el shell de los cuatro runners, cuando un comando la muestra —`cat`, `head`, `grep`, `sed`, `source`, una
54
+ redirección `<`, un intérprete en línea—. Un comodín que la nombra —`rg -g '.env*'`, `--include='*.env'`—
55
+ cuenta igual. Lo que sólo la nombra —`ls`, `test -f`, `rm`, `cp .env.example .env`— pasa, y lo que la
56
+ persona pidió en el chat también.
57
+
58
+ Lo que no ve ninguno, comprobado en sesiones reales: una búsqueda sobre la carpeta que no nombra el archivo
59
+ —un `rg` de todo el árbol—, un nombre armado en una variable, y en Gemini el propio entorno del runner,
60
+ que carga el `.env` de la carpeta al arrancar (documentado en geminicli.com/docs/reference/configuration):
61
+ un `env` muestra sus valores sin leer ningún archivo.
62
+
63
+ Hasta 0.80.0 Claude traía además reglas nativas `permissions.deny` `Read(...)`. Las aplica Claude mismo, sin
64
+ pasar por ningún hook, así que frenaban también lo que la persona pedía, mientras en los otros runners el
65
+ shell no tenía freno (caso 104). `automation install` las retira de una instalación anterior y conserva las
66
+ que escribió la empresa: quien quiera un bloqueo nativo total lo escribe como regla propia.
67
+
68
+ ### Lo que pide la persona
69
+
70
+ Un guard ve la llamada a la herramienta y nada de la conversación, así que frenaba igual lo que la persona
71
+ pidió con todas las letras y lo que el agente decidió solo. `chat` corre sobre el mensaje de la persona —el
72
+ runner lo dispara cuando ella manda algo, nunca por el resultado de una herramienta— y lo deja donde los
73
+ guards lo leen:
74
+
75
+ - lo que la persona **pidió nombrándolo** pasa: «leé el `.env`» autoriza leer el `.env`; «no toques el
76
+ `.env`» no, y tampoco una pregunta o un comentario que sólo lo nombra —«¿qué tiene el `.env`?»—;
77
+ - lo que se frenó sin que lo nombrara queda anotado, y un «dale» en el mensaje siguiente aprueba
78
+ exactamente eso;
79
+ - `plan-first` no aplica: el plan es del trabajo que va por tareas.
80
+
81
+ No cuenta cuando no hay persona —CI, o un aviso del runner como el de un subagente que terminó—, cuando lo
82
+ que pidió es un recorrido de Cauce (`/autobuild`, `$flow`…), ni en la llamada de un subagente, que Claude
83
+ marca con `agent_id`. En Claude y Codex cada llamada trae el identificador del mensaje que la originó;
84
+ Gemini no lo manda, y ahí vale el último mensaje. El registro vive en el temporal del sistema, uno por
85
+ sesión, y los guards de límites lo cuidan junto con `planning/.ops-approval`: el agente no puede
86
+ escribirse ninguno de los dos. Como todo lo de esta página, frena la forma habitual y no un script decidido.
61
87
 
62
88
  ## Cómo se ejecutan
63
89
 
@@ -85,8 +111,12 @@ runner, mientras la lógica se prueba y mantiene una sola vez en `engine/hooks/r
85
111
  | `pre-shell` | destructive, git-add, dependencies, governance, verify, shell-boundary | `guard-shell.sh` |
86
112
  | `pre-files` | secrets, generated, workspace-boundary, engine, migrations, integration-snapshot, test-evidence, plan-first | `guard-files.sh` |
87
113
  | `pre-read` | secrets-read | `guard-secrets-read.sh` |
114
+ | `prompt` | chat | `guard-chat.sh` |
88
115
  | `stop` | planning-drift | `guard-planning-drift.sh` |
89
116
 
117
+ `prompt` corre sobre el mensaje de la persona —`UserPromptSubmit` en Claude y Codex, `BeforeAgent` en
118
+ Gemini— y no sobre una herramienta, y su shim sale siempre con 0.
119
+
90
120
  Registrar el grupo gasta un proceso por herramienta en lugar de cinco, con el mismo orden y la misma
91
121
  semántica: el primer guard que bloquea corta la ejecución. Un runner que necesite granularidad fina puede
92
122
  seguir registrando los wrappers individuales.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ # Shim: qué registra está en engine/hooks/run.js → guards['chat']. Sale siempre con 0: el runner lo corre
3
+ # sobre el mensaje de la persona, y un 2 ahí no frena una herramienta sino lo que la persona escribió.
4
+ "$(dirname "$0")/run-hook.sh" chat >/dev/null 2>&1
5
+ exit 0
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ # Shim: qué bloquea el guard está en engine/hooks/run.js → guards['secrets-shell'].
3
+ exec "$(dirname "$0")/run-hook.sh" secrets-shell
@@ -1,7 +1,12 @@
1
1
  # Cauce
2
2
 
3
- Lee y cumple `{{OPS_DIR}}AGENTS.md`, `{{OPS_DIR}}planning/PROTOCOL.md` y `{{OPS_DIR}}planning/rules/system/` antes
4
- de ejecutar trabajo. `{{OPS_DIR}}planning/wip/<runner>.md` es el mutex de
3
+ Lee y cumple `{{OPS_DIR}}AGENTS.md`, `{{OPS_DIR}}planning/PROTOCOL.md` y las reglas que rigen este proyecto
4
+ antes de ejecutar trabajo: las propias y las de `system/` que el proyecto no sobrescribió. Donde una propia
5
+ contradice a una del sistema, rige la propia.
6
+
7
+ {{RULES:list}}
8
+
9
+ `{{OPS_DIR}}planning/wip/<runner>.md` es el mutex de
5
10
  ejecución y `{{OPS_DIR}}planning/AWAITING_REVIEW.md` bloquea una corrida nueva. No promociones ideas desde INBOX, no
6
11
  inventes aprobaciones o credenciales y no hagas push ni deploy. Cierra cada tarea con verificación real y
7
12
  evidencia en DONE.
@@ -2,10 +2,7 @@
2
2
 
3
3
  @{{OPS_DIR}}AGENTS.md
4
4
  @{{OPS_DIR}}planning/PROTOCOL.md
5
- @{{OPS_DIR}}planning/rules/system/process.md
6
- @{{OPS_DIR}}planning/rules/system/code-shape.md
7
- @{{OPS_DIR}}planning/rules/system/commits.md
8
- @{{OPS_DIR}}planning/rules/system/conduct.md
5
+ {{RULES:imports}}
9
6
 
10
7
  Los hooks de `.claude/settings.json` son obligatorios. En una instancia recién creada, empezá corriendo
11
8
  `node {{OPS_DIR}}tools/ops.js onboard`: es instantáneo, y lo primero que imprime es la pregunta con la que
@@ -1,14 +1,15 @@
1
1
  # Claude Code
2
2
 
3
- Adaptador nativo mediante `PreToolUse` y `Stop`. Instalar con:
3
+ Adaptador nativo mediante `PreToolUse`, `UserPromptSubmit` y `Stop`. Instalar con:
4
4
 
5
5
  ```bash
6
6
  node tools/ops.js automation install . claude
7
7
  ```
8
8
 
9
- El instalador fusiona la sección `hooks` en `.claude/settings.json` y conserva otras claves. También
10
- suma a `permissions.deny` reglas `Read(...)` por los nombres de credencial conocidos, junto a las que el
11
- proyecto ya tenga; una que Cauce retire en una versión futura no se quita sola. Si ya
9
+ El instalador fusiona la sección `hooks` en `.claude/settings.json` y conserva otras claves. Las reglas
10
+ `permissions.deny` `Read(...)` que Cauce sumaba hasta 0.80.0 las retira al reinstalar —las declara
11
+ `config.retired` en `manifest.json`— y deja las que el proyecto haya escrito: leer una credencial lo frenan
12
+ ahora los guards, y lo que la persona pide en el chat pasa (caso 104). Si ya
12
13
  existe una versión distinta de un archivo, se detiene sin sobrescribirla para no destruir
13
14
  personalizaciones del proyecto. También crea `CLAUDE.md` cuando no existe y conserva uno existente.
14
15
 
@@ -4,7 +4,32 @@
4
4
  "command": "claude",
5
5
  "config": {
6
6
  "source": "settings.json",
7
- "target": ".claude/settings.json"
7
+ "target": ".claude/settings.json",
8
+ "retired": {
9
+ "permissions": {
10
+ "deny": [
11
+ "Read(.env)",
12
+ "Read(.env.local)",
13
+ "Read(.env.*.local)",
14
+ "Read(.npmrc)",
15
+ "Read(.netrc)",
16
+ "Read(_netrc)",
17
+ "Read(.pypirc)",
18
+ "Read(.dockercfg)",
19
+ "Read(id_rsa)",
20
+ "Read(id_dsa)",
21
+ "Read(id_ecdsa)",
22
+ "Read(id_ed25519)",
23
+ "Read(credentials)",
24
+ "Read(credentials*.json)",
25
+ "Read(*service-account*.json)",
26
+ "Read(*.pem)",
27
+ "Read(*.key)",
28
+ "Read(accesos.md)",
29
+ "Read(credenciales*)"
30
+ ]
31
+ }
32
+ }
8
33
  },
9
34
  "instructions": [
10
35
  {
@@ -1,27 +1,4 @@
1
1
  {
2
- "permissions": {
3
- "deny": [
4
- "Read(.env)",
5
- "Read(.env.local)",
6
- "Read(.env.*.local)",
7
- "Read(.npmrc)",
8
- "Read(.netrc)",
9
- "Read(_netrc)",
10
- "Read(.pypirc)",
11
- "Read(.dockercfg)",
12
- "Read(id_rsa)",
13
- "Read(id_dsa)",
14
- "Read(id_ecdsa)",
15
- "Read(id_ed25519)",
16
- "Read(credentials)",
17
- "Read(credentials*.json)",
18
- "Read(*service-account*.json)",
19
- "Read(*.pem)",
20
- "Read(*.key)",
21
- "Read(accesos.md)",
22
- "Read(credenciales*)"
23
- ]
24
- },
25
2
  "hooks": {
26
3
  "PreToolUse": [
27
4
  {
@@ -37,12 +14,19 @@
37
14
  ]
38
15
  },
39
16
  {
40
- "matcher": "Read",
17
+ "matcher": "Read|Grep",
41
18
  "hooks": [
42
19
  { "type": "command", "command": "$CLAUDE_PROJECT_DIR/{{OPS_DIR}}automatization/hooks/guard-secrets-read.sh" }
43
20
  ]
44
21
  }
45
22
  ],
23
+ "UserPromptSubmit": [
24
+ {
25
+ "hooks": [
26
+ { "type": "command", "command": "$CLAUDE_PROJECT_DIR/{{OPS_DIR}}automatization/hooks/guard-chat.sh" }
27
+ ]
28
+ }
29
+ ],
46
30
  "Stop": [
47
31
  {
48
32
  "hooks": [
@@ -1,8 +1,12 @@
1
1
  # Cauce para Codex
2
2
 
3
- `{{OPS_DIR}}AGENTS.md` tiene las reglas del sistema, `{{OPS_DIR}}planning/PROTOCOL.md` es la fuente de
4
- verdad del proceso y `{{OPS_DIR}}planning/rules/system/` son las reglas que rigen cada tarea. Leelos
5
- antes de trabajar —los tres, no cuando algo sale mal—: acá sólo está lo específico de este runner.
3
+ `{{OPS_DIR}}AGENTS.md` tiene las reglas del sistema y `{{OPS_DIR}}planning/PROTOCOL.md` es la fuente de
4
+ verdad del proceso. Las reglas que rigen cada tarea en este proyecto son éstas —las propias y las de
5
+ `system/` que el proyecto no sobrescribió—, y donde una propia contradice a una del sistema, rige la propia:
6
+
7
+ {{RULES:list}}
8
+
9
+ Leé todo eso antes de trabajar, no cuando algo sale mal: acá sólo está lo específico de este runner.
6
10
 
7
11
  > Codex lee el `AGENTS.md` de la raíz, que es un nombre compartido entre herramientas. Cuando el repo
8
12
  > ops **es** la raíz, este archivo no se instala: el `AGENTS.md` de la empresa ya está ahí y manda.
@@ -18,6 +18,10 @@ porque cambia el hash. No se usa `--dangerously-bypass-hook-trust`.
18
18
  Los `matcher` filtran el **nombre de la herramienta**: los comandos de shell llegan como `Bash` y las
19
19
  ediciones como `apply_patch`, `Edit` o `Write`. No son los nombres internos del protocolo.
20
20
 
21
+ `UserPromptSubmit` registra el mensaje de la persona, para que lo que pidió en el chat pase sin aprobarlo
22
+ a mano. Codex le pasa a cada herramienta el `turn_id` del mensaje que la originó, que es lo que ata la
23
+ llamada al pedido.
24
+
21
25
  Si actualizás una instalación anterior a este cambio, `.codex/hooks/hooks.json` queda huérfano —Codex
22
26
  nunca lo leyó— y se borra a mano.
23
27
 
@@ -14,6 +14,13 @@
14
14
  ]
15
15
  }
16
16
  ],
17
+ "UserPromptSubmit": [
18
+ {
19
+ "hooks": [
20
+ { "type": "command", "command": "{{OPS_ROOT}}/automatization/hooks/guard-chat.sh" }
21
+ ]
22
+ }
23
+ ],
17
24
  "SessionEnd": [
18
25
  {
19
26
  "hooks": [
@@ -2,10 +2,7 @@
2
2
 
3
3
  @{{OPS_DIR}}AGENTS.md
4
4
  @{{OPS_DIR}}planning/PROTOCOL.md
5
- @{{OPS_DIR}}planning/rules/system/process.md
6
- @{{OPS_DIR}}planning/rules/system/code-shape.md
7
- @{{OPS_DIR}}planning/rules/system/commits.md
8
- @{{OPS_DIR}}planning/rules/system/conduct.md
5
+ {{RULES:imports}}
9
6
 
10
7
  `{{OPS_DIR}}planning/PROTOCOL.md` es la fuente de verdad. Ejecuta `/cauce:autobuild` fase por fase; los
11
8
  workflows JS de Claude son referencia, no un runtime compatible. `{{OPS_DIR}}planning/wip/<runner>.md` es el mutex
@@ -17,9 +17,12 @@ Antes vivían bajo `/ops:` y eran tres: el arranque y el recorrido de equipo le
17
17
  así que alguien que venía de otro runner los buscaba en la lista y no estaban. Si actualizás una
18
18
  instalación vieja, `.gemini/commands/ops/` queda huérfano y se borra a mano.
19
19
 
20
- Gemini CLI tiene hooks nativos y el adaptador los usa: `BeforeTool` y `AfterAgent` en
20
+ Gemini CLI tiene hooks nativos y el adaptador los usa: `BeforeTool`, `BeforeAgent` y `AfterAgent` en
21
21
  `.gemini/settings.json`, declarados en `manifest.json`. Sólo corren si la carpeta está marcada como
22
22
  confiable —`GEMINI.md` explica qué avisa Gemini cuando no lo está—. `read_file` pasa por
23
- `guard-secrets-read.sh`, que frena leer una credencial; un `cat` por `run_shell_command` no lo ve.
23
+ `guard-secrets-read.sh`, que frena leer una credencial; un `cat` por `run_shell_command` lo frena
24
+ `secrets-shell`, en el grupo de shell.
25
+ `BeforeAgent` registra el mensaje de la persona, para que lo que pidió en el chat pase sin aprobarlo a
26
+ mano; Gemini no le pasa a cada herramienta de qué mensaje viene, así que ahí vale el último.
24
27
 
25
28
  Comprueba la instalación con `node tools/ops.js automation doctor . gemini`.
@@ -29,7 +29,7 @@
29
29
  ]
30
30
  },
31
31
  {
32
- "matcher": "read_file",
32
+ "matcher": "read_file|grep_search",
33
33
  "hooks": [
34
34
  {
35
35
  "type": "command",
@@ -38,6 +38,16 @@
38
38
  ]
39
39
  }
40
40
  ],
41
+ "BeforeAgent": [
42
+ {
43
+ "hooks": [
44
+ {
45
+ "type": "command",
46
+ "command": "$GEMINI_PROJECT_DIR/{{OPS_DIR}}automatization/hooks/guard-chat.sh"
47
+ }
48
+ ]
49
+ }
50
+ ],
41
51
  "AfterAgent": [
42
52
  {
43
53
  "hooks": [
@@ -0,0 +1,28 @@
1
+ // Lo que un recorrido le pide a un agente cuando escribe en el INBOX (caso 101). El tope lo aplica el
2
+ // recorrido sobre lo que le pasa al agente, no el agente: pedirle que se limite no es un tope. Lo que no
3
+ // entra se cuenta donde cada recorrido lo deja, y ninguno lo tira en silencio.
4
+ const INBOX_CAP = 3
5
+ // La forma de una entrada, igual a la que declara el molde de `INBOX.md`: quien escribe no tiene que
6
+ // abrir el archivo para saberla. Una prueba ata las dos.
7
+ const INBOX_ENTRY = '- **slug-del-item** — Qué es, y qué se decide o se resuelve con esto.'
8
+ // Los nombres que ya hay, por sección, tal como los imprime `ops context --json` en su campo inbox.
9
+ const INBOX_HEADS = { type: 'object', additionalProperties: false, properties: Object.fromEntries(
10
+ ['deuda', 'ideas', 'propuestas', 'lecciones'].map((key) => [key, { type: 'array', items: { type: 'string' } }]),
11
+ ) }
12
+ // Una entrada es una línea. Un hallazgo de largo libre se recorta antes de llegar a quien lo escribe,
13
+ // porque lo que llega entero es lo que termina copiado entero.
14
+ const oneLine = (text) => {
15
+ const first = String(text || '').split('\n')[0].trim()
16
+ return first.length > 240 ? `${first.slice(0, 239)}…` : first
17
+ }
18
+ // La forma y los nombres que ya hay en las secciones donde se va a escribir. Van los nombres y no el
19
+ // archivo: con ellos alcanza para no repetir una entrada, y el archivo entero pesaría lo que el INBOX.
20
+ function inboxAsk(sections, heads) {
21
+ const known = sections.map((name) => {
22
+ const names = (heads || {})[name.toLowerCase()] || []
23
+ return names.length ? `en ${name} ya están ${names.join(', ')}` : `${name} no tiene entradas`
24
+ }).join('; ')
25
+ return `Cada entrada con la forma del molde —${INBOX_ENTRY}—: un nombre y una línea, y la evidencia se ` +
26
+ `cita donde ya vive, no se copia. Por nombre, ${known}: lo que ya esté con uno de esos nombres no se ` +
27
+ `vuelve a escribir.`
28
+ }
@@ -225,7 +225,7 @@ const verdicts = await pipeline(
225
225
  `normas o sistemas de terceros. Enumeralas con el registro que cada una lleva —verificado, ` +
226
226
  `documentado, hipótesis, o ninguno— y comprobá las que se puedan comprobar barato: abrí el archivo ` +
227
227
  `que cita y leé si dice eso, reproducí la invocación inocua que declara (\`--help\`, \`--version\`, ` +
228
- `un comando de sólo lectura), consultá la fuente pública que nombra. Llegá hasta donde R12 permite: ` +
228
+ `un comando de sólo lectura), consultá la fuente pública que nombra. Sin salirte de la lectura: ` +
229
229
  `nunca conectarte a un sistema real ni ejecutar la operación cuyo efecto se describe.\n\n` +
230
230
  `Empezá por la afirmación de la que depende la recomendación del cargo, no por la que parezca más ` +
231
231
  `discutible: son distintas, y la segunda suele estar bien rotulada porque el cargo esperaba que se la ` +