@ingeniomaps/cauce 0.26.0 → 0.28.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 +63 -0
- package/README.md +93 -15
- package/agents/roles/system/ai-governance-lead/evaluations/results/2026-08-17.md +1501 -0
- package/agents/roles/system/backend-engineer/evaluations/results/2026-08-18.md +1735 -0
- package/agents/roles/system/financial-controller/evaluations/results/2026-08-18.md +1585 -0
- package/agents/roles/system/sales-representative/evaluations/results/2026-08-17.md +1135 -0
- package/agents/roles/system/sales-representative/evaluations/results/2026-08-18.md +1200 -0
- package/agents/roles/system/security-engineer/evaluations/results/2026-08-18.md +1993 -0
- package/agents/roles/system/site-reliability-engineer/evaluations/results/2026-08-18.md +1620 -0
- package/agents/roles/system/technical-writer/evaluations/results/2026-08-17.md +1388 -0
- package/agents/roles/system/technical-writer/evaluations/results/2026-08-18.md +1329 -0
- package/automatization/runners/antigravity/manifest.json +4 -0
- package/automatization/runners/antigravity/skills/onboard/SKILL.md +30 -0
- package/automatization/runners/claude/CLAUDE.md +3 -2
- package/automatization/runners/claude/manifest.json +4 -0
- package/automatization/runners/codex/AGENTS.md +14 -0
- package/automatization/runners/gemini/GEMINI.md +14 -0
- package/automatization/workflows/agent-eval.js +21 -0
- package/automatization/workflows/onboard.js +256 -0
- package/engine/agents/catalog.js +5 -4
- package/engine/cli/args.js +4 -2
- package/engine/cli/bootstrap.js +96 -0
- package/engine/cli/ops.js +107 -17
- package/package.json +1 -1
- package/template/planning/rules/system/conduct.md +23 -0
package/engine/cli/ops.js
CHANGED
|
@@ -19,9 +19,13 @@ const T = require('../teams/registry')
|
|
|
19
19
|
const AG = require('../agents/catalog')
|
|
20
20
|
const EV = require('../agents/evaluations')
|
|
21
21
|
const { FLAGS, parse } = require('./args')
|
|
22
|
+
const BOOT = require('./bootstrap')
|
|
22
23
|
|
|
23
24
|
const PROJECT_ROOT = path.resolve(__dirname, '..', '..')
|
|
24
25
|
|
|
26
|
+
// Dónde aterriza una instancia cuando nadie eligió: una carpeta propia junto al código.
|
|
27
|
+
const DEFAULT_TARGET = 'ops'
|
|
28
|
+
|
|
25
29
|
function fail(message, code = 1) {
|
|
26
30
|
console.error(message)
|
|
27
31
|
process.exit(code)
|
|
@@ -29,7 +33,8 @@ function fail(message, code = 1) {
|
|
|
29
33
|
|
|
30
34
|
function usage() {
|
|
31
35
|
console.log(`Uso:
|
|
32
|
-
ops init
|
|
36
|
+
ops init [destino] [--name <nombre>] [--mode embedded|sidecar] [--force]
|
|
37
|
+
[--runner claude|codex|gemini|antigravity] [--integration <proveedor>] [--install|--no-install]
|
|
33
38
|
ops check <planning-dir> [--json]
|
|
34
39
|
ops tree <planning-dir> [--no-color] [--json]
|
|
35
40
|
ops context <planning-dir> [--json]
|
|
@@ -142,12 +147,8 @@ function scaffold(root, { name, mode, force = false, quiet = false }) {
|
|
|
142
147
|
root,
|
|
143
148
|
)
|
|
144
149
|
const version = require(path.join(PROJECT_ROOT, 'package.json')).version
|
|
145
|
-
// El motor
|
|
146
|
-
//
|
|
147
|
-
// que la copia sólo ahorraba este `package.json` de seis líneas a cambio de 5 MB en la historia de
|
|
148
|
-
// la empresa y de no tener cómo enterarse de que salió una versión nueva.
|
|
149
|
-
//
|
|
150
|
-
// El repo ops es un sidecar: declarar npm acá no convierte en Node al servicio de Go de al lado.
|
|
150
|
+
// El motor llega como dependencia para que el lockfile fije su versión. El repo ops es un sidecar:
|
|
151
|
+
// declarar npm acá no convierte en Node al servicio de Go de al lado.
|
|
151
152
|
declareEngine(path.join(root, 'package.json'), version)
|
|
152
153
|
let entregado = {}
|
|
153
154
|
for (const relative of O.trackedPaths()) {
|
|
@@ -225,21 +226,110 @@ function evaluationBench(root, agent, caso, force) {
|
|
|
225
226
|
return dir
|
|
226
227
|
}
|
|
227
228
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
// Dónde va la instancia cuando nadie eligió destino. Parada frecuente: el dev ya creó `acme-ops/` y
|
|
230
|
+
// corre `init` adentro. Sin esto la instancia caía en `acme-ops/ops/` —una carpeta del toolkit dentro
|
|
231
|
+
// de otra— y el proyecto quedaba llamándose «acme-ops». La carpeta que ya nombra al toolkit es la
|
|
232
|
+
// instancia; no hay una segunda adentro.
|
|
233
|
+
function implicitTarget(cwd) {
|
|
234
|
+
const base = path.basename(cwd)
|
|
235
|
+
return base === DEFAULT_TARGET || base.endsWith('-ops') ? '.' : DEFAULT_TARGET
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// El nombre sale de la carpeta del proyecto, no de la que aloja la instancia: `ops/` y `acme-ops/`
|
|
239
|
+
// nombran al toolkit, y quien lee `project` en la configuración espera leer «acme».
|
|
240
|
+
function defaultName(root) {
|
|
241
|
+
const base = path.basename(root)
|
|
242
|
+
return base === DEFAULT_TARGET ? path.basename(path.dirname(root)) : base.replace(/-ops$/, '')
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// El motor no se instala solo: `npm install` baja el paquete que `init` acaba de declarar, y sin él el
|
|
246
|
+
// shim, los cargos, los equipos y los adaptadores no se resuelven. Correrlo desde acá es lo que hace que
|
|
247
|
+
// una instalación sea un comando y no una lista. En Windows el ejecutable es `npm.cmd`.
|
|
248
|
+
function npmInstall(root) {
|
|
249
|
+
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
|
250
|
+
const result = spawnSync(npm, ['install'], { cwd: root, stdio: 'inherit' })
|
|
251
|
+
if (result.error) {
|
|
252
|
+
console.error(` no pude ejecutar npm (${result.error.code || result.error.message}).`)
|
|
253
|
+
return 1
|
|
254
|
+
}
|
|
255
|
+
return result.status === null ? 1 : result.status
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Un `cli` que no tiene banderas, para reusar un comando desde otro: el `--force` de `init` habla del
|
|
259
|
+
// molde y no del wiring del runner, así que pasarle el suyo instalaría a la fuerza algo que nadie pidió.
|
|
260
|
+
const SIN_BANDERAS = { has: () => false, value: (_flag, fallback = '') => fallback }
|
|
261
|
+
|
|
262
|
+
// Lo que quedó pendiente, y sólo eso: cuando la instalación corrió, `check` ya se ejecutó y repetirlo
|
|
263
|
+
// como sugerencia hace dudar de que haya pasado.
|
|
264
|
+
function initSteps(enter, resultado) {
|
|
265
|
+
if (resultado.instalado) return []
|
|
266
|
+
const pasos = ['npm install']
|
|
267
|
+
if (resultado.runner !== BOOT.SIN_RUNNER) {
|
|
268
|
+
pasos.push(`node tools/ops.js automation install . ${resultado.runner}`)
|
|
269
|
+
}
|
|
270
|
+
pasos.push('node tools/ops.js check planning')
|
|
271
|
+
return pasos.map((paso, indice) => ` siguiente: ${indice === 0 ? enter : ''}${paso}`)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function init(target, cli) {
|
|
275
|
+
// Sin destino la instancia va a `ops/` y en modo sidecar, en vez de volcarse donde esté parado el
|
|
276
|
+
// dev: un monorepo que recibe `planning/`, `teams/`, `organization/` y `AGENTS.md` en su primer
|
|
277
|
+
// nivel deja de distinguir qué es suyo y qué llegó del toolkit. Es el layout que `automation
|
|
278
|
+
// install` ya asume —el wiring del runner va al padre, donde se abre la herramienta—, así que lo
|
|
279
|
+
// único que faltaba era que fuera lo que pasa cuando no se elige nada.
|
|
280
|
+
const root = path.resolve(target || implicitTarget(process.cwd()))
|
|
281
|
+
const mode = cli.value('--mode', target ? 'embedded' : 'sidecar')
|
|
231
282
|
if (!['embedded', 'sidecar'].includes(mode)) fail('--mode debe ser embedded o sidecar.', 2)
|
|
232
|
-
const name = cli.value('--name',
|
|
233
|
-
const root = path.resolve(target)
|
|
283
|
+
const name = cli.value('--name', defaultName(root))
|
|
234
284
|
const force = cli.has('--force')
|
|
235
|
-
|
|
285
|
+
// `.git` no cuenta como contenido: es lo único que hay en la carpeta que alguien acaba de crear y
|
|
286
|
+
// versionar para la instancia, y el toolkit no escribe nada adentro. Sin esta excepción el camino
|
|
287
|
+
// más natural —`mkdir acme-ops && git init && cauce init`— pedía `--force` para no pisar nada.
|
|
288
|
+
const existing = (fs.existsSync(root) ? fs.readdirSync(root) : []).filter((entry) => entry !== '.git')
|
|
236
289
|
if (existing.length && !force) {
|
|
237
290
|
fail(`El destino no está vacío: ${root}. Usa --force para agregar solo archivos faltantes.`)
|
|
238
291
|
}
|
|
239
292
|
scaffold(root, { name, mode, force })
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
console.log(
|
|
293
|
+
const relative = path.relative(process.cwd(), root)
|
|
294
|
+
const enter = relative && relative !== '.' ? `cd ${relative} && ` : ''
|
|
295
|
+
console.log(`\n✓ ${name}: sistema ops creado en ${root} (modo ${mode})`)
|
|
296
|
+
|
|
297
|
+
// Preguntar exige una terminal, e instalar baja un paquete y escribe `node_modules`: las dos cosas
|
|
298
|
+
// pasan cuando hay alguien mirando. Una corrida automatizada —CI, un contenedor, estas pruebas—
|
|
299
|
+
// recibe la instancia materializada y decide por bandera, sin descargas ni preguntas implícitas.
|
|
300
|
+
const interactivo = Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
301
|
+
const opciones = {
|
|
302
|
+
runner: cli.value('--runner'),
|
|
303
|
+
integration: cli.value('--integration'),
|
|
304
|
+
runners: A.RUNNER_NAMES,
|
|
305
|
+
providers: providerNames(),
|
|
306
|
+
interactive: interactivo,
|
|
307
|
+
install: cli.has('--install') || (interactivo && !cli.has('--no-install')),
|
|
308
|
+
}
|
|
309
|
+
let resultado
|
|
310
|
+
try {
|
|
311
|
+
resultado = await BOOT.run(root, opciones, {
|
|
312
|
+
log: console.log,
|
|
313
|
+
npm: npmInstall,
|
|
314
|
+
installRunner: (runner) => automation('install', root, runner, SIN_BANDERAS),
|
|
315
|
+
enableProvider: (provider) => INTEGRATION.enable.run(root, provider),
|
|
316
|
+
})
|
|
317
|
+
} catch (error) { fail(error.message, 2) }
|
|
318
|
+
|
|
319
|
+
if (resultado.instalado) {
|
|
320
|
+
check(path.join(root, 'planning'), SIN_BANDERAS)
|
|
321
|
+
// Una instancia recién instalada funciona y no sabe nada de este proyecto: `organization/` es el
|
|
322
|
+
// molde y el roadmap está vacío. Llenarlo exige leer el repositorio y decidir qué es cada cosa, que
|
|
323
|
+
// es justo lo que un CLI determinista no puede hacer; el recorrido vive en el runner, así que lo
|
|
324
|
+
// único útil acá es decir cuál es y con qué se abre.
|
|
325
|
+
if (resultado.runner !== BOOT.SIN_RUNNER) {
|
|
326
|
+
console.log(` siguiente: abrí ${resultado.runner} en este directorio y corré /onboard`)
|
|
327
|
+
console.log(' escanea el repositorio y deja escrito el contexto de la empresa y la primera épica')
|
|
328
|
+
}
|
|
329
|
+
console.log(` listo: el ciclo empieza en ${path.join(relative || '.', 'planning', 'FLOW.md')}`)
|
|
330
|
+
}
|
|
331
|
+
for (const paso of initSteps(enter, resultado)) console.log(paso)
|
|
332
|
+
if (resultado.error) fail(`${resultado.error}: la instancia quedó creada pero todavía no funciona.`)
|
|
243
333
|
}
|
|
244
334
|
|
|
245
335
|
function check(dir, cli) {
|
|
@@ -1047,7 +1137,7 @@ async function run(cli) {
|
|
|
1047
1137
|
fail(`${command}: bandera desconocida ${sobran.join(', ')}. ${acepta}`, 2)
|
|
1048
1138
|
}
|
|
1049
1139
|
const arg = cli.positional
|
|
1050
|
-
if (command === 'init') init(arg[1], cli)
|
|
1140
|
+
if (command === 'init') await init(arg[1], cli)
|
|
1051
1141
|
else if (command === 'check') check(arg[1], cli)
|
|
1052
1142
|
else if (command === 'tree') tree(arg[1], cli)
|
|
1053
1143
|
else if (command === 'context') context(arg[1], cli)
|
package/package.json
CHANGED
|
@@ -67,3 +67,26 @@ establece ejecutando lo destructivo, queda en hipótesis; acá la abstención va
|
|
|
67
67
|
|
|
68
68
|
No se infiere el default de una herramienta desde otra del mismo paquete, ni una regla de una jurisdicción
|
|
69
69
|
desde otra. Una negativa correcta sostenida en un mecanismo falso queda tan comprometida como el mecanismo.
|
|
70
|
+
|
|
71
|
+
## R15 — Lo que el contrato enumera no desaparece del entregable
|
|
72
|
+
|
|
73
|
+
Una entrega puede estar incompleta; lo que no puede es parecer completa. Cuando el contrato enumera las
|
|
74
|
+
dimensiones que una entrega cubre —los criterios de un scorecard, los ejes de un descubrimiento, los campos
|
|
75
|
+
de un contrato de release, las secciones de un informe—, dejar una afuera sin que se vea produce algo que se
|
|
76
|
+
lee entero y no lo está. Nadie va a pedir después lo que falta, porque nada indica que faltaba.
|
|
77
|
+
|
|
78
|
+
El daño no está en la omisión sino en su forma. Una rúbrica cuyos pesos suman 100 %, una guía con todas sus
|
|
79
|
+
preguntas, una plantilla con todos sus campos llenos: la estructura afirma completitud aunque ninguna frase
|
|
80
|
+
lo diga, y quien decide sobre eso no tiene cómo saber que había una dimensión más.
|
|
81
|
+
|
|
82
|
+
Una ausencia no deja rastro, así que no se detecta leyendo lo escrito: **antes de entregar se contrasta el
|
|
83
|
+
entregable contra la enumeración del contrato**, dimensión por dimensión. Es mecánico y barato, y es lo
|
|
84
|
+
único que la encuentra — revisar lo que está nunca muestra lo que no está.
|
|
85
|
+
|
|
86
|
+
La dimensión que todavía no se puede cubrir no se borra: queda en el entregable con qué la activa, qué
|
|
87
|
+
evidencia la cierra y quién la revisa. Declararla ausente alcanza sólo cuando cubrirla es imposible y no
|
|
88
|
+
apenas prematuro, y esa declaración va donde iba la dimensión, no en una nota al pie: sirve para que la
|
|
89
|
+
lea quien decide, no para dejar constancia de que se sabía.
|
|
90
|
+
|
|
91
|
+
Es la contraparte de R13, y las dos terminan igual. Ahí lo que no se entrega es lo que sí se podía; acá lo
|
|
92
|
+
que se entrega tapa lo que faltó. En los dos casos alguien decide con menos de lo que cree tener.
|