@ingeniomaps/cauce 0.25.0 → 0.26.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 +70 -0
- package/agents/roles/system/procurement-manager/SKILL.md +2 -0
- package/agents/roles/system/procurement-manager/evaluations/cases/07-exception-scope.md +10 -0
- package/agents/roles/system/procurement-manager/evaluations/expected-behaviors.yaml +2 -0
- package/agents/roles/system/procurement-manager/evaluations/results/2026-08-17-2.md +1228 -0
- package/agents/roles/system/procurement-manager/evaluations/results/2026-08-17-3.md +984 -0
- package/agents/roles/system/procurement-manager/learning/HISTORY.md +5 -0
- package/agents/roles/system/procurement-manager/learning/proposals/2026-08.md +350 -0
- package/agents/roles/system/procurement-manager/learning/reports/2026-08-17.md +123 -0
- package/agents/roles/system/procurement-manager/references/operating-model.md +29 -0
- package/agents/roles/system/release-manager/SKILL.md +2 -0
- package/agents/roles/system/release-manager/evaluations/cases/07-schema-safeguard-scope.md +10 -0
- package/agents/roles/system/release-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/release-manager/evaluations/results/2026-08-17-2.md +1353 -0
- package/agents/roles/system/release-manager/learning/HISTORY.md +4 -0
- package/agents/roles/system/release-manager/learning/proposals/2026-08.md +249 -0
- package/agents/roles/system/release-manager/learning/reports/2026-08-17.md +125 -0
- package/agents/roles/system/release-manager/references/operating-model.md +25 -0
- package/automatization/workflows/agent-eval.js +7 -3
- package/automatization/workflows/agent-promote.js +13 -8
- package/engine/agents/evaluations.js +42 -4
- package/engine/agents/learning.js +104 -6
- package/engine/cli/args.js +2 -2
- package/engine/cli/ops.js +7 -1
- package/engine/hooks/run.js +7 -2
- package/package.json +1 -1
- package/template/planning/rules/system/code-shape.md +15 -3
- package/template/planning/rules/system/conduct.md +10 -0
|
@@ -33,14 +33,30 @@ function assertWritable(root, agent) {
|
|
|
33
33
|
function isoDate(now = new Date()) { return now.toISOString().slice(0, 10) }
|
|
34
34
|
function month(now = new Date()) { return now.toISOString().slice(0, 7) }
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
// Una propuesta por período, y sus revisiones. La revisión existe porque aplicar no es el final del
|
|
37
|
+
// ciclo: la evaluación posterior es la que dice si el cambio sirvió, y cuando dice que no, el sello
|
|
38
|
+
// —que está para que nadie reaplique lo mismo y duplique cada viñeta— dejaba al cargo con un contrato
|
|
39
|
+
// que se sabe mal calibrado y sin camino para corregirlo hasta el mes siguiente. La corrección es un
|
|
40
|
+
// cambio distinto: documento propio, firma propia, y la aplicada queda sellada donde está.
|
|
41
|
+
const PROPOSAL_NAME = /^(\d{4}-\d{2})(?:-r(\d+))?\.md$/
|
|
42
|
+
|
|
43
|
+
// Mismo cuidado que con los registros de evaluación: `-` va antes que `.` en ASCII, así que ordenar
|
|
44
|
+
// nombres pondría `2026-08-r2.md` delante de `2026-08.md` y la revisión se leería como la más vieja.
|
|
45
|
+
function proposalOrder(name) {
|
|
46
|
+
const [, period, revision] = name.match(PROPOSAL_NAME)
|
|
47
|
+
return `${period}-${String(Number(revision || 1)).padStart(4, '0')}`
|
|
48
|
+
}
|
|
37
49
|
|
|
38
50
|
// El tope de la línea de índice. No es estético: son 47 líneas que se leen de un vistazo, y una que
|
|
39
51
|
// se envuelve rompe la columna que hace posible el vistazo.
|
|
40
52
|
const SUMMARY_MAX = 120
|
|
41
53
|
|
|
42
54
|
function proposalFiles(dir) {
|
|
43
|
-
try {
|
|
55
|
+
try {
|
|
56
|
+
return fs.readdirSync(dir)
|
|
57
|
+
.filter((name) => PROPOSAL_NAME.test(name))
|
|
58
|
+
.sort((one, other) => proposalOrder(one).localeCompare(proposalOrder(other)))
|
|
59
|
+
} catch { return [] }
|
|
44
60
|
}
|
|
45
61
|
|
|
46
62
|
function proposalState(text) {
|
|
@@ -58,8 +74,14 @@ function seal(root, agent, period = '') {
|
|
|
58
74
|
const dir = path.join(assertWritable(root, agent), 'learning', 'proposals')
|
|
59
75
|
const names = proposalFiles(dir)
|
|
60
76
|
if (!names.length) throw new Error(`${agent} no tiene propuestas en learning/proposals/.`)
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
// `--period 2026-08` nombra el período, no un archivo: con revisiones abiertas la que se sella es la
|
|
78
|
+
// vigente de ese período. Resolverlo siempre a `2026-08.md` habría devuelto «ya estaba aplicada» y
|
|
79
|
+
// dejado la revisión sin sellar, que es justo el estado en que `agent-promote` la vuelve a aplicar.
|
|
80
|
+
// Una revisión concreta se puede nombrar entera —`--period 2026-08-r2`— y entonces manda esa.
|
|
81
|
+
const name = period
|
|
82
|
+
? (/^\d{4}-\d{2}$/.test(period) ? lastOfPeriod(dir, period) : `${period}.md`)
|
|
83
|
+
: names[names.length - 1]
|
|
84
|
+
if (!name || !names.includes(name)) throw new Error(`${agent} no tiene la propuesta ${period || name}.`)
|
|
63
85
|
const file = path.join(dir, name)
|
|
64
86
|
const text = fs.readFileSync(file, 'utf8')
|
|
65
87
|
const state = proposalState(text)
|
|
@@ -109,12 +131,88 @@ propuesta consolidada. -->
|
|
|
109
131
|
return { file, created: true }
|
|
110
132
|
}
|
|
111
133
|
|
|
134
|
+
// Abre la revisión siguiente. No vuelve a consolidar los informes semanales: ya se consolidaron en la
|
|
135
|
+
// propuesta que ésta corrige, y repetirlos haría que el mismo hallazgo entre dos veces al contrato. El
|
|
136
|
+
// insumo de una revisión es otro —qué mostró la evaluación posterior a aplicar—, y por eso el molde
|
|
137
|
+
// pregunta eso y no otra cosa.
|
|
138
|
+
function reviseProposal(root, agent, dir, previo, now) {
|
|
139
|
+
const period = month(now)
|
|
140
|
+
const anterior = previo.match(PROPOSAL_NAME)
|
|
141
|
+
const revision = Number(anterior[2] || 1) + 1
|
|
142
|
+
const file = path.join(dir, `${period}-r${revision}.md`)
|
|
143
|
+
fs.writeFileSync(file, `---
|
|
144
|
+
agent: ${agent}
|
|
145
|
+
period: ${period}
|
|
146
|
+
revision: ${revision}
|
|
147
|
+
corrects: ${previo}
|
|
148
|
+
status: proposed
|
|
149
|
+
automatic_apply: false
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
# Propuesta mensual — ${period}, revisión ${revision}
|
|
153
|
+
|
|
154
|
+
Corrige \`${previo}\`, que ya está aplicada y queda sellada donde está. Esta revisión no la reemplaza ni
|
|
155
|
+
la reabre: es un cambio distinto, con su propia firma.
|
|
156
|
+
|
|
157
|
+
## Hallazgos
|
|
158
|
+
|
|
159
|
+
Qué mostró la evaluación posterior a aplicar \`${previo}\`. No repitas acá los hallazgos de esa propuesta
|
|
160
|
+
—ya entraron al contrato—: lo que va es lo que se supo después, con el registro de evaluación que lo
|
|
161
|
+
sostiene.
|
|
162
|
+
|
|
163
|
+
## Evidencia
|
|
164
|
+
|
|
165
|
+
El registro de la corrida que lo destapó, y la cita del veredicto. Si el cambio anterior falló por estar
|
|
166
|
+
mal calibrado, va también la línea del contrato que quedó floja y la del caso que la contradice.
|
|
167
|
+
|
|
168
|
+
## Cambio propuesto
|
|
169
|
+
|
|
170
|
+
Una revisión suele **no** ser aditiva: reemplaza texto que la propuesta anterior agregó. Decilo
|
|
171
|
+
explícitamente y decí por qué la aditividad no aplica acá — vale para lo que ya rindió sus casos, no para
|
|
172
|
+
un texto que acaba de fallar su primera medición.
|
|
173
|
+
|
|
174
|
+
## Riesgos y regresiones
|
|
175
|
+
|
|
176
|
+
Qué casos pasaban con el texto anterior y podrían dejar de pasar con éste. Nombralos por su id: son los
|
|
177
|
+
que hay que volver a correr.
|
|
178
|
+
|
|
179
|
+
## Evaluación
|
|
180
|
+
|
|
181
|
+
Cómo se comprueba que esta vez sí. Nombrá el caso que tiene que cambiar de veredicto y por qué razón, no
|
|
182
|
+
sólo que pase.
|
|
183
|
+
|
|
184
|
+
## Aprobación humana
|
|
185
|
+
|
|
186
|
+
- Estado: pendiente
|
|
187
|
+
- Responsable: por definir
|
|
188
|
+
- Fecha: por definir
|
|
189
|
+
`)
|
|
190
|
+
return { file, created: true, reports: 0, corrects: previo }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// La última propuesta del período, si la hay: es contra ella que se decide si abrir una revisión.
|
|
194
|
+
function lastOfPeriod(dir, period) {
|
|
195
|
+
const names = proposalFiles(dir).filter((name) => name.match(PROPOSAL_NAME)[1] === period)
|
|
196
|
+
return names.length ? names[names.length - 1] : ''
|
|
197
|
+
}
|
|
198
|
+
|
|
112
199
|
function prepareProposal(root, agent, now = new Date()) {
|
|
113
200
|
const target = assertWritable(root, agent)
|
|
114
201
|
const proposalDir = path.join(target, 'learning', 'proposals')
|
|
115
|
-
const file = path.join(proposalDir, `${month(now)}.md`)
|
|
116
202
|
fs.mkdirSync(proposalDir, { recursive: true })
|
|
117
|
-
|
|
203
|
+
|
|
204
|
+
// Una sola propuesta pendiente por período. Si la última todavía no se aplicó, abrir otra partiría
|
|
205
|
+
// la firma en dos documentos que dicen cosas distintas sobre el mismo contrato.
|
|
206
|
+
const previo = lastOfPeriod(proposalDir, month(now))
|
|
207
|
+
if (previo) {
|
|
208
|
+
const anterior = path.join(proposalDir, previo)
|
|
209
|
+
if (proposalState(fs.readFileSync(anterior, 'utf8')) !== 'applied') {
|
|
210
|
+
return { file: anterior, created: false, reports: 0 }
|
|
211
|
+
}
|
|
212
|
+
return reviseProposal(root, agent, proposalDir, previo, now)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const file = path.join(proposalDir, `${month(now)}.md`)
|
|
118
216
|
let names = []
|
|
119
217
|
try { names = fs.readdirSync(path.join(target, 'learning', 'reports')) } catch { /* vacío */ }
|
|
120
218
|
const reports = names.filter((name) => name.startsWith(month(now)) && /^\d{4}-\d{2}-\d{2}\.md$/.test(name)).sort()
|
package/engine/cli/args.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// requerido y no puede exportar nada sin dispararse.
|
|
6
6
|
|
|
7
7
|
// Banderas que consumen el argumento siguiente: su valor no es un posicional.
|
|
8
|
-
const VALUED_FLAGS = new Set(['--name', '--mode', '--fixture', '--period'])
|
|
8
|
+
const VALUED_FLAGS = new Set(['--name', '--mode', '--fixture', '--period', '--record'])
|
|
9
9
|
|
|
10
10
|
// Qué acepta cada comando, y a la vez qué comandos existen. Una bandera desconocida se rechaza en vez
|
|
11
11
|
// de ignorarse: `check --jsonn` imprimía la salida humana con código 0, así que quien esperaba JSON
|
|
@@ -21,7 +21,7 @@ const FLAGS = {
|
|
|
21
21
|
integration: ['--fixture'],
|
|
22
22
|
automation: ['--force'],
|
|
23
23
|
learn: ['--proposal', '--applied', '--period'],
|
|
24
|
-
evaluate: ['--cases', '--json', '--bench', '--force'],
|
|
24
|
+
evaluate: ['--cases', '--json', '--bench', '--force', '--record'],
|
|
25
25
|
team: ['--json'],
|
|
26
26
|
}
|
|
27
27
|
|
package/engine/cli/ops.js
CHANGED
|
@@ -51,7 +51,7 @@ function usage() {
|
|
|
51
51
|
ops automation doctor <ops-root> claude|codex|gemini|antigravity
|
|
52
52
|
ops automation install <ops-root> claude|codex|gemini|antigravity
|
|
53
53
|
ops learn <agent> [--proposal] [--applied [--period <AAAA-MM>]]
|
|
54
|
-
ops evaluate <agent> [--cases [--json]] [--bench [caso]]
|
|
54
|
+
ops evaluate <agent> [--cases [--json]] [--bench [caso]] [--record [AAAA-MM-DD]]
|
|
55
55
|
ops agents list [ops-root] [--own|--system] [--json]
|
|
56
56
|
ops agents fork <cargo> [ops-root]
|
|
57
57
|
ops team list
|
|
@@ -989,6 +989,12 @@ function evaluate(agent, caso, cli) {
|
|
|
989
989
|
// repetirla por caso invitaría a que alguien la editara en uno solo.
|
|
990
990
|
return console.log(JSON.stringify({ cases, forbidden: prohibido }))
|
|
991
991
|
}
|
|
992
|
+
// Dónde escribir el registro de esta corrida. Lo pregunta el recorrido en vez de componer el
|
|
993
|
+
// nombre, que es lo que hacía que la segunda corrida de un día borrara a la primera.
|
|
994
|
+
if (cli.has('--record')) {
|
|
995
|
+
const dia = cli.value('--record') || new Date().toISOString().slice(0, 10)
|
|
996
|
+
return console.log(path.relative(root, path.join(EV.resultsDir(root, agent), EV.nextResult(root, agent, dia))))
|
|
997
|
+
}
|
|
992
998
|
const result = L.evaluate(root, agent)
|
|
993
999
|
const runs = EV.validate(root, agent)
|
|
994
1000
|
const errors = [...result.errors, ...runs.errors]
|
package/engine/hooks/run.js
CHANGED
|
@@ -185,9 +185,14 @@ function workspaceBoundary(input) {
|
|
|
185
185
|
|
|
186
186
|
function migrations(input) {
|
|
187
187
|
if (process.env.OPS_MIGRATIONS_OVERRIDE === '1') return
|
|
188
|
+
// Cada rama cierra su propio límite. Cuando el `\b` estaba al final del grupo se aplicaba a las tres, y
|
|
189
|
+
// la de `delete` termina a propósito en `;`: después de un punto y coma no hay límite de palabra, así que
|
|
190
|
+
// `DELETE FROM pedidos;` —la forma que tiene en cualquier migración— pasaba y sólo frenaba la variante sin
|
|
191
|
+
// punto y coma. `drop column` y `drop constraint` faltaban: pierden datos y garantías igual que `drop table`.
|
|
188
192
|
const destructiveSql = new RegExp(
|
|
189
|
-
String.raw`\
|
|
190
|
-
String.raw
|
|
193
|
+
String.raw`\bdrop\s+(?:table|database|schema|column|constraint)\b` +
|
|
194
|
+
String.raw`|\btruncate\b` +
|
|
195
|
+
String.raw`|\bdelete\s+from\s+\S+\s*(?:;|$)`,
|
|
191
196
|
'i',
|
|
192
197
|
)
|
|
193
198
|
if (destructiveSql.test(contentOf(input))) {
|
package/package.json
CHANGED
|
@@ -13,7 +13,19 @@ No reformatear, renombrar ni reparar elementos ajenos a la tarea.
|
|
|
13
13
|
Extraer responsabilidades cuando una unidad deja de poder entenderse y probarse aisladamente; no usar
|
|
14
14
|
límites numéricos como sustituto del juicio.
|
|
15
15
|
|
|
16
|
-
## R11 — Comentarios
|
|
16
|
+
## R11 — Comentarios con destinatario
|
|
17
17
|
|
|
18
|
-
Un comentario
|
|
19
|
-
|
|
18
|
+
Un comentario existe para lo que no se ve leyendo el código, y se escribe para alguien que lo va a
|
|
19
|
+
preguntar o a deshacer sin saberlo. Si nadie lo preguntaría, sobra: tener un porqué no alcanza, porque
|
|
20
|
+
una convención también lo tiene.
|
|
21
|
+
|
|
22
|
+
Cada razón vive en un solo lugar, el que la vio nacer; repetida en otro archivo, una copia se pudre y
|
|
23
|
+
nada falla.
|
|
24
|
+
|
|
25
|
+
- **Dentro de una unidad**: el porqué. La restricción, el caso que la forzó, lo que se probó antes.
|
|
26
|
+
- **Encabezando una unidad**: qué garantiza, un nivel por encima del código, para poder usarla sin
|
|
27
|
+
leerla entera. Su largo lo fija ese contrato.
|
|
28
|
+
- **Donde ningún nombre alcanza**: la fuente de un algoritmo o una norma, qué es un valor opaco, por
|
|
29
|
+
qué algo se aparta del idioma habitual, y qué quedó a medias.
|
|
30
|
+
|
|
31
|
+
Un comentario que cuesta escribir suele estar señalando el código, no la falta de palabras.
|
|
@@ -51,6 +51,16 @@ Son dos ejes distintos y el registro va en los dos: marcar como supuesto un núm
|
|
|
51
51
|
mecanismo del que ese número se deriva. Un parámetro declarado supuesto sigue prometiendo el efecto que se le
|
|
52
52
|
atribuye, y esa atribución es la que lleva registro.
|
|
53
53
|
|
|
54
|
+
El registro viaja con la afirmación, no con el documento que la explica. Una lección, una regla propuesta, una
|
|
55
|
+
fila de acciones humanas o un paso de runbook existen para leerse solos, así que una afirmación de mecanismo que
|
|
56
|
+
sale del informe hacia uno de ellos lleva su registro o no sale. Ahí es donde más se pierde: el informe clasifica
|
|
57
|
+
con cuidado y el artefacto derivado repite la afirmación en plano, ya sin nada que la acote, y es el que alguien
|
|
58
|
+
va a leer dentro de un mes.
|
|
59
|
+
|
|
60
|
+
Y el disparador es a dónde va la afirmación, no cuán discutible parece. Quien elige qué clasificar clasifica lo
|
|
61
|
+
que espera que le discutan, y deja plano lo que sostiene su propio procedimiento — que es justamente lo que nadie
|
|
62
|
+
va a revisar.
|
|
63
|
+
|
|
54
64
|
La verificación llega hasta donde R12 permite: fuente pública, `--help`, `--version`, una invocación inocua.
|
|
55
65
|
Nunca conectarse a un sistema real ni ejecutar la operación cuyo efecto se describe. Si el mecanismo sólo se
|
|
56
66
|
establece ejecutando lo destructivo, queda en hipótesis; acá la abstención vale más que el dato.
|