@ingeniomaps/cauce 0.9.1 → 0.10.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 +28 -0
- package/README.md +9 -8
- package/automatization/AGENTS.md +1 -1
- package/automatization/hooks/README.md +1 -1
- package/automatization/hooks/run-hook.sh +3 -4
- package/automatization/runners/antigravity/hook.js +1 -2
- package/automatization/runners/codex/AGENTS.md +1 -2
- package/engine/automation/index.js +3 -3
- package/engine/cli/ops.js +20 -40
- package/engine/core/ownership.js +7 -13
- package/package.json +1 -1
- package/template/automatization/AGENTS.md +1 -1
- package/template/integrations/AGENTS.md +1 -1
- package/template/ops.config.json +1 -1
- package/template/planning/adr/system/OPS-002-runtime-autocontenido-y-neutral-al-runner.md +10 -4
- package/template/tools/ops.js +2 -4
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,34 @@ esa operación sea confiable en vez de sólo cómoda: acá se lee qué cambió a
|
|
|
8
8
|
un cambio en el protocolo, en las reglas del sistema o en un guard es visible para el usuario y sube
|
|
9
9
|
minor aunque no toque una sola línea de código.
|
|
10
10
|
|
|
11
|
+
## [0.10.0] - 2026-08-15
|
|
12
|
+
|
|
13
|
+
### Cambiado
|
|
14
|
+
|
|
15
|
+
- **El motor llega siempre como dependencia. Se retiró el modo copia.** `--engine copy|dependency` ya
|
|
16
|
+
no existe: `init` declara `@ingeniomaps/cauce` en el `package.json` del repo ops, creándolo si hace
|
|
17
|
+
falta.
|
|
18
|
+
|
|
19
|
+
La copia en `.ops/` existía para no exigirle npm a un repo de Go, Python o Rust. Dejó de tener
|
|
20
|
+
sentido cuando el repo ops pasó a ser un **sidecar**, hermano de los repos de producto: declarar npm
|
|
21
|
+
ahí no le impone un stack a ninguno. Y Node hace falta igual —el motor, los guards y los workflows
|
|
22
|
+
son JavaScript—, así que la copia sólo ahorraba un `package.json` de seis líneas a cambio de 5 MB y
|
|
23
|
+
763 archivos en la historia de la empresa, y de no tener cómo enterarse de que salió una versión
|
|
24
|
+
nueva: sin npm no hay `npm outdated`.
|
|
25
|
+
- Los tres resolutores en cascada —`tools/ops.js`, `run-hook.sh` y el motor— pasan de tres caminos a
|
|
26
|
+
dos. Menos superficie donde esconder un caso raro.
|
|
27
|
+
- Una instancia que arrastra `.ops/` **no se toca**: `upgrade` avisa que Cauce ya no lo distribuye y
|
|
28
|
+
dice qué correr. Borrarlo por su cuenta la dejaría sin motor.
|
|
29
|
+
- El `$schema` de `ops.config.json` ya no depende del modo.
|
|
30
|
+
|
|
31
|
+
## [0.9.2] - 2026-08-15
|
|
32
|
+
|
|
33
|
+
### Corregido
|
|
34
|
+
|
|
35
|
+
- Una instancia ya no recibe un `.github/workflows/` vacío. `init` lo copiaba salteando los dos
|
|
36
|
+
únicos archivos que existen —`ci.yml` valida el toolkit y el ciclo de aprendizaje dejó de
|
|
37
|
+
distribuirse en 0.4.0—, así que creaba dos directorios y no ponía nada adentro.
|
|
38
|
+
|
|
11
39
|
## [0.9.1] - 2026-08-15
|
|
12
40
|
|
|
13
41
|
### Corregido
|
package/README.md
CHANGED
|
@@ -35,11 +35,12 @@ node engine/cli/ops.js team show product-development
|
|
|
35
35
|
El destino debe estar vacío o no existir. En modo embebido normalmente ya es un repo: `--force` permite
|
|
36
36
|
completar archivos faltantes, pero nunca sobrescribe archivos existentes.
|
|
37
37
|
|
|
38
|
-
El motor llega
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
El motor llega como dependencia y el lockfile fija la versión. `init` declara `@ingeniomaps/cauce` en el
|
|
39
|
+
`package.json` del repo ops —creándolo si no existe— y el proyecto invoca `node tools/ops.js`, que resuelve
|
|
40
|
+
el motor sin que nadie tenga que saber dónde está.
|
|
41
|
+
|
|
42
|
+
Declarar npm ahí no le impone un stack a nadie: el repo ops es un sidecar, hermano de los repos de
|
|
43
|
+
producto, y Node hace falta igual —el motor, los guards y los workflows son JavaScript—.
|
|
43
44
|
|
|
44
45
|
Dentro de un proyecto generado, el CLI autocontenido se invoca con `node tools/ops.js`. En la tabla siguiente,
|
|
45
46
|
`ops` representa cualquiera de esas dos formas según el contexto. El binario `cauce` también queda
|
|
@@ -169,8 +170,8 @@ y actualizar no exige resolver conflictos: se reemplaza `system/` entero y nada
|
|
|
169
170
|
|
|
170
171
|
### Dónde vive cada cosa del catálogo
|
|
171
172
|
|
|
172
|
-
Los cargos que trae Cauce **no se copian al proyecto**: se resuelven desde la dependencia
|
|
173
|
-
|
|
173
|
+
Los cargos que trae Cauce **no se copian al proyecto**: se resuelven desde la dependencia. Evolucionan
|
|
174
|
+
como profesión, y esa evolución es la misma para
|
|
174
175
|
todas las empresas: investigarla una vez y bien es mejor que repetirla en cada instalación.
|
|
175
176
|
|
|
176
177
|
| Qué | Dónde | Quién lo mantiene |
|
|
@@ -232,7 +233,7 @@ promoción y validación no se reimplementan. Consulta [integrations/README.md](
|
|
|
232
233
|
|
|
233
234
|
El toolkit no guarda contexto real de ninguna empresa. `template/organization/` es el molde que cada proyecto
|
|
234
235
|
recibe como `organization/`. De igual forma, `planning/` pertenece a la instancia generada: conserva su
|
|
235
|
-
intención, estado y evidencia, mientras el motor reusable permanece en
|
|
236
|
+
intención, estado y evidencia, mientras el motor reusable permanece en la dependencia.
|
|
236
237
|
|
|
237
238
|
## Hooks y runners
|
|
238
239
|
|
package/automatization/AGENTS.md
CHANGED
|
@@ -8,7 +8,7 @@ implementa y conecta ese proceso con runners concretos: no redefine producto, pr
|
|
|
8
8
|
|
|
9
9
|
| Ruta | Responsabilidad | Fuente canónica |
|
|
10
10
|
|---|---|---|
|
|
11
|
-
| `hooks/` | Entradas ejecutables estables para los guards. | La lógica vive una sola vez en
|
|
11
|
+
| `hooks/` | Entradas ejecutables estables para los guards. | La lógica vive una sola vez en `engine/hooks/run.js`, que llega con la dependencia. |
|
|
12
12
|
| `workflows/` | Recorridos portables de build e integraciones. | Las fases y gates pertenecen a `../planning/PROTOCOL.md`. |
|
|
13
13
|
| `runners/` | Wiring, capacidades e instalación por herramienta. | Cada adaptador declara su contrato en `manifest.json`. |
|
|
14
14
|
|
|
@@ -25,7 +25,7 @@ o automatization/hooks/guard-<nombre>.sh (guard suelto)
|
|
|
25
25
|
↓ nombre del grupo o del guard
|
|
26
26
|
automatization/hooks/run-hook.sh
|
|
27
27
|
↓ localiza el runtime
|
|
28
|
-
|
|
28
|
+
node_modules/@ingeniomaps/cauce/engine/hooks/run.js (instancia)
|
|
29
29
|
o engine/hooks/run.js (repositorio del toolkit)
|
|
30
30
|
```
|
|
31
31
|
|
|
@@ -8,12 +8,11 @@ ops_root=$(CDPATH= cd -- "$hook_dir/../.." && pwd)
|
|
|
8
8
|
# abre en la carpeta de la compañía y la raíz ops es un hermano, que ninguna búsqueda hacia arriba
|
|
9
9
|
# encuentra: sin esto el guard no halla `ops.config.json` y deja pasar todo en silencio.
|
|
10
10
|
export OPS_ROOT="$ops_root"
|
|
11
|
-
# Mismo orden que tools/ops.js:
|
|
12
|
-
#
|
|
11
|
+
# Mismo orden que tools/ops.js: la dependencia npm y, por último, el propio repositorio del toolkit.
|
|
12
|
+
# Un guard que no encuentra su motor bloquea, nunca permite.
|
|
13
13
|
runner=""
|
|
14
14
|
for candidate in \
|
|
15
15
|
"$ops_root/node_modules/@ingeniomaps/cauce/engine/hooks/run.js" \
|
|
16
|
-
"$ops_root/.ops/engine/hooks/run.js" \
|
|
17
16
|
"$ops_root/engine/hooks/run.js"
|
|
18
17
|
do
|
|
19
18
|
if [ -f "$candidate" ]; then runner="$candidate"; break; fi
|
|
@@ -21,7 +20,7 @@ done
|
|
|
21
20
|
|
|
22
21
|
if [ -z "$runner" ]; then
|
|
23
22
|
echo "BLOQUEADO [$hook_name]: no se encontró el motor de hooks de Cauce." >&2
|
|
24
|
-
echo " Buscado en node_modules/@ingeniomaps/cauce
|
|
23
|
+
echo " Buscado en node_modules/@ingeniomaps/cauce y engine/ bajo $ops_root" >&2
|
|
25
24
|
exit 2
|
|
26
25
|
fi
|
|
27
26
|
|
|
@@ -44,10 +44,9 @@ function findRoot(input) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function runtimeAt(root) {
|
|
47
|
-
// Mismo orden que tools/ops.js: dependencia npm,
|
|
47
|
+
// Mismo orden que tools/ops.js: dependencia npm y, por último, el repositorio del toolkit.
|
|
48
48
|
const candidates = [
|
|
49
49
|
path.join(root, 'node_modules', '@ingeniomaps', 'cauce', 'engine', 'hooks', 'run.js'),
|
|
50
|
-
path.join(root, '.ops', 'engine', 'hooks', 'run.js'),
|
|
51
50
|
path.join(root, 'engine', 'hooks', 'run.js'),
|
|
52
51
|
]
|
|
53
52
|
// El bridge corre antes de poder cargar el motor, así que repite la cascada de
|
|
@@ -17,8 +17,7 @@ Antes de cerrar, corré `node {{OPS_DIR}}tools/ops.js check {{OPS_DIR}}planning`
|
|
|
17
17
|
|
|
18
18
|
## Los cargos
|
|
19
19
|
|
|
20
|
-
El catálogo está en `{{OPS_DIR}}node_modules/@ingeniomaps/cauce/agents/`
|
|
21
|
-
`{{OPS_DIR}}.ops/agents/` si este repo no usa npm— y los propios de la empresa en
|
|
20
|
+
El catálogo está en `{{OPS_DIR}}node_modules/@ingeniomaps/cauce/agents/` y los propios de la empresa en
|
|
22
21
|
`{{OPS_DIR}}agents/`, que mandan sobre los del sistema con el mismo nombre.
|
|
23
22
|
|
|
24
23
|
Cada cargo tiene un `SKILL.md` con su contrato: cuándo actúa, qué decide, qué no le corresponde y cuál es
|
|
@@ -29,7 +29,7 @@ function runnerManifest(root, name) {
|
|
|
29
29
|
}
|
|
30
30
|
const packaged = packagedAutomation(root)
|
|
31
31
|
if (!packaged) {
|
|
32
|
-
throw new Error('no encuentro automatization/:
|
|
32
|
+
throw new Error('no encuentro automatization/: corré "npm install" en la raíz del repo ops')
|
|
33
33
|
}
|
|
34
34
|
const file = path.join(packaged, 'runners', name, 'manifest.json')
|
|
35
35
|
try { return JSON.parse(fs.readFileSync(file, 'utf8')) } catch (error) {
|
|
@@ -261,14 +261,14 @@ function check(root) {
|
|
|
261
261
|
}
|
|
262
262
|
// El motor puede venir de la dependencia npm, de la copia local o del propio repositorio.
|
|
263
263
|
if (!O.engineAt(root, path.join('hooks', 'run.js'))) {
|
|
264
|
-
errors.push('falta engine/hooks/run.js:
|
|
264
|
+
errors.push('falta engine/hooks/run.js: corré "npm install" en la raíz del repo ops')
|
|
265
265
|
}
|
|
266
266
|
const workflows = ['autobuild.js', 'team.js', path.join('integrations', 'sync.js')]
|
|
267
267
|
workflows.push(path.join('integrations', 'promote.js'))
|
|
268
268
|
const packaged = packagedAutomation(root)
|
|
269
269
|
for (const name of workflows) {
|
|
270
270
|
if (!packaged || !fs.existsSync(path.join(packaged, 'workflows', name))) {
|
|
271
|
-
errors.push(`falta automatization/workflows/${name}:
|
|
271
|
+
errors.push(`falta automatization/workflows/${name}: corré "npm install" en la raíz del repo ops`)
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
for (const { file, edited } of staleHooks(root)) {
|
package/engine/cli/ops.js
CHANGED
|
@@ -26,7 +26,7 @@ function fail(message, code = 1) {
|
|
|
26
26
|
|
|
27
27
|
function usage() {
|
|
28
28
|
console.log(`Uso:
|
|
29
|
-
ops init <destino> [--name <nombre>] [--mode embedded|sidecar] [--
|
|
29
|
+
ops init <destino> [--name <nombre>] [--mode embedded|sidecar] [--force]
|
|
30
30
|
ops check <planning-dir> [--json]
|
|
31
31
|
ops tree <planning-dir> [--no-color] [--json]
|
|
32
32
|
ops context <planning-dir> [--json]
|
|
@@ -56,7 +56,7 @@ function usage() {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// Banderas que consumen el argumento siguiente: su valor no es un posicional.
|
|
59
|
-
const VALUED_FLAGS = new Set(['--name', '--mode', '--
|
|
59
|
+
const VALUED_FLAGS = new Set(['--name', '--mode', '--fixture'])
|
|
60
60
|
|
|
61
61
|
// Los posicionales del comando, salteando banderas y sus valores. Leer `process.argv` crudo hacía
|
|
62
62
|
// que `agents list --json` tomara `--json` como la raíz: el catálogo salía vacío, sin error, y quien
|
|
@@ -157,13 +157,9 @@ function init(target) {
|
|
|
157
157
|
'{{PLANNING_DIR}}': 'planning',
|
|
158
158
|
'{{WORKSPACE_PATH}}': mode === 'embedded' ? '.' : '..',
|
|
159
159
|
}, process.argv.includes('--force'), providerNames())
|
|
160
|
-
// `ci.yml` valida el toolkit con `npm run ci
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
'{{MODE}}': mode,
|
|
164
|
-
'{{PLANNING_DIR}}': 'planning',
|
|
165
|
-
'{{WORKSPACE_PATH}}': mode === 'embedded' ? '.' : '..',
|
|
166
|
-
}, process.argv.includes('--force'), ['ci.yml', 'agent-learning.yml'])
|
|
160
|
+
// No se copia `.github/`: `ci.yml` valida el toolkit con `npm run ci` —que una instancia no tiene— y
|
|
161
|
+
// el ciclo de aprendizaje dejó de distribuirse en 0.4.0. Copiar salteando los dos únicos archivos
|
|
162
|
+
// que existen dejaba `.github/workflows/` vacío en cada instancia.
|
|
167
163
|
const preserve = process.argv.includes('--force')
|
|
168
164
|
copyRuntime(
|
|
169
165
|
path.join(PROJECT_ROOT, 'automatization', 'hooks'),
|
|
@@ -172,29 +168,13 @@ function init(target) {
|
|
|
172
168
|
root,
|
|
173
169
|
)
|
|
174
170
|
const version = require(path.join(PROJECT_ROOT, 'package.json')).version
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const engine = path.join(root, '.ops', 'engine')
|
|
183
|
-
copyRuntime(path.join(PROJECT_ROOT, 'engine'), engine, preserve, root)
|
|
184
|
-
fs.chmodSync(path.join(engine, 'cli', 'ops.js'), 0o755)
|
|
185
|
-
// Sin npm no hay de dónde leer el catálogo en tiempo de ejecución: viaja junto al motor, igual
|
|
186
|
-
// que los adaptadores de runner y los workflows que instala `automation install`.
|
|
187
|
-
copyRuntime(path.join(PROJECT_ROOT, 'agents'), path.join(root, '.ops', 'agents'), preserve, root)
|
|
188
|
-
copyRuntime(path.join(PROJECT_ROOT, 'teams'), path.join(root, '.ops', 'teams'), preserve, root)
|
|
189
|
-
for (const name of ['runners', 'workflows']) {
|
|
190
|
-
copyRuntime(
|
|
191
|
-
path.join(PROJECT_ROOT, 'automatization', name),
|
|
192
|
-
path.join(root, '.ops', 'automatization', name),
|
|
193
|
-
preserve,
|
|
194
|
-
root,
|
|
195
|
-
)
|
|
196
|
-
}
|
|
197
|
-
}
|
|
171
|
+
// El motor siempre llega como dependencia. La alternativa era vendorizarlo en `.ops/`, y no valía:
|
|
172
|
+
// Node hace falta igual en los dos casos —el motor, los guards y los workflows son JavaScript—, así
|
|
173
|
+
// que la copia sólo ahorraba este `package.json` de seis líneas a cambio de 5 MB en la historia de
|
|
174
|
+
// la empresa y de no tener cómo enterarse de que salió una versión nueva.
|
|
175
|
+
//
|
|
176
|
+
// El repo ops es un sidecar: declarar npm acá no convierte en Node al servicio de Go de al lado.
|
|
177
|
+
declareEngine(path.join(root, 'package.json'), version)
|
|
198
178
|
let entregado = {}
|
|
199
179
|
for (const relative of O.trackedPaths()) {
|
|
200
180
|
const dir = path.join(root, relative)
|
|
@@ -205,13 +185,10 @@ function init(target) {
|
|
|
205
185
|
const configFile = path.join(root, 'ops.config.json')
|
|
206
186
|
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'))
|
|
207
187
|
config.cauceVersion = version
|
|
208
|
-
|
|
209
|
-
config.$schema = engineMode === 'dependency'
|
|
210
|
-
? 'node_modules/@ingeniomaps/cauce/engine/schemas/ops-config.schema.json'
|
|
211
|
-
: '.ops/engine/schemas/ops-config.schema.json'
|
|
188
|
+
config.$schema = 'node_modules/@ingeniomaps/cauce/engine/schemas/ops-config.schema.json'
|
|
212
189
|
F.atomicWriteJson(configFile, config)
|
|
213
190
|
console.log(`\n✓ ${name}: sistema ops creado en ${root}`)
|
|
214
|
-
|
|
191
|
+
console.log(' siguiente: npm install (el motor viene de la dependencia)')
|
|
215
192
|
console.log(` siguiente: node ${path.join(root, 'tools', 'ops.js')} check ${path.join(root, 'planning')}`)
|
|
216
193
|
}
|
|
217
194
|
|
|
@@ -569,9 +546,6 @@ function upgrade(dir) {
|
|
|
569
546
|
const origin = path.join(PROJECT_ROOT, O.sourceOf(relative))
|
|
570
547
|
if (!fs.existsSync(origin)) continue
|
|
571
548
|
const target = path.join(root, relative)
|
|
572
|
-
// `.ops/` es el paquete vendorizado de una instancia sin npm. Si no existe, esta instancia lo
|
|
573
|
-
// toma de la dependencia y crearlo sería duplicar lo que el lockfile ya versiona.
|
|
574
|
-
if (relative.startsWith('.ops/') && !fs.existsSync(target)) continue
|
|
575
549
|
if (fs.statSync(origin).isDirectory()) overlayTree(origin, target, root)
|
|
576
550
|
else {
|
|
577
551
|
F.assertNoSymlinkPath(root, target)
|
|
@@ -616,6 +590,12 @@ function upgrade(dir) {
|
|
|
616
590
|
console.log(`= conservado ${override.collection}/${override.project}: sobrescribe ${override.system}`)
|
|
617
591
|
}
|
|
618
592
|
console.log(' planning, organization y todo lo propio quedaron intactos')
|
|
593
|
+
// No se borra: sin la dependencia declarada, quitarle `.ops/` la dejaría sin motor. Se avisa y
|
|
594
|
+
// decide una persona.
|
|
595
|
+
if (fs.existsSync(path.join(root, '.ops', 'engine'))) {
|
|
596
|
+
console.log('\n⚠ esta instancia tiene el motor vendorizado en .ops/, que Cauce ya no distribuye.')
|
|
597
|
+
console.log(' Corré "npm install" para tenerlo como dependencia y después borrá .ops/ a mano.')
|
|
598
|
+
}
|
|
619
599
|
// El wiring del runner no se actualiza solo: vive fuera de la instancia y lo escribe otro comando.
|
|
620
600
|
// Sin este recordatorio, una mejora en un workflow o en el catálogo se queda en el paquete.
|
|
621
601
|
const runners = Object.keys(M.readRunners(root))
|
package/engine/core/ownership.js
CHANGED
|
@@ -51,11 +51,6 @@ const SYSTEM_COLLECTIONS = [
|
|
|
51
51
|
// Se reemplazan enteras. Mientras no tengan su propio `system/`, una edición local se detecta y
|
|
52
52
|
// se reporta antes de pisarla, nunca después.
|
|
53
53
|
const RUNTIME_PATHS = [
|
|
54
|
-
'.ops/engine',
|
|
55
|
-
'.ops/agents',
|
|
56
|
-
'.ops/teams',
|
|
57
|
-
'.ops/automatization/runners',
|
|
58
|
-
'.ops/automatization/workflows',
|
|
59
54
|
'automatization/hooks',
|
|
60
55
|
]
|
|
61
56
|
|
|
@@ -76,8 +71,6 @@ const TEMPLATE_FILES = new Set(['AGENTS.md', 'Makefile'])
|
|
|
76
71
|
|
|
77
72
|
function sourceOf(relative) {
|
|
78
73
|
if (TEMPLATE_FILES.has(relative)) return path.join('template', relative)
|
|
79
|
-
// `.ops/` es el paquete vendorizado: cada ruta de ahí adentro se llama igual en el origen.
|
|
80
|
-
if (relative.startsWith('.ops/')) return relative.slice('.ops/'.length)
|
|
81
74
|
// El runtime sale de la raíz del paquete aunque su prefijo sea de plantilla: `automatization/`
|
|
82
75
|
// le entrega documentos a la instancia, pero los guards que ejecuta son los del toolkit.
|
|
83
76
|
if (RUNTIME_PATHS.includes(relative)) return relative
|
|
@@ -87,13 +80,16 @@ function sourceOf(relative) {
|
|
|
87
80
|
return relative
|
|
88
81
|
}
|
|
89
82
|
|
|
90
|
-
// Dónde puede estar el motor
|
|
91
|
-
//
|
|
92
|
-
//
|
|
83
|
+
// Dónde puede estar el motor. Lo mismo que resuelven `tools/ops.js`, el wrapper de hooks y el bridge
|
|
84
|
+
// de Antigravity: declararlo una vez evita que un consumidor quede afuera.
|
|
85
|
+
//
|
|
86
|
+
// Dos caminos, no tres: el motor llega por npm, y la tercera entrada es el propio repositorio del
|
|
87
|
+
// toolkit corriendo sobre sí mismo. La copia vendorizada en `.ops/` se retiró en 0.10.0 — ahorraba un
|
|
88
|
+
// `package.json` a cambio de 5 MB en la historia de la empresa y de no poder enterarse de una versión
|
|
89
|
+
// nueva, y Node hace falta igual en los dos casos.
|
|
93
90
|
function engineCandidates(root) {
|
|
94
91
|
return [
|
|
95
92
|
path.join(root, 'node_modules', '@ingeniomaps', 'cauce', 'engine'),
|
|
96
|
-
path.join(root, '.ops', 'engine'),
|
|
97
93
|
path.join(root, 'engine'),
|
|
98
94
|
]
|
|
99
95
|
}
|
|
@@ -109,7 +105,6 @@ function engineAt(root, relative = '') {
|
|
|
109
105
|
function packagePath(root, relative) {
|
|
110
106
|
const candidates = [
|
|
111
107
|
path.join(root, 'node_modules', '@ingeniomaps', 'cauce', relative),
|
|
112
|
-
path.join(root, '.ops', relative),
|
|
113
108
|
path.join(root, relative),
|
|
114
109
|
]
|
|
115
110
|
return candidates.find((candidate) => fs.existsSync(candidate)) || ''
|
|
@@ -121,7 +116,6 @@ function packagePath(root, relative) {
|
|
|
121
116
|
function packageDir(root, name) {
|
|
122
117
|
const candidates = [
|
|
123
118
|
path.join(root, 'node_modules', '@ingeniomaps', 'cauce', name),
|
|
124
|
-
path.join(root, '.ops', name),
|
|
125
119
|
path.join(root, name),
|
|
126
120
|
]
|
|
127
121
|
return candidates.find((dir) => fs.existsSync(path.join(dir, 'system'))
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ implementa y conecta ese proceso con runners concretos: no redefine producto, pr
|
|
|
8
8
|
|
|
9
9
|
| Ruta | Responsabilidad |
|
|
10
10
|
|---|---|
|
|
11
|
-
| `hooks/` | Entradas estables; la lógica vive en
|
|
11
|
+
| `hooks/` | Entradas estables; la lógica vive en el motor, que llega con la dependencia. |
|
|
12
12
|
| `workflows/` | Recorridos portables; las fases pertenecen a `planning/PROTOCOL.md`. |
|
|
13
13
|
| `runners/` | Adaptadores que declaran su contrato en `manifest.json`. |
|
|
14
14
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# integrations/ — reglas para sistemas externos
|
|
2
2
|
|
|
3
3
|
`config.json` registra proveedores y cada subcarpeta contiene configuración no secreta, staging y
|
|
4
|
-
propuestas. La conexión y normalización viven en
|
|
4
|
+
propuestas. La conexión y normalización viven en el motor; no implementes clientes de API
|
|
5
5
|
dentro de esta carpeta.
|
|
6
6
|
|
|
7
7
|
Toda integración se considera de solo lectura. `writeback-plan` solo calcula intención y nunca autoriza ni
|
package/template/ops.config.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# OPS-002 — Distribuir un runtime autocontenido y neutral al runner
|
|
2
2
|
|
|
3
3
|
**Estado:** Aceptado
|
|
4
|
-
**Fecha:** 2026-08-14
|
|
4
|
+
**Fecha:** 2026-08-14 · **Revisado:** 2026-08-15
|
|
5
5
|
|
|
6
6
|
> Decide cómo se instala y ejecuta Cauce; no elige qué runner debe usar cada proyecto.
|
|
7
7
|
|
|
@@ -12,10 +12,16 @@ herramienta o de rutas hacia este repositorio central, una actualización o sesi
|
|
|
12
12
|
|
|
13
13
|
## Decisión
|
|
14
14
|
|
|
15
|
-
**
|
|
15
|
+
**El motor llega como dependencia versionada y el lockfile fija cuál corre.** El protocolo y los guards son
|
|
16
16
|
neutrales; cada runner se conecta mediante un adaptador declarado. La instalación preserva archivos existentes,
|
|
17
17
|
no activa runners silenciosamente y rechaza destinos atravesados por symlinks inseguros.
|
|
18
18
|
|
|
19
|
+
Hasta el 2026-08-15 cada instancia recibía además una copia del motor en `.ops/engine/`, para no exigirle npm
|
|
20
|
+
a un repo de Go, Python o Rust. Se retiró: el repo ops es un **sidecar**, hermano de los repos de producto, así
|
|
21
|
+
que declarar npm ahí no le impone un stack a ninguno. Y Node hace falta igual —el motor, los guards y los
|
|
22
|
+
workflows son JavaScript—, de modo que la copia sólo ahorraba un `package.json` a cambio de 5 MB en la historia
|
|
23
|
+
de la empresa y de no tener cómo enterarse de que salió una versión nueva.
|
|
24
|
+
|
|
19
25
|
## Alternativas consideradas
|
|
20
26
|
|
|
21
27
|
- **Depender del toolkit central:** impide operar de forma aislada y hace frágiles las rutas.
|
|
@@ -26,8 +32,8 @@ no activa runners silenciosamente y rechaza destinos atravesados por symlinks in
|
|
|
26
32
|
|
|
27
33
|
**Ganamos:** portabilidad, comportamiento uniforme y actualizaciones revisables.
|
|
28
34
|
|
|
29
|
-
**Costos que aceptamos:**
|
|
30
|
-
|
|
35
|
+
**Costos que aceptamos:** el repo ops necesita Node y un `package.json`. Una sesión sin acceso a npm puede
|
|
36
|
+
seguir operando con lo instalado, pero no puede actualizarse.
|
|
31
37
|
|
|
32
38
|
## Estado de implementación
|
|
33
39
|
|
package/template/tools/ops.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
4
|
// Punto de entrada estable del proyecto: nadie —ni una persona ni un agente— necesita saber de
|
|
5
|
-
// dónde sale el motor.
|
|
6
|
-
// repositorio no usa npm (Go, Python, Rust), se usa la copia de `.ops/engine`.
|
|
5
|
+
// dónde sale el motor. Viene de la dependencia npm, que el lockfile versiona.
|
|
7
6
|
|
|
8
7
|
const path = require('path')
|
|
9
8
|
|
|
@@ -16,7 +15,6 @@ process.env.OPS_ROOT = process.env.OPS_ROOT || root
|
|
|
16
15
|
|
|
17
16
|
const candidates = [
|
|
18
17
|
() => require.resolve('@ingeniomaps/cauce/engine/cli/ops.js', { paths: [root] }),
|
|
19
|
-
() => require.resolve(path.join(root, '.ops', 'engine', 'cli', 'ops.js')),
|
|
20
18
|
]
|
|
21
19
|
|
|
22
20
|
let engine = ''
|
|
@@ -26,7 +24,7 @@ for (const candidate of candidates) {
|
|
|
26
24
|
|
|
27
25
|
if (!engine) {
|
|
28
26
|
console.error('No se encontró el motor de Cauce.')
|
|
29
|
-
console.error(' Instalá la dependencia con "npm install"
|
|
27
|
+
console.error(' Instalá la dependencia con "npm install" desde la raíz del repo ops.')
|
|
30
28
|
process.exit(2)
|
|
31
29
|
}
|
|
32
30
|
|