@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.
Files changed (25) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +93 -15
  3. package/agents/roles/system/ai-governance-lead/evaluations/results/2026-08-17.md +1501 -0
  4. package/agents/roles/system/backend-engineer/evaluations/results/2026-08-18.md +1735 -0
  5. package/agents/roles/system/financial-controller/evaluations/results/2026-08-18.md +1585 -0
  6. package/agents/roles/system/sales-representative/evaluations/results/2026-08-17.md +1135 -0
  7. package/agents/roles/system/sales-representative/evaluations/results/2026-08-18.md +1200 -0
  8. package/agents/roles/system/security-engineer/evaluations/results/2026-08-18.md +1993 -0
  9. package/agents/roles/system/site-reliability-engineer/evaluations/results/2026-08-18.md +1620 -0
  10. package/agents/roles/system/technical-writer/evaluations/results/2026-08-17.md +1388 -0
  11. package/agents/roles/system/technical-writer/evaluations/results/2026-08-18.md +1329 -0
  12. package/automatization/runners/antigravity/manifest.json +4 -0
  13. package/automatization/runners/antigravity/skills/onboard/SKILL.md +30 -0
  14. package/automatization/runners/claude/CLAUDE.md +3 -2
  15. package/automatization/runners/claude/manifest.json +4 -0
  16. package/automatization/runners/codex/AGENTS.md +14 -0
  17. package/automatization/runners/gemini/GEMINI.md +14 -0
  18. package/automatization/workflows/agent-eval.js +21 -0
  19. package/automatization/workflows/onboard.js +256 -0
  20. package/engine/agents/catalog.js +5 -4
  21. package/engine/cli/args.js +4 -2
  22. package/engine/cli/bootstrap.js +96 -0
  23. package/engine/cli/ops.js +107 -17
  24. package/package.json +1 -1
  25. 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 <destino> [--name <nombre>] [--mode embedded|sidecar] [--force]
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 siempre llega como dependencia. La alternativa era vendorizarlo en `.ops/`, y no valía:
146
- // Node hace falta igual en los dos casos —el motor, los guards y los workflows son JavaScript—, así
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
- function init(target, cli) {
229
- if (!target) fail('Falta <destino>.', 2)
230
- const mode = cli.value('--mode', 'embedded')
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', path.basename(path.resolve(target)).replace(/-ops$/, ''))
233
- const root = path.resolve(target)
283
+ const name = cli.value('--name', defaultName(root))
234
284
  const force = cli.has('--force')
235
- const existing = fs.existsSync(root) ? fs.readdirSync(root) : []
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
- console.log(`\n✓ ${name}: sistema ops creado en ${root}`)
241
- console.log(' siguiente: npm install (el motor viene de la dependencia)')
242
- console.log(` siguiente: node ${path.join(root, 'tools', 'ops.js')} check ${path.join(root, 'planning')}`)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingeniomaps/cauce",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "Sistema portable de planificación y ejecución verificable para cualquier proyecto",
5
5
  "keywords": [
6
6
  "planning",
@@ -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.