@ingeniomaps/cauce 0.98.0 → 0.99.1
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 +113 -0
- package/agents/roles/system/ai-product-manager/learning/HISTORY.md +1 -0
- package/agents/roles/system/analytics-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/backend-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/cloud-architect/learning/HISTORY.md +1 -0
- package/agents/roles/system/customer-success-manager/learning/HISTORY.md +1 -0
- package/agents/roles/system/data-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/data-governance-steward/learning/HISTORY.md +1 -0
- package/agents/roles/system/devops-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/engineering-manager/learning/HISTORY.md +1 -0
- package/agents/roles/system/engineering-manager/learning/sources.yaml +1 -1
- package/agents/roles/system/engineering-manager/references/operating-model.md +1 -1
- package/agents/roles/system/machine-learning-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/mlops-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/mobile-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/product-manager/learning/HISTORY.md +1 -0
- package/agents/roles/system/site-reliability-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/software-architect/learning/HISTORY.md +1 -0
- package/agents/roles/system/ui-designer/learning/HISTORY.md +1 -0
- package/automatization/hooks/README.md +6 -2
- package/automatization/runners/antigravity/README.md +4 -1
- package/automatization/runners/antigravity/hook.js +84 -22
- package/automatization/runners/antigravity/manifest.json +1 -0
- package/automatization/shared/acceptance.js +26 -0
- package/automatization/workflows/agent-promote.js +3 -1
- package/automatization/workflows/autobuild.js +69 -26
- package/engine/agents/learning-seal.js +20 -1
- package/engine/automation/index.js +11 -2
- package/engine/automation/registration.js +89 -0
- package/engine/cli/args.js +1 -1
- package/engine/cli/catalog.js +8 -2
- package/engine/cli/ops.js +1 -1
- package/engine/cli/validate.js +3 -0
- package/engine/cli/wiring.js +1 -1
- package/engine/config/validate.js +30 -11
- package/engine/core/changelog.js +60 -1
- package/engine/core/migrations.js +275 -0
- package/engine/core/repos.js +18 -4
- package/engine/hooks/approval.js +77 -14
- package/engine/hooks/chat.js +85 -27
- package/engine/hooks/files.js +9 -107
- package/engine/hooks/input.js +67 -11
- package/engine/hooks/migrations.js +93 -0
- package/engine/hooks/push.js +5 -3
- package/engine/hooks/run.js +10 -5
- package/engine/hooks/secrets-shell.js +2 -1
- package/engine/hooks/self-approval.js +39 -22
- package/engine/hooks/verify.js +79 -22
- package/engine/planning/acceptance.js +18 -0
- package/engine/planning/contracts.js +78 -45
- package/engine/schemas/ops-config.schema.json +9 -0
- package/package.json +1 -1
- package/template/AGENTS.md +11 -2
- package/template/planning/PROTOCOL.md +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,119 @@ 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.99.1] - 2026-09-25
|
|
18
|
+
|
|
19
|
+
### Cambiado
|
|
20
|
+
|
|
21
|
+
- **Una lectura de credencial que necesitás siempre se aprueba una vez.** Cuando el agente se frena leyendo
|
|
22
|
+
una credencial, te pregunta además si querés dejarla aprobada para siempre. Si esa es tu intención —con
|
|
23
|
+
tus palabras, sin frase fija—, él mismo escribe la ruta en `planning/.ops-approval` con quién lo aprobó y
|
|
24
|
+
para qué, y ninguna sesión vuelve a preguntarlo. Tu confirmación le deja escribir esa línea y ninguna otra.
|
|
25
|
+
- **Confirmar el bloqueo de `.ops-approval` ahora aprueba.** Si el agente intenta escribir la aprobación sin
|
|
26
|
+
que se la hayas pedido, el bloqueo te muestra qué iba a agregar y tu «sí» lo deja pasar. Antes el bloqueo
|
|
27
|
+
ofrecía confirmar y la confirmación no hacía nada: sólo pasaba pidiéndolo con el nombre del archivo.
|
|
28
|
+
- **El bloqueo dice cuánto dura una línea pegada**: hasta que alguien la borre. Decía que dejaba de valer
|
|
29
|
+
«en cuanto cambie», y una lectura siempre frena la misma ruta, así que nunca cambiaba.
|
|
30
|
+
- **`check` nombra aparte las credenciales aprobadas** —«N credencial(es) aprobadas hasta que se borre su
|
|
31
|
+
línea»—, en vez de contarlas como una aprobación olvidada. Sigue mostrándolas en cada corrida.
|
|
32
|
+
|
|
33
|
+
## [0.99.0] - 2026-09-24
|
|
34
|
+
|
|
35
|
+
### Agregado
|
|
36
|
+
|
|
37
|
+
- **`migrations.paths` en `ops.config.json`**: las carpetas donde viven tus migraciones, por ejemplo
|
|
38
|
+
`["migrations", "alembic/versions"]`. Si la declarás, reemplaza el default (`migrations`, `migration`,
|
|
39
|
+
`migrate`), que no alcanzaba a Alembic. `check` avisa la extensión o la carpeta declarada que no alcanza
|
|
40
|
+
a ningún archivo: una cobertura que declaraste y no cubre nada.
|
|
41
|
+
|
|
42
|
+
### Cambiado
|
|
43
|
+
|
|
44
|
+
- **`check` falla si una entrada de DONE tiene `tests:` todo `n/a` y su commit toca algo que no sea un
|
|
45
|
+
documento (`.md`, `.txt`, `.adoc`) o una imagen (`.png`, `.jpg`, `.jpeg`, `.gif`, `.svg`, `.webp`).** Es lo que vuelve creíble el `n/a` de arriba: lo demás se ejecuta y se prueba. Rige
|
|
46
|
+
para lo cerrado desde el 2026-09-24, así que tu historia anterior no se pone en rojo al actualizar.
|
|
47
|
+
|
|
48
|
+
### Corregido
|
|
49
|
+
|
|
50
|
+
- **Un aviso de una tarea de fondo ya no le borra a la persona lo que dijo.** Si un guard frenaba algo en el
|
|
51
|
+
turno de un aviso de un subagente, eso no quedaba esperando tu confirmación, y tu «dale» siguiente no
|
|
52
|
+
aprobaba nada. Ahora lo frenado espera tu respuesta aunque el aviso llegue antes que ella, y tu negativa
|
|
53
|
+
—«no toques el `.env`»— sigue valiendo después de un aviso sin depender del texto que el aviso traiga.
|
|
54
|
+
|
|
55
|
+
- **Una negación sobre otra cosa ya no te hace perder un bloqueo.** «dale, fijate si esto no es un
|
|
56
|
+
defecto» dejaba lo frenado sin anotar, y tu «confirmo» siguiente no aprobaba nada. Ahora la respuesta se
|
|
57
|
+
lee en la primera parte del mensaje, y lo que negás nombrándolo —«dale, pero no el .env»— sigue sin
|
|
58
|
+
pasar, igual que un push si en cualquier parte decís que no se publique —«no hagas push todavía»—. Cuando algo no queda esperando tu confirmación, el bloqueo lo dice y te indica que lo pidas
|
|
59
|
+
nombrándolo, en vez de ofrecerte un «dale» que no iba a servir.
|
|
60
|
+
|
|
61
|
+
- **Si lo que se frenó lo hacía un subagente, tu confirmación le llega.** Dentro de trabajo delegado —Build en
|
|
62
|
+
cada `autobuild`— el bloqueo no ofrecía el chat: decías que sí y el reintento volvía a frenar con el mismo
|
|
63
|
+
mensaje, y la única salida era pegar la línea en `planning/.ops-approval`. Ahora el subagente devuelve el
|
|
64
|
+
bloqueo para que te lo pregunten, y lo que confirmes pasa aunque lo reintente otro subagente. Sólo eso: un
|
|
65
|
+
pedido tuyo que nombraba algo sigue valiendo para el agente con el que hablás, no para un subagente.
|
|
66
|
+
|
|
67
|
+
- **Sin nadie en el chat, el bloqueo ya no le ordena al agente que se escriba la aprobación.** Decía «Aprobalo
|
|
68
|
+
pegando tal cual en…», y el agente lo leía como una orden que su contrato le prohíbe. Ahora dice que las
|
|
69
|
+
líneas las pega una persona, y al agente qué hacer mientras tanto.
|
|
70
|
+
|
|
71
|
+
- **El gate de commit reconoce el código de sqlc donde lo pongas.** Sólo aceptaba el generado bajo una
|
|
72
|
+
carpeta `sqlc/` o `generated/`, así que un `out` como `internal/platform/pgdb/` pedía aprobación en cada
|
|
73
|
+
commit que tocaba una consulta aunque hubieras regenerado. Ahora reconoce `*.sql.go` en cualquier carpeta.
|
|
74
|
+
Y ve una consulta en cualquier carpeta `queries/` —`api/db/queries/` en un monorepo—, que antes se
|
|
75
|
+
commiteaba sin su generado sin que nada avisara; eso sólo si el repositorio tiene `sqlc.yaml`, `sqlc.yml`
|
|
76
|
+
o `sqlc.json`. Si frena, dice qué buscó. Límite: con `output_files_suffix` el generado no se reconoce.
|
|
77
|
+
|
|
78
|
+
- **Un guard invocado a mano ya no se queda colgado.** Lanzado fuera del runner —a mano, desde un script o
|
|
79
|
+
en segundo plano— con stdin abierto y sin datos, esperaba hasta que el otro extremo cerrara: horas, sin
|
|
80
|
+
llegar a correr. Ahora, si en 2 s no llega nada, bloquea y explica cómo invocarlo: con el JSON del hook
|
|
81
|
+
por stdin, o sin entrada con `</dev/null`. Lo que manda el runner se lee entero, así que una escritura
|
|
82
|
+
grande no se corta, y un JSON completo se juzga aunque quien lo mandó no cierre stdin.
|
|
83
|
+
|
|
84
|
+
- **Una tarea cuyo entregable es un documento o una decisión escrita ya puede cerrarse en `autobuild`.**
|
|
85
|
+
Paraba en `verify-hollow` pidiendo pruebas imposibles. Ahora Verify declara esos criterios sin superficie
|
|
86
|
+
ejecutable, llegan a Done como `tests: n/a — <razón>`, y QA comprueba que el documento exista y cubra lo
|
|
87
|
+
que la aceptación enumera. Con causas mezcladas, el rebote pide sólo las pruebas que de verdad faltan, y
|
|
88
|
+
Done recibe de Verify qué prueba cubre cada criterio en vez de componerlo de memoria.
|
|
89
|
+
|
|
90
|
+
- **Una condición marcada `(fuera de verify: <razón>)` ya no llega a Verify ni a QA.** Era la salida que
|
|
91
|
+
`check` recomienda, y la corrida paraba igual; ahora viaja a Done para quedar cumplida en `tests:`, `qa:`
|
|
92
|
+
o `commit:`.
|
|
93
|
+
|
|
94
|
+
- **El guard de migraciones ya no frena la reversión honesta.** Buscaba en todo el archivo, así que el
|
|
95
|
+
`DROP TABLE` del `-- +goose Down` frenaba cada tabla nueva y una migración sin `Down` pasaba. Ahora, en
|
|
96
|
+
goose y dbmate, juzga sólo el bloque que aplica; un `*.down.sql` es reversión entera; corregir el `Down`
|
|
97
|
+
con una edición también pasa; y el mensaje dice en qué bloque está lo que frena.
|
|
98
|
+
|
|
99
|
+
- **Si declarás `migrations.extensions`, el guard ve también el borrado escrito con la API del ORM**, no
|
|
100
|
+
sólo el SQL crudo: `op.drop_table`/`drop_column` (Alembic), `drop_table`, `remove_column(s)` y
|
|
101
|
+
`drop_join_table` (Rails), `dropTable`/`dropColumn(s)` (TypeORM), `dropTable(IfExists)`/`dropColumn`
|
|
102
|
+
(Knex) y `DeleteModel`/`RemoveField` (Django). La reversión —`downgrade()`, `down`— no se juzga.
|
|
103
|
+
|
|
104
|
+
- **El puente de Antigravity ya no deja pasar lo que no puede leer.** Ante un JSON ilegible respondía
|
|
105
|
+
`allow`: un `git push --force` al que le faltaba una llave pasaba. Y con stdin abierto se colgaba. Ahora
|
|
106
|
+
lee con el mismo lector que los guards: lo ilegible se niega en `pre-shell` y `pre-files`, si en 2 s no
|
|
107
|
+
llega nada niega y dice cómo invocarlo a mano, y en `stop` deja cerrar con el motivo a la vista. Sin
|
|
108
|
+
entrada (`</dev/null`) sigue permitiendo, como antes.
|
|
109
|
+
|
|
110
|
+
- **El puente de Antigravity niega una llamada que no sabe describir.** Leía cada campo por un nombre fijo,
|
|
111
|
+
así que una llamada con otra forma —un campo renombrado en una actualización del runner— llegaba vacía a
|
|
112
|
+
los guards y pasaba: todos apagados sin avisar. Ahora un `pre-shell` sin comando o un `pre-files` sin
|
|
113
|
+
archivo se niegan, y el motivo nombra los campos que sí llegaron.
|
|
114
|
+
|
|
115
|
+
- **El gate de commit reconoce una especificación OpenAPI por lo que declara, no por la carpeta.** Cualquier
|
|
116
|
+
`.yaml` bajo `api/`, `openapi/` o `spec/` pedía regenerar el cliente: una config de sqlc, un compose o un
|
|
117
|
+
fixture ahí frenaban cada commit. Ahora cuenta el que declara `openapi:` o `swagger:`, o un fragmento de una
|
|
118
|
+
carpeta que tiene uno.
|
|
119
|
+
|
|
120
|
+
- **En Codex, el guard de migraciones juzga cada archivo del parche por separado.** Un `apply_patch` llegaba
|
|
121
|
+
como un solo sobre: los marcadores de goose y dbmate venían con el `+` del parche y no partían, así que la
|
|
122
|
+
reversión honesta volvía a frenar, y un `DROP TABLE` citado en otro archivo del mismo parche frenaba la
|
|
123
|
+
migración. Ahora cada archivo se juzga por su sección, y en una modificación sólo lo agregado.
|
|
124
|
+
|
|
125
|
+
- **`automation doctor` de Antigravity mira la copia que `agy` ejecuta de verdad.** `agy` corre la que registrás
|
|
126
|
+
con `agy plugin install`, una por usuario, y `doctor` miraba la del workspace: decía «operativo» mientras `agy`
|
|
127
|
+
ejecutaba el plugin de otro proyecto o uno roto, y frenaba cada comando. Ahora compara las dos, lanza la
|
|
128
|
+
registrada como la lanza `agy`, y si difiere te dice qué comando corre para registrar esta instalación.
|
|
129
|
+
|
|
17
130
|
## [0.98.0] - 2026-09-17
|
|
18
131
|
|
|
19
132
|
### Cambiado
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -5,3 +5,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
7
|
| 2026-08-30 | `learning/proposals/2026-08.md` | Aprobada | @ingeniomaps (Manuel Pinzon) | Nada: el período se revisó y se decidió no cambiar ningún contrato. |
|
|
8
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -5,3 +5,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
7
|
| 2026-08-30 | `agents/roles/system/cloud-architect/learning/proposals/2026-08.md` | Aprobada | @ingeniomaps | `agents/roles/system/cloud-architect/SKILL.md` |
|
|
8
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -5,3 +5,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
7
|
| 2026-08-30 | `learning/proposals/2026-08.md` | Aprobada | Manuel Pinzon | `SKILL.md` (párrafo nuevo al final de «Construir contexto»); `learning/proposals/2026-08.md`. Dos desviaciones, escritas al final de «Aprobación humana» de la propuesta: (1) los ejemplos del párrafo se adaptaron al vocabulario del cargo —la propuesta autoriza adaptar la redacción y la licencia se usó sólo en esa lista: donde decía «Delighted, una herramienta de analítica», el `SKILL.md` dice «la encuesta de NPS o CSAT, el producto de analítica», porque Delighted es el proveedor del caso `07-nps-que-subio` y nombrarlo metía un caso de evaluación dentro del contrato del cargo—; el resto entró literal, incluida «abstenerse cubre lo que no se puede consultar, no lo que cuesta abrir una página», y en la ubicación que la propuesta fija; (2) no se re-corrió `07-nps-que-subio`, que la sección «Evaluación» pide como confirmación: esta aplicación se limitó a «Cambio propuesto», y hasta que exista ese veredicto el efecto del párrafo en este cargo no está medido. Sin caso adversarial nuevo: la propuesta acota el cambio al párrafo y nada más. |
|
|
8
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -17,7 +17,7 @@ sources:
|
|
|
17
17
|
tier: profession
|
|
18
18
|
topics: [developer-productivity, wellbeing, flow, collaboration]
|
|
19
19
|
- name: Google re Work team effectiveness
|
|
20
|
-
url: https://rework.withgoogle.com/intl/en/guides/
|
|
20
|
+
url: https://rework.withgoogle.com/intl/en/guides/understand-team-effectiveness
|
|
21
21
|
tier: profession
|
|
22
22
|
topics: [psychological-safety, effectiveness]
|
|
23
23
|
- name: ACM Code of Ethics
|
|
@@ -54,7 +54,7 @@ Combinar satisfacción/bienestar, performance/outcomes, actividad contextual, co
|
|
|
54
54
|
|
|
55
55
|
- [DORA Capabilities](https://dora.dev/capabilities/): entrega sostenible, feedback, cultura y desempeño organizacional.
|
|
56
56
|
- **SPACE Framework**, ACM Queue 19(1) (sin enlace: `queue.acm.org` bloquea): productividad multidimensional, no reducible a actividad.
|
|
57
|
-
- [Google re:Work — Understand team effectiveness](https://rework.withgoogle.com/intl/en/guides/
|
|
57
|
+
- [Google re:Work — Understand team effectiveness](https://rework.withgoogle.com/intl/en/guides/understand-team-effectiveness): seguridad psicológica, confiabilidad, estructura, significado e impacto.
|
|
58
58
|
- [ACM Code of Ethics](https://www.acm.org/binaries/content/assets/about/acm-code-of-ethics-booklet.pdf): bienestar, justicia, privacidad, competencia y liderazgo responsable.
|
|
59
59
|
|
|
60
60
|
Verificar políticas laborales, jurisdicción y contexto real de cada empresa.
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -4,3 +4,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-09-01 | `2026-09.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -6,3 +6,4 @@ Una fila por propuesta cerrada: cuándo, cuál, qué se decidió —aplicarla o
|
|
|
6
6
|
|---|---|---|---|---|
|
|
7
7
|
| 2026-08-30 | `agents/roles/system/ui-designer/learning/proposals/2026-08.md` | Aprobada | Manuel Pinzon | `agents/roles/system/ui-designer/SKILL.md` |
|
|
8
8
|
| 2026-08-30 | `agents/roles/system/ui-designer/learning/proposals/2026-09.md` | Aprobada | @ingeniomaps (Manuel Pinzon) | `agents/roles/system/ui-designer/SKILL.md` |
|
|
9
|
+
| 2026-09-01 | `2026-09-r2.md` | archivada | malpisa1@gmail.com | Se archivó sin decidir: el documento quedó con el molde. |
|
|
@@ -12,7 +12,8 @@ Los hooks convierten invariantes comprobables en gates mecánicos. La base recom
|
|
|
12
12
|
- cierre de sesión con planning o integraciones inválidas;
|
|
13
13
|
- modificación del protocolo durante una tarea de producto.
|
|
14
14
|
- escrituras fuera de las raíces declaradas del workspace;
|
|
15
|
-
- reescritura de migraciones y SQL
|
|
15
|
+
- reescritura de migraciones y borrado destructivo —SQL o la API del ORM— en la parte que aplica, no en su
|
|
16
|
+
reversión; qué carpetas y extensiones son migraciones lo declara `migrations` en `ops.config.json`;
|
|
16
17
|
- publicación, instalaciones globales y drift entre manifests y lockfiles.
|
|
17
18
|
|
|
18
19
|
La lógica portable vive en `engine/hooks/run.js`; los `guard-*.sh` son entradas ejecutables comunes. Cada
|
|
@@ -84,7 +85,10 @@ guards lo leen:
|
|
|
84
85
|
|
|
85
86
|
No cuenta cuando no hay persona —CI, o un aviso del runner como el de un subagente que terminó—, cuando lo
|
|
86
87
|
que pidió es un recorrido de Cauce (`/autobuild`, `$flow`…), ni en la llamada de un subagente, que Claude
|
|
87
|
-
marca con `agent_id
|
|
88
|
+
marca con `agent_id`: a un subagente no le llega lo que ella pidió ni lo que se le concedió antes. Sí le
|
|
89
|
+
llega su confirmación a un bloqueo, que nombra exactamente lo frenado: el bloqueo de un subagente le pide
|
|
90
|
+
devolverlo a quien lo lanzó para que se lo pregunte, y lo que ella confirme pasa aunque lo reintente otro
|
|
91
|
+
subagente. En Claude y Codex cada llamada trae el identificador del mensaje que la originó;
|
|
88
92
|
Gemini no lo manda, y ahí vale el último mensaje. El registro vive en el temporal del sistema, uno por
|
|
89
93
|
sesión, y los guards de límites lo cuidan junto con `planning/.ops-approval`: el registro no lo escribe
|
|
90
94
|
nunca una herramienta, y en la aprobación sólo entran las líneas que la persona nombró en ese mismo
|
|
@@ -19,7 +19,10 @@ usuario y no por workspace. De ahí salen tres consecuencias que conviene tener
|
|
|
19
19
|
- Registrar desde otro proyecto reemplaza el plugin del anterior.
|
|
20
20
|
|
|
21
21
|
Volvé a registrar cada vez que `automation install` cambie el wiring o el proyecto se mueva de lugar;
|
|
22
|
-
`agy plugin validate .agents/plugins/cauce` comprueba la copia del repo antes de registrarla.
|
|
22
|
+
`agy plugin validate .agents/plugins/cauce` comprueba la copia del repo antes de registrarla. Si te
|
|
23
|
+
olvidás, `automation doctor` lo dice: compara la copia registrada con la del workspace y la lanza como
|
|
24
|
+
la lanza `agy`, desde su carpeta, así que una copia de otro proyecto o de otra versión es un error con el
|
|
25
|
+
comando que lo corrige. Al instalar es una advertencia, porque registrar es justo el paso que sigue.
|
|
23
26
|
|
|
24
27
|
El plugin aporta hooks `PreToolUse` y `Stop`, reglas Cauce y los cinco recorridos —`/cauce:onboard`,
|
|
25
28
|
`/cauce:flow`, `/cauce:autobuild`, `/cauce:integration-sync` y `/cauce:integration-promote`— más el
|
|
@@ -4,13 +4,6 @@
|
|
|
4
4
|
const fs = require('node:fs')
|
|
5
5
|
const path = require('node:path')
|
|
6
6
|
|
|
7
|
-
function readInput() {
|
|
8
|
-
try {
|
|
9
|
-
const raw = fs.readFileSync(0, 'utf8')
|
|
10
|
-
return raw.trim() ? JSON.parse(raw) : {}
|
|
11
|
-
} catch { return {} }
|
|
12
|
-
}
|
|
13
|
-
|
|
14
7
|
// Dónde quedó la raíz ops respecto de la carpeta que Antigravity abre. Lo completa
|
|
15
8
|
// `automation install`, que es el único momento en que se sabe: en modo sidecar la raíz ops es un
|
|
16
9
|
// hermano de los repos de producto, y ninguna búsqueda hacia arriba la encuentra. Sin esto el bridge
|
|
@@ -78,18 +71,39 @@ function findRoot(input, markers = MARKERS) {
|
|
|
78
71
|
throw new Error('No se encontró una raíz Cauce desde el workspace de Antigravity.')
|
|
79
72
|
}
|
|
80
73
|
|
|
81
|
-
function
|
|
74
|
+
function engineAt(root, file) {
|
|
82
75
|
const candidates = [
|
|
83
|
-
path.join(root, 'node_modules', '@ingeniomaps', 'cauce', 'engine', 'hooks',
|
|
84
|
-
path.join(root, 'engine', 'hooks',
|
|
85
|
-
path.join(root, '..', 'node_modules', '@ingeniomaps', 'cauce', 'engine', 'hooks',
|
|
76
|
+
path.join(root, 'node_modules', '@ingeniomaps', 'cauce', 'engine', 'hooks', file),
|
|
77
|
+
path.join(root, 'engine', 'hooks', file),
|
|
78
|
+
path.join(root, '..', 'node_modules', '@ingeniomaps', 'cauce', 'engine', 'hooks', file),
|
|
86
79
|
]
|
|
87
80
|
// Copia de `packagePath`; su porqué vive allá. Son tres los que la repiten y el motor los nombra.
|
|
88
|
-
|
|
81
|
+
return candidates.find(fs.existsSync) || ''
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function runtimeAt(root) {
|
|
85
|
+
const runtime = engineAt(root, 'run.js')
|
|
89
86
|
if (!runtime) throw new Error('No se encontró el runtime engine/hooks/run.js.')
|
|
90
87
|
return require(runtime)
|
|
91
88
|
}
|
|
92
89
|
|
|
90
|
+
// La entrada se lee con el lector del motor y no con uno propio: el puente tenía su copia, y la copia se
|
|
91
|
+
// colgaba con stdin abierto y convertía un JSON ilegible en `allow` (caso 198). Lo que no se puede usar para
|
|
92
|
+
// encontrarlo es la entrada misma, que todavía no se leyó: se busca como `findRoot` sin entrada —la raíz que
|
|
93
|
+
// `install` dejó escrita y, si no resuelve, desde donde corre el puente—. Sólo la declarada dejaba sin
|
|
94
|
+
// lector, negando cada llamada, a un proyecto movido que los guards sí encontraban. Sin ninguna, se niega.
|
|
95
|
+
function inputReader(markers = MARKERS) {
|
|
96
|
+
let root = ''
|
|
97
|
+
try { root = findRoot({}, markers) } catch { /* sin raíz no hay motor: lo dice el error de abajo */ }
|
|
98
|
+
const reader = root && engineAt(root, 'input.js')
|
|
99
|
+
if (!reader) {
|
|
100
|
+
throw new Error('No se encontró engine/hooks/input.js, con el que se lee la entrada, ni en la raíz que '
|
|
101
|
+
+ `automation install declaró (${declaredRoot(markers) || 'ninguna'}) ni desde ${process.cwd()}. `
|
|
102
|
+
+ 'Reinstalá el runner.')
|
|
103
|
+
}
|
|
104
|
+
return require(reader)
|
|
105
|
+
}
|
|
106
|
+
|
|
93
107
|
// La carpeta que el runner abrió, deducida de la raíz: en sidecar la raíz ops es su hija, y en modo
|
|
94
108
|
// embebido son la misma.
|
|
95
109
|
function workspaceOf(root, markers) {
|
|
@@ -134,6 +148,47 @@ function respond(value) {
|
|
|
134
148
|
process.stdout.write(`${JSON.stringify(value)}\n`)
|
|
135
149
|
}
|
|
136
150
|
|
|
151
|
+
function refusal(event, message, blocking) {
|
|
152
|
+
const reason = `Cauce: ${message}`
|
|
153
|
+
if (event !== 'stop') return { decision: 'deny', reason }
|
|
154
|
+
// Un guard que bloquea marca su error con `blocked` (engine/hooks/run.js); cualquier otro es que el
|
|
155
|
+
// puente no llegó a juzgar nada. En `stop` los dos devolvían `continue`, y eso ata al agente: la
|
|
156
|
+
// raíz que no resuelve no se arregla sola, así que cada intento de cerrar repite el mismo error.
|
|
157
|
+
// El bloqueo sigue dando `continue` —es el mecanismo funcionando—; la falla deja cerrar y avisa.
|
|
158
|
+
return blocking ? { decision: 'continue', reason } : { decision: 'stop', reason }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Lo que cada evento tiene que traer para que sus guards juzguen algo. `normalize` lee campos por nombre y
|
|
162
|
+
// convierte en `''` el que no encuentra, así que una llamada con otra forma —un campo renombrado en una
|
|
163
|
+
// actualización de Antigravity— llegaba vacía a los guards, ninguno frenaba y el puente respondía `allow`:
|
|
164
|
+
// todos los guards apagados sin rastro (caso 200). Cerrado por defecto: sin lo que el evento juzga, se niega
|
|
165
|
+
// y se dice qué llegó, que es lo que hace falta para enseñarle la forma nueva a `normalize`.
|
|
166
|
+
//
|
|
167
|
+
// Una entrada vacía no es una llamada con otra forma: no describe ninguna, y es como se invoca a mano, con
|
|
168
|
+
// `OPS_HOOK_COMMAND` u `OPS_HOOK_FILE` (caso 198).
|
|
169
|
+
const DESCRIBED_BY = {
|
|
170
|
+
'pre-shell': { field: 'command', names: 'CommandLine' },
|
|
171
|
+
'pre-files': { field: 'file_path', names: 'TargetFile ni AbsolutePath' },
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Un archivo sin su contenido tampoco se puede juzgar: los guards que miran qué se escribe —secretos,
|
|
175
|
+
// migraciones— verían un texto vacío. Basta que el campo esté; vacío es un archivo vacío.
|
|
176
|
+
const CONTENT_FIELDS = ['CodeContent', 'ReplacementContent', 'ReplacementChunks']
|
|
177
|
+
|
|
178
|
+
function undescribed(event, input, normalized) {
|
|
179
|
+
const need = DESCRIBED_BY[event]
|
|
180
|
+
if (!need || !Object.keys(input).length) return ''
|
|
181
|
+
const fields = (input.toolCall && input.toolCall.args) || {}
|
|
182
|
+
const args = Object.keys(fields)
|
|
183
|
+
const missing = !normalized.tool_input[need.field] ? need.names
|
|
184
|
+
: event === 'pre-files' && !CONTENT_FIELDS.some((field) => field in fields)
|
|
185
|
+
? 'CodeContent, ReplacementContent ni ReplacementChunks' : ''
|
|
186
|
+
if (!missing) return ''
|
|
187
|
+
const received = args.length ? `toolCall.args trae ${args.join(', ')}` : `llegó ${Object.keys(input).join(', ')}`
|
|
188
|
+
return `la llamada no trae ${missing}, así que no hay nada que juzgar y no se autoriza (${received}). Si `
|
|
189
|
+
+ 'Antigravity cambió el formato de sus llamadas, el puente tiene que aprenderlo en normalize().'
|
|
190
|
+
}
|
|
191
|
+
|
|
137
192
|
function evaluate(event, input) {
|
|
138
193
|
try {
|
|
139
194
|
const root = findRoot(input)
|
|
@@ -141,23 +196,30 @@ function evaluate(event, input) {
|
|
|
141
196
|
const hooks = runtimeAt(root)
|
|
142
197
|
const normalized = normalize(input, root)
|
|
143
198
|
if (!hooks.hookGroups[event]) throw new Error(`Evento Antigravity desconocido: ${event || '(vacío)'}`)
|
|
199
|
+
const missing = undescribed(event, input, normalized)
|
|
200
|
+
if (missing) throw new Error(missing)
|
|
144
201
|
hooks.executeAll([event], normalized)
|
|
145
202
|
return event === 'stop' ? { decision: 'stop' } : { decision: 'allow' }
|
|
146
203
|
} catch (error) {
|
|
147
|
-
|
|
148
|
-
if (event !== 'stop') return { decision: 'deny', reason }
|
|
149
|
-
// Un guard que bloquea marca su error con `blocked` (engine/hooks/run.js); cualquier otro es que el
|
|
150
|
-
// puente no llegó a juzgar nada. En `stop` los dos devolvían `continue`, y eso ata al agente: la
|
|
151
|
-
// raíz que no resuelve no se arregla sola, así que cada intento de cerrar repite el mismo error.
|
|
152
|
-
// El bloqueo sigue dando `continue` —es el mecanismo funcionando—; la falla deja cerrar y avisa.
|
|
153
|
-
return error.blocked ? { decision: 'continue', reason } : { decision: 'stop', reason }
|
|
204
|
+
return refusal(event, error.message, error.blocked)
|
|
154
205
|
}
|
|
155
206
|
}
|
|
156
207
|
|
|
157
|
-
function main() {
|
|
158
|
-
|
|
208
|
+
async function main(event = process.argv[2]) {
|
|
209
|
+
let input
|
|
210
|
+
try {
|
|
211
|
+
const engine = inputReader()
|
|
212
|
+
const usage = 'pasale el JSON de Antigravity —printf \'%s\' '
|
|
213
|
+
+ `'{"toolCall":{"args":{"CommandLine":"…"}}}' | node hook.js ${event || '<evento>'}—`
|
|
214
|
+
input = await engine.readInput(process.stdin, engine.FIRST_BYTE_MS, usage)
|
|
215
|
+
} catch (error) {
|
|
216
|
+
// El lector del motor marca `blocked` lo que no pudo leer, porque para un guard eso es bloquear. Acá
|
|
217
|
+
// es el puente sin nada que juzgar, y en `stop` eso deja cerrar: reintentar no arregla la entrada.
|
|
218
|
+
return respond(refusal(event, error.message, false))
|
|
219
|
+
}
|
|
220
|
+
respond(evaluate(event, input))
|
|
159
221
|
}
|
|
160
222
|
|
|
161
223
|
if (require.main === module) main()
|
|
162
224
|
|
|
163
|
-
module.exports = { evaluate, findRoot, normalize }
|
|
225
|
+
module.exports = { evaluate, findRoot, normalize, inputReader }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Cómo se lee una aceptación, compartido por las dos puntas que la juzgan: `check`, que avisa sobre la
|
|
2
|
+
// cola, y `autobuild`, que la manda a Verify. Vive acá y no en el motor porque el recorrido no puede hacer
|
|
3
|
+
// `require` —se renderiza con `{{INCLUDE:}}`— y el motor sí puede leer este archivo: lo carga
|
|
4
|
+
// `engine/planning/acceptance.js`. Escrito dos veces, una copia dejaba de reconocer lo que la otra pedía
|
|
5
|
+
// escribir, que es exactamente el caso 195: `check` ofrecía una marca que el recorrido no conocía.
|
|
6
|
+
|
|
7
|
+
// Una condición por tramo separado con `;`, el grano con el que Verify contrasta —su `uncovered` enumera
|
|
8
|
+
// criterios— y con el que `check` avisa.
|
|
9
|
+
const acceptanceConditions = (acceptance) => String(acceptance || '').split(';')
|
|
10
|
+
.map((one) => one.trim()).filter(Boolean)
|
|
11
|
+
|
|
12
|
+
// La salida explícita, con la forma que el repositorio ya usa dos veces: `(sin partir: …)` para el umbral
|
|
13
|
+
// de R17 y `n/a — razón` para `tests:` y `commit:`. Acá vale lo mismo que allá —«como lleva su razón
|
|
14
|
+
// escrita se lee en el propio artefacto sin que nadie la cruce»— y por eso no se intenta adivinar si la
|
|
15
|
+
// prosa excluye a Verify. Adivinarlo es lo que no se puede: la única aceptación real que nombra el commit
|
|
16
|
+
// lo hace justamente para decir que no es condición de Verify, y cualquier lista de frases que la
|
|
17
|
+
// reconociera enseñaría a escribir esa frase exacta para silenciar el aviso.
|
|
18
|
+
const OUT_OF_VERIFY = /\(fuera de verify:\s*[^)]+\)/i
|
|
19
|
+
|
|
20
|
+
// Lo que no se ejecuta, y por eso lo único que un criterio `no-surface` puede haber producido (caso 189).
|
|
21
|
+
// Es la lista a favor y no la de lo ejecutable a propósito (R27): un `.sql` de migración, un workflow en
|
|
22
|
+
// YAML, un `Dockerfile`, un `Makefile` o un `.json` de configuración se ejecutan sin parecer código, y una
|
|
23
|
+
// lista de lo ejecutable dejaría afuera lo que venga después. Ampliarla es un cambio con su razón al lado.
|
|
24
|
+
// Las imágenes entraron porque un ADR suele traer su diagrama, y sin ellas ese commit contaba como código.
|
|
25
|
+
// Lo que no tiene extensión sigue contando como ejecutable: `Makefile` y `Dockerfile` no la tienen.
|
|
26
|
+
const NON_EXECUTABLE = ['.md', '.txt', '.adoc', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp']
|
|
@@ -184,9 +184,11 @@ await agent(
|
|
|
184
184
|
// Sellar es lo último: hasta que el cambio no está aplicado y registrado, la propuesta sigue
|
|
185
185
|
// pendiente. Lo hace el motor y no vos, a mano, porque marcar el estado editando frontmatter es
|
|
186
186
|
// exactamente el paso que se hace mal en silencio.
|
|
187
|
+
// En el toolkit, sellar además anota el cambio en el CHANGELOG para que salga en la versión siguiente.
|
|
188
|
+
// Un mes sin cambios no le cambia nada a quien actualiza, y `--unchanged` lo deja fuera.
|
|
187
189
|
await agent(
|
|
188
190
|
`From ${ROOT}, run "node ${signature.cli || 'tools/ops.js'} ` +
|
|
189
|
-
`learn ${AGENT} --applied --period ${PERIOD}" and report only ` +
|
|
191
|
+
`learn ${AGENT} --applied --period ${PERIOD}${NOTHING ? ' --unchanged' : ''}" and report only ` +
|
|
190
192
|
`what it printed. Change nothing else.`,
|
|
191
193
|
{ label: 'sella' },
|
|
192
194
|
)
|