@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.
- package/CHANGELOG.md +177 -0
- package/automatization/hooks/README.md +41 -11
- package/automatization/hooks/guard-chat.sh +5 -0
- package/automatization/hooks/guard-secrets-shell.sh +3 -0
- package/automatization/runners/antigravity/rules/cauce.md +7 -2
- package/automatization/runners/claude/CLAUDE.md +1 -4
- package/automatization/runners/claude/README.md +5 -4
- package/automatization/runners/claude/manifest.json +26 -1
- package/automatization/runners/claude/settings.json +8 -24
- package/automatization/runners/codex/AGENTS.md +7 -3
- package/automatization/runners/codex/README.md +4 -0
- package/automatization/runners/codex/hooks.json +7 -0
- package/automatization/runners/gemini/GEMINI.md +1 -4
- package/automatization/runners/gemini/README.md +5 -2
- package/automatization/runners/gemini/settings.json +11 -1
- package/automatization/shared/inbox.js +28 -0
- package/automatization/workflows/agent-eval.js +1 -1
- package/automatization/workflows/autobuild.js +52 -18
- package/automatization/workflows/flow.js +33 -8
- package/automatization/workflows/onboard.js +25 -3
- package/engine/automation/index.js +28 -5
- 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 +40 -9
- package/engine/hooks/chat.js +171 -0
- package/engine/hooks/files.js +28 -16
- package/engine/hooks/input.js +8 -12
- package/engine/hooks/push.js +147 -0
- package/engine/hooks/run.js +23 -3
- package/engine/hooks/secrets-shell.js +62 -0
- package/engine/hooks/self-approval.js +31 -0
- package/engine/hooks/shell.js +37 -31
- 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 +23 -7
- package/template/planning/INBOX.md +2 -1
- package/template/planning/RECURRING.md +6 -5
- 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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
# Cauce
|
|
2
2
|
|
|
3
|
-
Lee y cumple `{{OPS_DIR}}AGENTS.md`, `{{OPS_DIR}}planning/PROTOCOL.md` y
|
|
4
|
-
de ejecutar trabajo
|
|
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
|
-
|
|
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.
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
4
|
-
verdad del proceso
|
|
5
|
-
|
|
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
|
|
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
@{{OPS_DIR}}AGENTS.md
|
|
4
4
|
@{{OPS_DIR}}planning/PROTOCOL.md
|
|
5
|
-
|
|
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`
|
|
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.
|
|
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 ` +
|