@ingeniomaps/cauce 0.55.0 → 0.56.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 CHANGED
@@ -14,6 +14,24 @@ 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.56.0] - 2026-09-03
18
+
19
+ ### Corregido
20
+
21
+ - **El upgrade que documentaba el README rompía el pin exacto que puso `init`.** La razón para no
22
+ saltear su primer paso es que «`init` fija la versión exacta, así que `npm update` no la mueve», y
23
+ el comando de al lado la desarmaba: npm guarda con caret por defecto, así que
24
+ `npm install --save-dev @ingeniomaps/cauce@latest` dejaba `^0.55.0`, y dentro de `0.x` ese caret
25
+ alcanza a los patches —que Cauce publica—. Un `npm install` en otra máquina podía dejar el motor en
26
+ una versión que `ops.config.json` no decía.
27
+
28
+ Se arregla por los dos lados. El comando lleva `--save-exact` en los tres lugares que lo dictan —el
29
+ README, el `make upgrade` de tu instancia y la salida de `upgrade --check`—, y además **`upgrade`
30
+ repone la versión exacta al terminar**, así que una instancia que ya quedó con un rango se repara
31
+ sola en la próxima actualización. **Qué cambia para vos**: `upgrade` puede tocar ahora una línea de
32
+ tu `package.json`, y lo dice cuando lo hace. El resto del manifiesto queda como estaba, y una
33
+ instancia sin `package.json` no recibe uno.
34
+
17
35
  ## [0.55.0] - 2026-09-02
18
36
 
19
37
  ### Corregido
package/README.md CHANGED
@@ -266,13 +266,15 @@ existente detiene el `upgrade` antes de pisarlo.
266
266
  Son tres pasos y `make upgrade` hace los dos primeros:
267
267
 
268
268
  ```bash
269
- npm install --save-dev @ingeniomaps/cauce@latest # trae el motor nuevo
269
+ npm install --save-exact --save-dev @ingeniomaps/cauce@latest # trae el motor nuevo
270
270
  node tools/ops.js upgrade . # aplica system/ y el runtime
271
271
  node tools/ops.js automation install . claude # el wiring del runner
272
272
  ```
273
273
 
274
274
  El primero no se puede saltear: `init` fija la versión exacta, así que `npm update` no la mueve y
275
- `upgrade` compara contra el motor instalado —lo dice en su salida—. El tercero tampoco: los workflows y
275
+ `upgrade` compara contra el motor instalado —lo dice en su salida—. Va con `--save-exact` porque npm
276
+ guarda con caret por defecto, y ese caret es justamente lo que volvería falsa la frase anterior; si
277
+ alguna vez se instaló sin él, `upgrade` repone la versión exacta al terminar. El tercero tampoco: los workflows y
276
278
  las skills viven en el runner, no en la instancia, y `upgrade` no los toca. `upgrade` lo recuerda al
277
279
  terminar.
278
280
 
@@ -0,0 +1,73 @@
1
+ 'use strict'
2
+
3
+ // Cómo una instancia declara el motor en su `package.json`: ponerlo, reponerlo y sacarlo. Vive aparte
4
+ // del ciclo de vida porque su reloj es otro —npm y el versionado, no crear/actualizar/borrar— y porque
5
+ // sus tres consumidores no se solapan: `scaffold` declara, `upgrade` repone y `destroy` saca.
6
+
7
+ const fs = require('node:fs')
8
+ const path = require('node:path')
9
+ const F = require('../core/files')
10
+ const { fail } = require('./io')
11
+
12
+ // Declara el motor como dependencia exacta: el lockfile decide qué versión corre, no una copia.
13
+ // Conserva el manifiesto existente porque el repo anfitrión puede tener el suyo.
14
+ function declareEngine(manifest, version) {
15
+ let pkg = { name: path.basename(path.dirname(manifest)), private: true, version: '0.0.0' }
16
+ if (fs.existsSync(manifest)) {
17
+ try { pkg = JSON.parse(fs.readFileSync(manifest, 'utf8')) } catch (error) {
18
+ fail(`package.json inválido en ${manifest}: ${error.message}`)
19
+ }
20
+ }
21
+ pkg.devDependencies = { ...pkg.devDependencies, '@ingeniomaps/cauce': version }
22
+ F.atomicWriteJson(manifest, pkg)
23
+ }
24
+
25
+ // Repone la versión exacta que `declareEngine` había dejado, y devuelve la que había si cambió algo.
26
+ //
27
+ // El pin no sobrevive por sí solo: npm guarda con caret, así que el `npm install @latest` del primer
28
+ // paso del upgrade lo convierte en un rango, y con eso deja de valer la razón que el README da para no
29
+ // saltear ese paso —«init fija la versión exacta, así que npm update no la mueve»—. Repararlo acá y no
30
+ // sólo documentar el comando es lo que vuelve la propiedad independiente de cómo se haya instalado.
31
+ //
32
+ // Sin manifiesto no hay nada que reponer: una instancia que no lo tiene resuelve el motor de otra
33
+ // forma, y crearle uno sería otra operación que nadie pidió en un upgrade.
34
+ function pinEngine(root, version) {
35
+ const manifest = path.join(root, 'package.json')
36
+ if (!fs.existsSync(manifest)) return null
37
+ let pkg
38
+ try { pkg = JSON.parse(fs.readFileSync(manifest, 'utf8')) } catch { return 'ilegible' }
39
+ const current = (pkg.devDependencies || {})['@ingeniomaps/cauce']
40
+ if (!current || current === version) return null
41
+ declareEngine(manifest, version)
42
+ return current
43
+ }
44
+
45
+ // La inversa exacta de `declareEngine`: saca la clave que puso y nada más. El resto del manifiesto es
46
+ // del repo anfitrión aunque hoy no tenga otra cosa —un `package.json` vacío puede ser lo que alguien
47
+ // escribió para tener scripts— así que el archivo se borra sólo si es idéntico al que `declareEngine`
48
+ // habría creado desde cero, sin dependencias, sin scripts y con su `version: 0.0.0`.
49
+ //
50
+ // Lo que no se toca nunca es `node_modules/` ni el lockfile: los escribe npm, pueden tener dependencias
51
+ // del proyecto y borrarlos por nuestra cuenta destruye trabajo ajeno. Se nombran en la salida, que es
52
+ // la mitad que faltaba: `destroy` decía «tu repositorio queda donde está» y dejaba un `package.json`
53
+ // cuya única dependencia era Cauce. En un repo Rust eso es basura conspicua y nadie avisaba.
54
+ function undeclareEngine(manifest) {
55
+ if (!fs.existsSync(manifest)) return []
56
+ let pkg
57
+ try { pkg = JSON.parse(fs.readFileSync(manifest, 'utf8')) } catch { return [] }
58
+ const dev = pkg.devDependencies || {}
59
+ if (!('@ingeniomaps/cauce' in dev)) return []
60
+ delete dev['@ingeniomaps/cauce']
61
+ pkg.devDependencies = dev
62
+ const ours = !Object.keys(dev).length && !Object.keys(pkg.dependencies || {}).length
63
+ && !Object.keys(pkg.scripts || {}).length && pkg.private === true && pkg.version === '0.0.0'
64
+ if (ours) {
65
+ fs.rmSync(manifest, { force: true })
66
+ return ['package.json (lo había creado init: sin dependencias ni scripts propios)']
67
+ }
68
+ if (!Object.keys(dev).length) delete pkg.devDependencies
69
+ F.atomicWriteJson(manifest, pkg)
70
+ return ['package.json: se quitó la dependencia del motor y el resto queda como estaba']
71
+ }
72
+
73
+ module.exports = { declareEngine, pinEngine, undeclareEngine }
@@ -16,6 +16,7 @@ const P = require('../planning/parser')
16
16
  const ST = require('../planning/state')
17
17
  const A = require('../automation')
18
18
  const { fail } = require('./io')
19
+ const { declareEngine, pinEngine, undeclareEngine } = require('./dependency')
19
20
 
20
21
  const PROJECT_ROOT = path.resolve(__dirname, '..', '..')
21
22
 
@@ -86,47 +87,6 @@ function copyRuntime(source, target, preserve = false, boundary = target, skip =
86
87
  return preserved
87
88
  }
88
89
 
89
- // Declara el motor como dependencia exacta: el lockfile decide qué versión corre, no una copia.
90
- // Conserva el manifiesto existente porque el repo anfitrión puede tener el suyo.
91
- function declareEngine(manifest, version) {
92
- let pkg = { name: path.basename(path.dirname(manifest)), private: true, version: '0.0.0' }
93
- if (fs.existsSync(manifest)) {
94
- try { pkg = JSON.parse(fs.readFileSync(manifest, 'utf8')) } catch (error) {
95
- fail(`package.json inválido en ${manifest}: ${error.message}`)
96
- }
97
- }
98
- pkg.devDependencies = { ...pkg.devDependencies, '@ingeniomaps/cauce': version }
99
- F.atomicWriteJson(manifest, pkg)
100
- }
101
-
102
- // La inversa exacta de `declareEngine`: saca la clave que puso y nada más. El resto del manifiesto es
103
- // del repo anfitrión aunque hoy no tenga otra cosa —un `package.json` vacío puede ser lo que alguien
104
- // escribió para tener scripts— así que el archivo se borra sólo si es idéntico al que `declareEngine`
105
- // habría creado desde cero, sin dependencias, sin scripts y con su `version: 0.0.0`.
106
- //
107
- // Lo que no se toca nunca es `node_modules/` ni el lockfile: los escribe npm, pueden tener dependencias
108
- // del proyecto y borrarlos por nuestra cuenta destruye trabajo ajeno. Se nombran en la salida, que es
109
- // la mitad que faltaba: `destroy` decía «tu repositorio queda donde está» y dejaba un `package.json`
110
- // cuya única dependencia era Cauce. En un repo Rust eso es basura conspicua y nadie avisaba.
111
- function undeclareEngine(manifest) {
112
- if (!fs.existsSync(manifest)) return []
113
- let pkg
114
- try { pkg = JSON.parse(fs.readFileSync(manifest, 'utf8')) } catch { return [] }
115
- const dev = pkg.devDependencies || {}
116
- if (!('@ingeniomaps/cauce' in dev)) return []
117
- delete dev['@ingeniomaps/cauce']
118
- pkg.devDependencies = dev
119
- const ours = !Object.keys(dev).length && !Object.keys(pkg.dependencies || {}).length
120
- && !Object.keys(pkg.scripts || {}).length && pkg.private === true && pkg.version === '0.0.0'
121
- if (ours) {
122
- fs.rmSync(manifest, { force: true })
123
- return ['package.json (lo había creado init: sin dependencias ni scripts propios)']
124
- }
125
- if (!Object.keys(dev).length) delete pkg.devDependencies
126
- F.atomicWriteJson(manifest, pkg)
127
- return ['package.json: se quitó la dependencia del motor y el resto queda como estaba']
128
- }
129
-
130
90
  // El andamiaje de una instancia, sin leer argv. `init` es la cáscara que traduce banderas a esto, y
131
91
  // el banco de evaluación lo llama directo: crear una instancia programáticamente no puede depender de
132
92
  // cómo venga escrita la línea de comandos.
@@ -339,7 +299,10 @@ function upgrade(dir, cli) {
339
299
  // importa porque `init` fija la versión exacta, así que el motor no se mueve solo y esta línea,
340
300
  // a secas, se leía como «no hay nada nuevo» durante todas las versiones siguientes.
341
301
  console.log(`= ${to}: la instancia está al día con el motor instalado`)
342
- console.log(' para traer una versión más nueva: npm install --save-dev @ingeniomaps/cauce@latest')
302
+ // Con `--save-exact` porque npm guarda con caret por defecto, y el caret es lo que volvería
303
+ // falsa la línea de arriba: dentro de 0.x alcanza a los patches, y Cauce publica patches.
304
+ console.log(' para traer una versión más nueva:'
305
+ + ' npm install --save-exact --save-dev @ingeniomaps/cauce@latest')
343
306
  if (changed.length) process.exit(1)
344
307
  return
345
308
  }
@@ -446,12 +409,18 @@ function upgrade(dir, cli) {
446
409
  const config = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8'))
447
410
  config.cauceVersion = to
448
411
  F.atomicWriteJson(path.join(root, 'ops.config.json'), config)
412
+ // Acá es donde las tres versiones se saben a la vez, así que es donde pueden quedar diciendo lo mismo.
413
+ const pinned = pinEngine(root, to)
449
414
 
450
415
  console.log(`✓ Cauce ${from || '(previa)'} → ${to}`)
451
416
  // Descartar con --force es legítimo; hacerlo sin dejar rastro no. Queda en la salida del comando,
452
417
  // que es la evidencia que el protocolo pide para cualquier cambio.
453
418
  for (const file of changed) console.log(`− descartado tu cambio en ${file}`)
454
419
  for (const relative of retired) console.log(`− retirado ${relative}: Cauce ya no lo distribuye`)
420
+ // Se dice porque cambia un archivo que la empresa versiona, y porque explica un diff que si no
421
+ // aparecería sin autor. Ilegible se avisa y no se toca: el manifiesto es del repo anfitrión.
422
+ if (pinned === 'ilegible') console.log(' ⚠ package.json no se pudo leer: su versión quedó como estaba')
423
+ else if (pinned) console.log(` package.json: ${pinned} → ${to}, la versión exacta que acabás de aplicar`)
455
424
  printChangelog(from, to)
456
425
  console.log(` ${system.length} ruta(s) del sistema y ${O.RUNTIME_PATHS.length} del runtime actualizadas`)
457
426
  for (const override of overrides) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingeniomaps/cauce",
3
- "version": "0.55.0",
3
+ "version": "0.56.0",
4
4
  "description": "Sistema portable de planificación y ejecución verificable para cualquier proyecto",
5
5
  "keywords": [
6
6
  "planning",
package/template/Makefile CHANGED
@@ -29,7 +29,7 @@ destroy: ## Muestra qué se pierde al borrar esta instancia (borrar exige --forc
29
29
  @node tools/ops.js destroy .
30
30
 
31
31
  upgrade: ## Trae la última versión de Cauce y la aplica, sin tocar lo del proyecto
32
- @npm install --save-dev @ingeniomaps/cauce@latest
32
+ @npm install --save-exact --save-dev @ingeniomaps/cauce@latest
33
33
  @node tools/ops.js upgrade .
34
34
 
35
35
  automation-check: ## Comprueba hooks, workflows y adaptadores