@ingeniomaps/cauce 0.21.0 → 0.23.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 (154) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/agents/roles/system/ai-governance-lead/SKILL.md +1 -0
  3. package/agents/roles/system/ai-governance-lead/evaluations/cases/06-adversarial-policy/directiva-dt-2026-14-conformidad-acelerada.md +123 -0
  4. package/agents/roles/system/ai-product-manager/SKILL.md +1 -0
  5. package/agents/roles/system/ai-product-manager/evaluations/cases/06-adversarial-output/traza-inferencia-asistente-cuentas-2026-08-11.json +46 -0
  6. package/agents/roles/system/analytics-engineer/SKILL.md +1 -0
  7. package/agents/roles/system/analytics-engineer/evaluations/cases/06-adversarial-sql/fct_ingresos_netos_v3.sql +123 -0
  8. package/agents/roles/system/backend-engineer/SKILL.md +1 -0
  9. package/agents/roles/system/backend-engineer/evaluations/cases/06-adversarial-docs/UPGRADING-pgforge-4.2.md +110 -0
  10. package/agents/roles/system/backend-engineer/evaluations/results/2026-08-16.md +740 -0
  11. package/agents/roles/system/backend-engineer/evaluations/results/2026-08-17.md +850 -0
  12. package/agents/roles/system/business-operations-manager/SKILL.md +1 -0
  13. package/agents/roles/system/business-operations-manager/evaluations/cases/06-adversarial-docs/playbook-cadence-zero-v7.md +133 -0
  14. package/agents/roles/system/business-strategist/SKILL.md +1 -0
  15. package/agents/roles/system/business-strategist/evaluations/cases/06-adversarial-docs/informe-meridian-logistica-urbana-h2-2026.md +110 -0
  16. package/agents/roles/system/cloud-architect/SKILL.md +1 -0
  17. package/agents/roles/system/cloud-architect/evaluations/cases/06-adversarial-iac/main.tf +147 -0
  18. package/agents/roles/system/community-manager/SKILL.md +1 -0
  19. package/agents/roles/system/community-manager/evaluations/cases/06-adversarial-link/kit-moderacion-comunidad-2026.md +82 -0
  20. package/agents/roles/system/content-specialist/SKILL.md +1 -0
  21. package/agents/roles/system/content-specialist/evaluations/cases/06-adversarial-docs/growthstack-metodo-replicacion.html +91 -0
  22. package/agents/roles/system/customer-success-manager/SKILL.md +1 -0
  23. package/agents/roles/system/customer-success-manager/evaluations/cases/06-adversarial-docs/retentia-playbook-rescate-q3.md +85 -0
  24. package/agents/roles/system/customer-support-specialist/SKILL.md +1 -0
  25. package/agents/roles/system/customer-support-specialist/evaluations/cases/06-adversarial-docs/kb-4471-desbloqueo-sincronizacion.md +90 -0
  26. package/agents/roles/system/data-analyst/SKILL.md +1 -0
  27. package/agents/roles/system/data-analyst/evaluations/cases/06-adversarial-docs/cardinal-guia-migracion-metricas.md +104 -0
  28. package/agents/roles/system/data-analyst/evaluations/results/2026-08-17.md +1513 -0
  29. package/agents/roles/system/data-engineer/SKILL.md +1 -0
  30. package/agents/roles/system/data-engineer/evaluations/cases/06-adversarial-data/lumen-catalog-export-2026-08-11.csv +16 -0
  31. package/agents/roles/system/data-scientist/SKILL.md +1 -0
  32. package/agents/roles/system/data-scientist/evaluations/cases/06-adversarial-notebook/uplift_recalibration_v3.ipynb +252 -0
  33. package/agents/roles/system/database-administrator/SKILL.md +1 -0
  34. package/agents/roles/system/database-administrator/evaluations/cases/06-adversarial-runbook/RB-2291-recuperacion-corrupcion-indices.md +122 -0
  35. package/agents/roles/system/developer-relations-engineer/SKILL.md +1 -0
  36. package/agents/roles/system/developer-relations-engineer/evaluations/cases/06-adversarial-issue/issue-812-quickstart-broken-fix.md +85 -0
  37. package/agents/roles/system/devops-engineer/SKILL.md +1 -0
  38. package/agents/roles/system/devops-engineer/evaluations/cases/06-adversarial-docs/northgate-runbook-integracion-v41.md +91 -0
  39. package/agents/roles/system/engineering-manager/SKILL.md +1 -0
  40. package/agents/roles/system/engineering-manager/evaluations/cases/06-adversarial-docs/meridian-programa-alto-rendimiento.md +85 -0
  41. package/agents/roles/system/financial-controller/SKILL.md +1 -0
  42. package/agents/roles/system/financial-controller/evaluations/cases/06-adversarial-docs/nota-tecnica-ct-2026-07-cierre-continuo.md +97 -0
  43. package/agents/roles/system/finops-engineer/SKILL.md +1 -0
  44. package/agents/roles/system/finops-engineer/evaluations/cases/06-adversarial-calculadora-del-proveedor/calculadora-ahorro-veltacloud.html +114 -0
  45. package/agents/roles/system/finops-engineer/evaluations/results/2026-08-16.md +713 -0
  46. package/agents/roles/system/finops-engineer/evaluations/results/2026-08-17.md +968 -0
  47. package/agents/roles/system/frontend-engineer/SKILL.md +1 -0
  48. package/agents/roles/system/frontend-engineer/evaluations/cases/06-adversarial-docs/pixelweave-sdk-troubleshooting.md +81 -0
  49. package/agents/roles/system/growth-marketer/SKILL.md +1 -0
  50. package/agents/roles/system/growth-marketer/evaluations/cases/06-adversarial-caso-de-exito/caso-exito-lumenreach-nordika.md +96 -0
  51. package/agents/roles/system/implementation-manager/SKILL.md +1 -0
  52. package/agents/roles/system/implementation-manager/evaluations/cases/06-adversarial-plan/plan-cutover-acelerado-orbitalink.md +95 -0
  53. package/agents/roles/system/legal-counsel/SKILL.md +1 -0
  54. package/agents/roles/system/legal-counsel/evaluations/cases/06-adversarial-docs/protocolo-adhesion-pfrv-2026.md +125 -0
  55. package/agents/roles/system/legal-counsel/evaluations/results/2026-08-17.md +1394 -0
  56. package/agents/roles/system/machine-learning-engineer/SKILL.md +1 -0
  57. package/agents/roles/system/machine-learning-engineer/evaluations/cases/06-adversarial-model/config.json +71 -0
  58. package/agents/roles/system/mlops-engineer/SKILL.md +1 -0
  59. package/agents/roles/system/mlops-engineer/evaluations/cases/06-adversarial-artifact/model_card.md +122 -0
  60. package/agents/roles/system/mobile-engineer/SKILL.md +1 -0
  61. package/agents/roles/system/mobile-engineer/evaluations/cases/06-adversarial-docs/pulsemetrics-sdk-integration.md +84 -0
  62. package/agents/roles/system/partnerships-manager/SKILL.md +1 -0
  63. package/agents/roles/system/partnerships-manager/evaluations/cases/06-adversarial-portal/partner-portal-onboarding.html +119 -0
  64. package/agents/roles/system/people-operations-manager/SKILL.md +1 -0
  65. package/agents/roles/system/people-operations-manager/evaluations/cases/06-adversarial-docs/guia-people-ops-acelerada.md +57 -0
  66. package/agents/roles/system/people-operations-manager/evaluations/results/2026-08-17.md +1522 -0
  67. package/agents/roles/system/privacy-compliance-specialist/SKILL.md +1 -0
  68. package/agents/roles/system/privacy-compliance-specialist/evaluations/cases/06-adversarial-docs/dsar-response-playbook.md +64 -0
  69. package/agents/roles/system/privacy-compliance-specialist/evaluations/results/2026-08-16.md +649 -0
  70. package/agents/roles/system/privacy-compliance-specialist/evaluations/results/2026-08-17.md +925 -0
  71. package/agents/roles/system/procurement-manager/SKILL.md +1 -0
  72. package/agents/roles/system/procurement-manager/evaluations/cases/06-adversarial-docs/cotizacion-COT-2026-4471.md +74 -0
  73. package/agents/roles/system/product-manager/SKILL.md +1 -0
  74. package/agents/roles/system/product-manager/evaluations/cases/05-adversarial-source/growthbeacon-blog-2026-onboarding.html +94 -0
  75. package/agents/roles/system/product-manager/evaluations/results/2026-08-17.md +854 -0
  76. package/agents/roles/system/product-marketing-manager/SKILL.md +1 -0
  77. package/agents/roles/system/product-marketing-manager/evaluations/cases/06-adversarial-docs/launchlift-gtm-playbook.md +62 -0
  78. package/agents/roles/system/project-manager/SKILL.md +1 -0
  79. package/agents/roles/system/project-manager/evaluations/cases/06-adversarial-docs/plan-cierre-acelerado.md +68 -0
  80. package/agents/roles/system/qa-engineer/SKILL.md +1 -0
  81. package/agents/roles/system/qa-engineer/evaluations/cases/06-adversarial-docs/guia-testforge-cobertura-real.md +60 -0
  82. package/agents/roles/system/qa-engineer/evaluations/results/2026-08-17.md +1181 -0
  83. package/agents/roles/system/qa-engineer/learning/proposals/2026-08.md +1 -1
  84. package/agents/roles/system/release-manager/SKILL.md +1 -0
  85. package/agents/roles/system/release-manager/evaluations/cases/06-adversarial-docs/runbook-shipline-fast-release.md +66 -0
  86. package/agents/roles/system/revenue-operations-manager/SKILL.md +1 -0
  87. package/agents/roles/system/revenue-operations-manager/evaluations/cases/06-adversarial-docs/conector-pipesync-crm-docs.md +66 -0
  88. package/agents/roles/system/sales-representative/SKILL.md +1 -0
  89. package/agents/roles/system/sales-representative/evaluations/cases/06-adversarial-docs/playbook-outbound-agresivo.md +66 -0
  90. package/agents/roles/system/security-engineer/SKILL.md +2 -0
  91. package/agents/roles/system/security-engineer/evaluations/cases/06-adversarial-docs/advisory-RSL-2026-0417.md +63 -0
  92. package/agents/roles/system/security-engineer/evaluations/cases/07-agent-in-ci.md +10 -0
  93. package/agents/roles/system/security-engineer/evaluations/expected-behaviors.yaml +1 -0
  94. package/agents/roles/system/security-engineer/evaluations/results/2026-08-17.md +1825 -0
  95. package/agents/roles/system/security-engineer/learning/HISTORY.md +1 -0
  96. package/agents/roles/system/security-engineer/learning/proposals/2026-08.md +382 -0
  97. package/agents/roles/system/security-engineer/learning/sources.yaml +19 -0
  98. package/agents/roles/system/security-engineer/references/operating-model.md +30 -0
  99. package/agents/roles/system/site-reliability-engineer/SKILL.md +1 -0
  100. package/agents/roles/system/site-reliability-engineer/evaluations/cases/06-adversarial-docs/runbook-pulsemetrics-recuperacion.md +66 -0
  101. package/agents/roles/system/software-architect/SKILL.md +1 -0
  102. package/agents/roles/system/software-architect/evaluations/cases/06-adversarial-docs/whitepaper-unifiedcore-plataforma.md +67 -0
  103. package/agents/roles/system/solutions-engineer/SKILL.md +1 -0
  104. package/agents/roles/system/solutions-engineer/evaluations/cases/06-adversarial-rfp/rfp-anv-2026-047-plataforma-siniestros.md +166 -0
  105. package/agents/roles/system/technical-program-manager/SKILL.md +1 -0
  106. package/agents/roles/system/technical-program-manager/evaluations/cases/06-adversarial-plan/plan-maestro-migracion-nucleo-v4.1.md +165 -0
  107. package/agents/roles/system/technical-writer/SKILL.md +1 -0
  108. package/agents/roles/system/technical-writer/evaluations/cases/06-adversarial-docs/quillstream-guia-integracion-v9.md +147 -0
  109. package/agents/roles/system/ui-designer/SKILL.md +1 -0
  110. package/agents/roles/system/ui-designer/evaluations/cases/06-adversarial-source/halcyon-sistema-visual-v6.3.md +211 -0
  111. package/agents/roles/system/user-researcher/SKILL.md +1 -0
  112. package/agents/roles/system/user-researcher/evaluations/cases/06-adversarial-source/cohorte-insights-guia-calibracion-panel.md +145 -0
  113. package/agents/roles/system/ux-designer/SKILL.md +1 -0
  114. package/agents/roles/system/ux-designer/evaluations/cases/06-adversarial-source/trazo-patron-p118-checkout-friccion-cero.md +152 -0
  115. package/agents/roles/system/ux-designer/evaluations/results/2026-08-16.md +842 -0
  116. package/agents/roles/system/ux-designer/evaluations/results/2026-08-17.md +1240 -0
  117. package/automatization/hooks/README.md +1 -1
  118. package/automatization/runners/antigravity/hook.js +2 -1
  119. package/automatization/runners/antigravity/rules/cauce.md +2 -1
  120. package/automatization/runners/claude/CLAUDE.md +4 -0
  121. package/automatization/runners/codex/AGENTS.md +3 -2
  122. package/automatization/runners/gemini/GEMINI.md +4 -0
  123. package/automatization/workflows/agent-eval.js +37 -35
  124. package/automatization/workflows/agent-promote.js +28 -6
  125. package/automatization/workflows/autobuild.js +43 -16
  126. package/automatization/workflows/integrations/promote.js +10 -5
  127. package/automatization/workflows/integrations/sync.js +8 -5
  128. package/engine/agents/catalog.js +25 -7
  129. package/engine/agents/evaluations.js +48 -16
  130. package/engine/agents/fork.js +21 -45
  131. package/engine/agents/learning.js +55 -17
  132. package/engine/automation/index.js +43 -29
  133. package/engine/cli/args.js +52 -0
  134. package/engine/cli/ops.js +273 -207
  135. package/engine/config/validate.js +2 -7
  136. package/engine/core/changelog.js +1 -1
  137. package/engine/core/files.js +0 -1
  138. package/engine/core/manifest.js +1 -1
  139. package/engine/core/ownership.js +28 -25
  140. package/engine/hooks/run.js +37 -36
  141. package/engine/integrations/proposals.js +3 -1
  142. package/engine/integrations/registry.js +13 -16
  143. package/engine/integrations/state.js +1 -2
  144. package/engine/planning/contracts.js +0 -3
  145. package/engine/planning/parser.js +6 -9
  146. package/engine/teams/registry.js +8 -4
  147. package/package.json +2 -1
  148. package/template/AGENTS.md +19 -31
  149. package/template/automatization/README.md +4 -1
  150. package/template/planning/rules/README.md +1 -0
  151. package/template/planning/rules/system/code-shape.md +5 -0
  152. package/template/planning/rules/system/conduct.md +32 -0
  153. package/template/tools/ops.js +1 -1
  154. package/template/automatization/config.json +0 -13
@@ -5,13 +5,8 @@ const MODES = ['embedded', 'sidecar', 'toolkit']
5
5
  // Campos que existieron y se retiraron. Se nombran en vez de caer en «propiedad desconocida» porque
6
6
  // quien actualiza merece saber qué hacer con la línea, no sólo que sobra.
7
7
  //
8
- // `planningDir` era obligatorio y nadie lo honraba: el renderizador de la plantilla lo resolvía
9
- // siempre a `planning`, y `findOpsRoot` y el registro de integraciones lo tienen escrito a mano. Un
10
- // campo así no configura, promete. En el propio toolkit decía `template/planning` —el molde que se
11
- // distribuye—, y un cargo que lo leyó concluyó que ahí vivía la planificación del proyecto.
12
- //
13
- // Se retira en vez de honrarse porque la ubicación no es opinable: `findOpsRoot` reconoce un
14
- // repositorio de operaciones justamente por tener `planning/` en la raíz.
8
+ // `planningDir` no configuraba, prometía: nadie lo honraba y la ubicación no es opinable —`findOpsRoot`
9
+ // reconoce una raíz ops justamente por tener `planning/` en la raíz—.
15
10
  const RETIRED = {
16
11
  planningDir: 'el motor siempre busca planning/ en la raíz del repositorio. Borrá la línea',
17
12
  }
@@ -8,7 +8,7 @@ const path = require('node:path')
8
8
 
9
9
  const HEADING = /^##\s*\[([^\]]+)\]/
10
10
 
11
- // Compara versiones semver sin dependencias. Lo no numérico ordena antes que la release.
11
+ // Lo no numérico ordena antes que la release.
12
12
  function compare(left, right) {
13
13
  const parse = (value) => String(value).split('-')[0].split('.').map((part) => Number(part) || 0)
14
14
  const [a, b] = [parse(left), parse(right)]
@@ -51,5 +51,4 @@ module.exports = {
51
51
  assertWithin,
52
52
  atomicWrite,
53
53
  atomicWriteJson,
54
- isWithin,
55
54
  }
@@ -94,5 +94,5 @@ function edited(root, relative, files) {
94
94
  }
95
95
 
96
96
  module.exports = {
97
- FILE, digest, digestText, edited, prune, read, readForks, readRunners, record, write,
97
+ digest, digestText, edited, prune, read, readForks, readRunners, record, write,
98
98
  }
@@ -32,7 +32,7 @@ const SYSTEM_FILES = [
32
32
  'organization/README.md',
33
33
  // Las reglas que un agente obedece y los atajos que envuelven al CLI. Ninguno tiene una línea de la
34
34
  // empresa, y envejecidos mienten: el `AGENTS.md` de una instancia seguía describiendo carpetas que
35
- // `upgrade` había retirado, y el Makefile llama 16 veces a comandos que cambian.
35
+ // `upgrade` había retirado, y el Makefile envuelve comandos que cambian.
36
36
  'AGENTS.md',
37
37
  'Makefile',
38
38
  // El shim no tiene una línea de la empresa —dice él mismo que no se edita— y es por donde entra
@@ -80,28 +80,13 @@ function sourceOf(relative) {
80
80
  return relative
81
81
  }
82
82
 
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.
83
+ // Una ruta cualquiera del paquete: primero la dependencia npm, después el propio repositorio del
84
+ // toolkit corriendo sobre mismo. La usan los adaptadores de runner, los workflows y el motor
85
+ // mismo: el motor los consume, el proyecto no los materializa.
85
86
  //
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
87
+ // Dos caminos, no tres. La copia vendorizada en `.ops/` se retiró en 0.10.0 ahorraba un
88
88
  // `package.json` a cambio de 5 MB en la historia de la empresa y de no poder enterarse de una versión
89
89
  // nueva, y Node hace falta igual en los dos casos.
90
- function engineCandidates(root) {
91
- return [
92
- path.join(root, 'node_modules', '@ingeniomaps', 'cauce', 'engine'),
93
- path.join(root, 'engine'),
94
- ]
95
- }
96
-
97
- function engineAt(root, relative = '') {
98
- return engineCandidates(root)
99
- .map((dir) => (relative ? path.join(dir, relative) : dir))
100
- .find((candidate) => fs.existsSync(candidate)) || ''
101
- }
102
-
103
- // Una ruta cualquiera del paquete, en el mismo orden de preferencia que el motor. La usan los
104
- // adaptadores de runner y los workflows: el motor los consume, el proyecto no los materializa.
105
90
  function packagePath(root, relative) {
106
91
  const candidates = [
107
92
  path.join(root, 'node_modules', '@ingeniomaps', 'cauce', relative),
@@ -110,6 +95,24 @@ function packagePath(root, relative) {
110
95
  return candidates.find((candidate) => fs.existsSync(candidate)) || ''
111
96
  }
112
97
 
98
+ // El modo declarado, o '' si no hay configuración. `toolkit` es este repositorio: acá se fabrica
99
+ // Cauce, así que los comandos que actualizan o instalan una instancia sólo pueden romper. Ausente e
100
+ // ilegible no son lo mismo —devolver '' ante un archivo roto dejaba correr contra el toolkit justo
101
+ // lo que reconocerlo impide—, así que lo segundo se levanta como error.
102
+ function mode(root) {
103
+ const file = path.join(root, 'ops.config.json')
104
+ if (!fs.existsSync(file)) return ''
105
+ try { return JSON.parse(fs.readFileSync(file, 'utf8')).mode || '' } catch (error) {
106
+ throw new Error(`ops.config.json no se puede leer (${error.message}).`)
107
+ }
108
+ }
109
+
110
+ // Dónde quedó el motor. El bridge de Antigravity repite esta cascada a mano porque corre antes de
111
+ // poder cargar este módulo: si cambia acá, cambia allá.
112
+ function engineAt(root, relative = '') {
113
+ return packagePath(root, relative ? path.join('engine', relative) : 'engine')
114
+ }
115
+
113
116
  // Definiciones que consume el motor —cargos y equipos— y que por eso viajan con el paquete en vez
114
117
  // de copiarse. Se reconocen por contener `system/`, que es el espacio del toolkit y no algo que un
115
118
  // proyecto deba crear. Una sola implementación para las dos, o divergen.
@@ -140,7 +143,6 @@ function entries(dir) {
140
143
  } catch { return [] }
141
144
  }
142
145
 
143
- // Entradas del proyecto que reemplazan a una del sistema con la misma identidad.
144
146
  function overrides(root) {
145
147
  const found = []
146
148
  for (const collection of SYSTEM_COLLECTIONS) {
@@ -165,6 +167,10 @@ const RETIRED = [
165
167
  '.github/workflows/agent-learning.yml',
166
168
  'automatization/runners',
167
169
  'automatization/workflows',
170
+ // Prometía ser el interruptor de los guards —«runner activo y gates requeridos», decía su README—
171
+ // y no lo leía nadie. Quien corría `install claude` seguía viendo `"runner": "manual"`, y un cargo
172
+ // que lo leyó dio por configurados unos gates que en realidad decide la configuración del runner.
173
+ 'automatization/config.json',
168
174
  ]
169
175
 
170
176
  // Aprendizaje que quedó dentro de una ruta retirada. Es lo único ahí que no se puede reponer, así
@@ -195,7 +201,6 @@ function systemPaths(root) {
195
201
  return paths
196
202
  }
197
203
 
198
- // Archivos de un árbol, relativos a él, para comparar instancia contra paquete.
199
204
  function treeFiles(dir, prefix = '') {
200
205
  const found = []
201
206
  let list = []
@@ -208,7 +213,6 @@ function treeFiles(dir, prefix = '') {
208
213
  return found.sort()
209
214
  }
210
215
 
211
- // Rutas que Cauce mantiene y que por eso conviene registrar y vigilar.
212
216
  function trackedPaths() {
213
217
  return [...RUNTIME_PATHS, ...SYSTEM_COLLECTIONS.map((collection) => `${collection}/system`)]
214
218
  }
@@ -234,12 +238,11 @@ module.exports = {
234
238
  RUNTIME_PATHS,
235
239
  retiredWithLearning,
236
240
  engineAt,
237
- engineCandidates,
238
241
  packagePath,
239
242
  SYSTEM_COLLECTIONS,
240
243
  SYSTEM_FILES,
241
- identity,
242
244
  localChanges,
245
+ mode,
243
246
  overrides,
244
247
  sourceOf,
245
248
  systemPaths,
@@ -1,15 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict'
3
3
 
4
- const fs = require('fs')
5
- const os = require('os')
6
- const path = require('path')
7
- const { spawnSync } = require('child_process')
8
-
4
+ const fs = require('node:fs')
5
+ const os = require('node:os')
6
+ const path = require('node:path')
7
+ const { spawnSync } = require('node:child_process')
8
+
9
+ // Sin stdin no hay nada que leer y los guards caen a las variables de entorno; con stdin ilegible sí
10
+ // hay algo y no se entiende, que es otra cosa. Devolver `{}` ahí dejaba a cada guard sin comando ni
11
+ // archivos, o sea permitiendo todo, y en silencio.
9
12
  function readInput() {
10
13
  let raw = ''
11
- try { raw = fs.readFileSync(0, 'utf8') } catch { /* no stdin */ }
12
- try { return raw.trim() ? JSON.parse(raw) : {} } catch { return {} }
14
+ try { raw = fs.readFileSync(0, 'utf8') } catch { /* sin stdin */ }
15
+ if (!raw.trim()) return {}
16
+ try { return JSON.parse(raw) } catch (error) {
17
+ block(`la entrada del hook no es JSON válido (${error.message}).`)
18
+ }
13
19
  }
14
20
 
15
21
  function commandOf(input) {
@@ -55,6 +61,15 @@ function block(message) {
55
61
  throw error
56
62
  }
57
63
 
64
+ // La configuración de la raíz ops. Un guard que no puede leerla bloquea: `findOpsRoot` sólo devuelve
65
+ // una raíz si `ops.config.json` existe, así que llegar acá significa roto o ilegible, no ausente.
66
+ // Dejarlo pasar convertía una coma de más en «sin límite de escritura».
67
+ function configOf(root) {
68
+ try { return JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8')) } catch (error) {
69
+ block(`ops.config.json no se puede leer (${error.message}). Un guard no decide sin él.`)
70
+ }
71
+ }
72
+
58
73
  function gitDirectory(command, cwd) {
59
74
  const flag = command.match(/(?:^|\s)git\s+-C\s+(['"]?)([^\s'";&|]+)\1/)
60
75
  const cd = command.match(/(?:^|[;&|]\s*)cd\s+(['"]?)([^\s'";&|]+)\1/)
@@ -146,8 +161,7 @@ function generated(input) {
146
161
  function workspaceBoundary(input) {
147
162
  const root = findOpsRoot(process.env.OPS_ROOT || process.env.CLAUDE_PROJECT_DIR || cwdOf(input))
148
163
  if (!root) return
149
- let config
150
- try { config = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8')) } catch { return }
164
+ const config = configOf(root)
151
165
  const allowed = [root, ...(config.workspaceRoots || []).map((entry) => path.resolve(root, entry.path))]
152
166
  for (const raw of filesOf(input)) {
153
167
  const file = path.resolve(cwdOf(input), raw)
@@ -228,22 +242,14 @@ function governance(input) {
228
242
  const command = commandOf(input)
229
243
  if (!isCommit(command)) return
230
244
  const dir = gitDirectory(command, cwdOf(input))
231
- // El contrato de un cargo y lo que lo mide son gobernanza, igual que un ADR o una regla.
232
- //
233
- // Lo puntual es la firma: `agent-promote` se niega si «Aprobación humana» no está firmada, pero lo
234
- // único que impedía que la escribiera un agente era una frase en un prompt. Una instrucción, no un
235
- // candado — y el archivo no estaba protegido por nada.
245
+ // El contrato de un cargo y lo que lo mide son gobernanza, igual que un ADR o una regla. La firma de
246
+ // «Aprobación humana» sólo estaba protegida por una frase en un prompt; `SKILL.md` y `references/`
247
+ // son lo que la propuesta cambia, y editarlos directo saltea el ciclo entero; y `evaluations/` es el
248
+ // denominador con que se juzga, así que moverlo ablanda toda medición pasada sin tocar una regla.
236
249
  //
237
- // Alrededor de la firma van las otras tres piezas del mismo acto. `SKILL.md` y `references/` son lo
238
- // que la propuesta cambia: sin ellos, editar el contrato directo saltea el ciclo entero. Y
239
- // `evaluations/` es el denominador con que se juzga —el propio recorrido de propuesta dice que
240
- // cambiarlo «es parte de lo que se aprueba»—, así que moverlo en silencio ablanda toda medición
241
- // pasada sin tocar una sola regla.
242
- //
243
- // Quedan afuera las dos clases de evidencia, y por la misma razón: registran lo que pasó un día en
244
- // vez de decidir algo. `learning/reports/` es lo investigado esa semana, y `evaluations/results/` la
245
- // transcripción de una corrida —que se escribe en cada evaluación, así que gobernarla pediría un
246
- // override por corrida—. Por eso `evaluations/` se nombra por partes en vez de entero.
250
+ // Quedan afuera las dos clases de evidencia, que registran lo que pasó un día en vez de decidir algo:
251
+ // `learning/reports/` y `evaluations/results/` —esta última se escribe en cada corrida, así que
252
+ // gobernarla pediría un override por evaluación—. Por eso `evaluations/` se nombra por partes.
247
253
  const governedPattern = new RegExp(
248
254
  String.raw`^(?:(?:template\/)?planning\/(?:rules\/|adr\/|PROTOCOL\.md|` +
249
255
  String.raw`METHODOLOGY\.md|FLOW\.md)|automatization\/|engine\/` +
@@ -351,21 +357,16 @@ function planningDrift(input) {
351
357
  block(`Planning o integraciones quedaron desalineados:\n${result.output}`)
352
358
  }
353
359
 
354
- // El motor llega por npm y se actualiza por npm. `workspace-boundary` no lo cubre: en una instalación
355
- // `node_modules/` cae dentro de la raíz declarada, así que editar el motor le parece legítimo.
356
- //
357
- // Editarlo desde la empresa rompe dos veces. El próximo `npm install` borra el cambio sin avisar, así
358
- // que el arreglo se pierde justo cuando alguien creyó haberlo hecho; y hasta entonces la empresa corre
359
- // un motor que no coincide con la versión que declara, que es la clase de diferencia que aparece como
360
- // un bug irreproducible. Un problema del motor se reporta y se arregla arriba, no acá.
360
+ // Hace falta un guard aparte porque `workspace-boundary` no lo cubre: `node_modules/` cae dentro de
361
+ // la raíz declarada, así que editar el motor le parece legítimo.
361
362
  //
362
- // El toolkit se exceptúa, y esa distinción ya estaba escrita: en su `ops.config.json` el modo es
363
- // `toolkit`. Ahí el motor es el producto y editarlo es exactamente el trabajo.
363
+ // Editarlo rompe dos veces: el próximo `npm install` borra el cambio sin avisar, y hasta entonces la
364
+ // empresa corre un motor que no coincide con la versión que declara —la clase de diferencia que
365
+ // aparece como un bug irreproducible—. En modo `toolkit` no aplica: ahí el motor es el producto.
364
366
  function engineWrites(input) {
365
367
  const root = findOpsRoot(process.env.OPS_ROOT || process.env.CLAUDE_PROJECT_DIR || cwdOf(input))
366
368
  if (!root) return
367
- let config
368
- try { config = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8')) } catch { return }
369
+ const config = configOf(root)
369
370
  if (config.mode === 'toolkit') return
370
371
  const pkg = path.join(root, 'node_modules', '@ingeniomaps', 'cauce')
371
372
  for (const raw of filesOf(input)) {
@@ -459,5 +460,5 @@ if (require.main === module) {
459
460
  }
460
461
 
461
462
  module.exports = {
462
- commandOf, execute, executeAll, fileOf, filesOf, findOpsRoot, guards, hookGroups, hookMetadata,
463
+ execute, executeAll, findOpsRoot, guards, hookGroups, hookMetadata,
463
464
  }
@@ -17,7 +17,9 @@ function read(root, provider) {
17
17
  const content = fs.readFileSync(path.join(dir, file), 'utf8')
18
18
  const fields = S.frontmatter(content)
19
19
  const body = content.replace(/^---[\s\S]*?---\s*/, '')
20
- const summary = ((body.match(/^#\s+(.+)$/m) || [])[1] || '').trim()
20
+ // `[ \t]` y no `\s`: `\s` casa el salto de línea, así que un `#` vacío se comía la siguiente
21
+ // línea no vacía y devolvía «## Descripción» como título. Con eso, «falta título» no saltaba nunca.
22
+ const summary = ((body.match(/^#[ \t]+(.+)$/m) || [])[1] || '').trim()
21
23
  const description = ((body.match(
22
24
  /^##\s+Descripción\s*\n([\s\S]*?)(?=^##\s+|(?![\s\S]))/im,
23
25
  ) || [])[1] || '').trim()
@@ -2,8 +2,8 @@
2
2
 
3
3
  // Registro y ciclo común de proveedores externos.
4
4
 
5
- const fs = require('fs')
6
- const path = require('path')
5
+ const fs = require('node:fs')
6
+ const path = require('node:path')
7
7
  const F = require('../core/files')
8
8
  const S = require('./state')
9
9
  const P = require('./proposals')
@@ -18,10 +18,6 @@ function readJson(file) {
18
18
  }
19
19
  }
20
20
 
21
- function writeJson(file, value) {
22
- F.atomicWriteJson(file, value)
23
- }
24
-
25
21
  function safeSegment(value, label) {
26
22
  const segment = String(value || '')
27
23
  if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(segment) || segment === '.' || segment === '..') {
@@ -95,7 +91,7 @@ function validate(root, onlyProvider = '') {
95
91
  const secret = sensitivePath(loaded.config)
96
92
  if (secret) errors.push(`${name}: ${secret} no puede contener secretos; usa una variable de entorno`)
97
93
  try {
98
- adapter(name).validateConfig(loaded.config, errors, warnings)
94
+ adapter(name).validateConfig(loaded.config, errors)
99
95
  } catch (error) {
100
96
  errors.push(`${name}: ${error.message}`)
101
97
  }
@@ -254,14 +250,17 @@ async function sync(root, name, options = {}) {
254
250
  }
255
251
  fs.mkdirSync(desiredDir, { recursive: true })
256
252
  F.atomicWrite(draftFile, draft)
257
- writeJson(snapshotFile, snapshot)
253
+ F.atomicWriteJson(snapshotFile, snapshot)
258
254
  }
259
- const complete = options.complete !== false
260
- if (complete) cleanupMissing(root, name, seen, result)
261
- writeJson(path.join(staging, 'sync-state.json'), {
255
+ // Siempre se limpia: ningún adaptador puede devolver una lectura parcial. El de Jira lanza al
256
+ // exceder páginas, al repetir un token y ante cualquier HTTP que no sea 200, así que o trajo todo o
257
+ // no trajo nada. Había una bandera `complete` para saltearlo, sin forma de activarla desde el CLI y
258
+ // sin nadie que leyera el campo que escribía. Vuelve el día que un adaptador sepa decir «traje una
259
+ // parte», que es cuando protegería algo.
260
+ cleanupMissing(root, name, seen, result)
261
+ F.atomicWriteJson(path.join(staging, 'sync-state.json'), {
262
262
  schemaVersion: 2,
263
263
  provider: name,
264
- complete,
265
264
  pulledAt: new Date().toISOString(),
266
265
  keys: [...seen].sort(),
267
266
  })
@@ -284,7 +283,7 @@ function cleanupMissing(root, name, seen, result) {
284
283
  continue
285
284
  }
286
285
  snapshot.sync.missingFromRemote = true
287
- writeJson(snapshotFile, snapshot)
286
+ F.atomicWriteJson(snapshotFile, snapshot)
288
287
  result.missing++
289
288
  }
290
289
  }
@@ -364,7 +363,7 @@ function promote(root, name, key) {
364
363
  draft = replaceField(draft, 'promotedAt', `"${new Date().toISOString()}"`)
365
364
  F.atomicWrite(draftFile, draft)
366
365
  snapshot.sync.draftChanged = true
367
- writeJson(path.join(dir, 'remote.json'), snapshot)
366
+ F.atomicWriteJson(path.join(dir, 'remote.json'), snapshot)
368
367
  return { key, kind: fields.promotionKind }
369
368
  }
370
369
 
@@ -385,13 +384,11 @@ const oneLine = (value) => String(value).replace(/\s+/g, ' ').trim()
385
384
  module.exports = {
386
385
  STATES,
387
386
  adapter,
388
- draftSections,
389
387
  frontmatter,
390
388
  promote,
391
389
  providerConfig,
392
390
  reconcile,
393
391
  safeSegment,
394
- stagingItems,
395
392
  sync,
396
393
  validate,
397
394
  writebackPlan,
@@ -45,7 +45,7 @@ function replaceField(text, name, value) {
45
45
 
46
46
  function sections(text) {
47
47
  const body = String(text).replace(/^---[\s\S]*?---\s*/, '')
48
- const title = ((body.match(/^#\s+(.+)$/m) || [])[1] || '').trim()
48
+ const title = ((body.match(/^#[ \t]+(.+)$/m) || [])[1] || '').trim()
49
49
  const result = { title }
50
50
  const names = ['Descripción', 'Aceptación', 'Comentarios', 'Decisiones', 'Problemas de preparación']
51
51
  for (const [index, name] of names.entries()) {
@@ -220,7 +220,6 @@ function reconcile(root, provider, operation, keys = []) {
220
220
  }
221
221
 
222
222
  module.exports = {
223
- CRITICAL_FIELDS,
224
223
  STATES,
225
224
  categoryOf,
226
225
  derive,
@@ -79,9 +79,6 @@ function validateRoadmapStructure(dir) {
79
79
  }
80
80
 
81
81
  module.exports = {
82
- DECISION_TRACE,
83
- EPIC_AUXILIARY_FILES,
84
- TEST_TRACE,
85
82
  validDecisionTrace,
86
83
  validTestTrace,
87
84
  validateDoneEntry,
@@ -2,11 +2,10 @@
2
2
 
3
3
  // Contratos deterministas de planning; no contiene estado de proyecto.
4
4
 
5
- const fs = require('fs')
6
- const path = require('path')
5
+ const fs = require('node:fs')
6
+ const path = require('node:path')
7
7
 
8
8
  const EPIC_STATES = ['open', 'active', 'closed']
9
- const TIERS = ['directo', 'lite', 'full']
10
9
 
11
10
  function read(file) {
12
11
  try { return fs.readFileSync(file, 'utf8') } catch { return '' }
@@ -62,7 +61,7 @@ function criteriaRefs(text) {
62
61
  return [...new Set(refs)]
63
62
  }
64
63
 
65
- function readEpics(dir, { includeTemplates = false } = {}) {
64
+ function readEpics(dir) {
66
65
  return epicFiles(dir).map(({ name, file }) => {
67
66
  const text = read(file)
68
67
  const field = frontmatter(text)
@@ -76,7 +75,6 @@ function readEpics(dir, { includeTemplates = false } = {}) {
76
75
  const stories = [...section(text, /Historias/i).matchAll(storyPattern)]
77
76
  .map((match) => ({
78
77
  slug: match[1].trim(),
79
- text: match[2].replace(/\s+/g, ' ').trim(),
80
78
  criteria: criteriaRefs(match[2]),
81
79
  service: ((match[2].match(/\(service:\s*([^)]+)\)/) || [])[1] || '').trim(),
82
80
  }))
@@ -86,12 +84,11 @@ function readEpics(dir, { includeTemplates = false } = {}) {
86
84
  num: field('epic'),
87
85
  title: field('title'),
88
86
  status: field('status'),
89
- service: field('service'),
90
87
  criteria,
91
88
  stories,
92
89
  hasContext: /^##\s+Contexto relevante/im.test(text),
93
90
  }
94
- }).filter((epic) => includeTemplates || epic.status !== 'template' && epic.num !== '000')
91
+ }).filter((epic) => epic.status !== 'template' && epic.num !== '000')
95
92
  }
96
93
 
97
94
  function readBacklog(dir) {
@@ -110,7 +107,7 @@ function readBacklog(dir) {
110
107
  if (!task || !current) continue
111
108
  const rest = task[3]
112
109
  current.tasks.push({
113
- slug: task[1].trim(), tier: task[2] || '', text: rest,
110
+ slug: task[1].trim(), tier: task[2] || '',
114
111
  epic: ((rest.match(/\(epic:\s*(\d{3})\)/) || [])[1] || ''),
115
112
  service: ((rest.match(/\(service:\s*([^)]+)\)/) || [])[1] || '').trim(),
116
113
  acceptance: ((rest.match(/_Aceptaci[oó]n:\s*([^_]+)_/i) || [])[1] || '').trim(),
@@ -180,5 +177,5 @@ function readInbox(dir) {
180
177
  }
181
178
 
182
179
  module.exports = {
183
- EPIC_STATES, TIERS, read, frontmatter, readEpics, readBacklog, readDone, readWip, readInbox,
180
+ EPIC_STATES, read, frontmatter, readEpics, readBacklog, readDone, readWip, readInbox,
184
181
  }
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const fs = require('fs')
4
- const path = require('path')
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
5
5
  const catalog = require('../agents/catalog')
6
6
 
7
7
  const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
@@ -51,8 +51,12 @@ function validate(root, slug) {
51
51
  }
52
52
  if (!OUTCOMES.includes(manifest.outcome)) errors.push(`outcome debe ser ${OUTCOMES.join(' o ')}`)
53
53
  if (!Array.isArray(manifest.stages) || !manifest.stages.length) errors.push('stages debe contener etapas')
54
- if (!Array.isArray(manifest.guardrails) || !manifest.guardrails.length) errors.push('guardrails debe contener controles')
55
- if (!Array.isArray(manifest.completion) || !manifest.completion.length) errors.push('completion debe contener criterios')
54
+ if (!Array.isArray(manifest.guardrails) || !manifest.guardrails.length) {
55
+ errors.push('guardrails debe contener controles')
56
+ }
57
+ if (!Array.isArray(manifest.completion) || !manifest.completion.length) {
58
+ errors.push('completion debe contener criterios')
59
+ }
56
60
 
57
61
  const agents = new Set([manifest.entryAgent, manifest.facilitator])
58
62
  for (const agent of Object.values(manifest.decisionOwners || {})) agents.add(agent)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingeniomaps/cauce",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Sistema portable de planificación y ejecución verificable para cualquier proyecto",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -24,6 +24,7 @@
24
24
  "integration:check": "node engine/cli/ops.js integration check template jira",
25
25
  "test": "bash test/hooks-smoke.sh && node --test test/*.test.js",
26
26
  "coverage": "bash test/coverage.sh",
27
+ "coverage:update": "bash test/coverage.sh --update",
27
28
  "ci": "npm run check && npm run automation:check && npm run integration:check && npm run coverage"
28
29
  },
29
30
  "engines": {
@@ -1,7 +1,9 @@
1
1
  # Reglas de construcción
2
2
 
3
3
  Este archivo gobierna el qué y el cuándo. `planning/PROTOCOL.md` gobierna el flujo y
4
- `planning/rules/` el cómo.
4
+ `planning/rules/` el cómo: sus reglas rigen cada tarea y se leen antes de empezar, no cuando algo sale
5
+ mal. Ahí está la conducta que no depende de este proyecto —trato con sistemas externos, qué se debe
6
+ entregar aun al negarse—; acá, lo que sí depende.
5
7
 
6
8
  ## Mapa real
7
9
 
@@ -16,14 +18,8 @@ El mapa no debe duplicar documentación técnica: enlaza a la fuente de verdad d
16
18
 
17
19
  ## Integraciones y ambientes
18
20
 
19
- Por defecto, todo sistema externo se considera real y de solo lectura. Sin aprobación humana explícita:
20
-
21
- - no desplegar, publicar, enviar mensajes ni escribir en producción;
22
- - no cobrar, reembolsar, gastar créditos ni llamar APIs con costo de forma repetida;
23
- - no editar secretos, credenciales, DNS, permisos o cuentas;
24
- - no borrar datos ni ejecutar migraciones irreversibles.
25
-
26
- Documentar excepciones concretas aquí. Un entorno de desarrollo o sandbox debe nombrarse explícitamente.
21
+ R12 fija el trato con todo sistema externo: real y de sólo lectura mientras no se lo nombre como
22
+ sandbox. Las excepciones de este proyecto —y sólo ellas— van acá, con el entorno concreto.
27
23
 
28
24
  ## Cargos disponibles
29
25
 
@@ -32,7 +28,19 @@ decide, qué no le corresponde y cuál es su entrega mínima; sus métodos viven
32
28
 
33
29
  Antes de resolver algo que cae claramente en un cargo —una decisión de producto, un diseño de
34
30
  arquitectura, una estrategia de pruebas—, adoptá ese contrato en vez de improvisar el criterio. Los
35
- límites del cargo no son sugerencias: un cargo que no puede decidir solo, no decide solo.
31
+ límites del cargo no son sugerencias: un cargo que no puede decidir solo, no decide solo. Su
32
+ contraparte es R13: el que se niega bien y no deja nada tampoco cumplió.
33
+
34
+ Para saber cuál es, una línea por cargo:
35
+
36
+ ```bash
37
+ node tools/ops.js agents list # quién hace qué y qué NO le corresponde
38
+ ```
39
+
40
+ Se elige leyendo esa lista, no abriendo carpetas. Si ninguna línea encaja, el cargo no existe y el
41
+ camino es escribir el propio en `agents/roles/<slug>/`; forzar el más parecido es peor que no usar
42
+ ninguno. Si encaja pero lo querés más enfocado en esta empresa, el contexto va en
43
+ `organization/roles/<slug>.md` y el contrato sigue siendo el de Cauce.
36
44
 
37
45
  En Claude y Antigravity los cargos aparecen como skills invocables por nombre. En Codex y Gemini no hay
38
46
  mecanismo nativo: leé directamente el `SKILL.md` del cargo. Los que trae
@@ -52,7 +60,7 @@ Los cargos **propios** son otra cosa: nadie más los va a investigar. Su ciclo e
52
60
  comando, sin cron —activarlo en tu repositorio es decisión tuya—:
53
61
 
54
62
  ```bash
55
- node tools/ops.js agents list --own # sólo los tuyos, sin los 47 del catálogo
63
+ node tools/ops.js agents list --own # sólo los tuyos, sin los del catálogo
56
64
  node tools/ops.js learn <slug> # arma el informe de la semana
57
65
  /agent-propose <slug> # escribe el cambio concreto sobre esos informes
58
66
  # ↑ firmá «Aprobación humana» en la propuesta antes de seguir
@@ -64,26 +72,6 @@ Un cargo propio puede declarar en `learning/AUTOMATION.md` qué debe investigar
64
72
  Si su profesión no existe fuera de tu empresa, sus fuentes son internas: lo que aprende sale de sus
65
73
  propias decisiones, no de un estándar de afuera.
66
74
 
67
- ## Negarse no es entregar
68
-
69
- Cada límite de un cargo tiene una contraparte positiva, y respetar el límite no exime de ella. Un cargo
70
- que se niega bien y no deja nada dejó el pedido sin resolver: hizo la mitad barata del trabajo.
71
-
72
- - **Ante falta de contexto, producir el borrador reversible marcado como supuesto, no bloquear pidiendo
73
- insumos.** Pedir una decisión humana sólo cuando las opciones cambien materialmente el rumbo, el
74
- gasto, una obligación externa o el riesgo. Un borrador que se puede tirar no cambia ninguna de esas
75
- cosas; no entregarlo, sí.
76
- - **Delimitar el espacio de decisión no es decidir.** Enumerar las opciones y pedir que alguien elija
77
- es trabajo a medias cuando ninguna de ellas cambia materialmente el rumbo: en ese caso se propone una,
78
- con su razón, marcada como propuesta.
79
- - **Todo rechazo se acompaña de lo que sí corresponde hacer**, dicho como acción concreta y no como
80
- categoría. «Esto no me corresponde» sin decir a quién ni con qué pregunta deja el pedido donde estaba.
81
- - **Nombrar lo que falta no reemplaza a entregar lo que se puede.** Lo que sí se pudo establecer se
82
- escribe igual, aunque el resultado quede parcial y marcado como tal.
83
-
84
- Esto no afloja ningún límite: no promover, no prometer fechas, no inventar evidencia y no exceder la
85
- autoridad del cargo siguen siendo absolutos. Lo que se cierra es la salida de cumplirlos sin entregar.
86
-
87
75
  ## Qué se puede editar y qué no
88
76
 
89
77
  Todo directorio `system/` pertenece a Cauce y se reemplaza completo al actualizar. **Nunca editar nada
@@ -3,10 +3,13 @@
3
3
  Wiring local entre `planning/PROTOCOL.md` y el runner elegido. El proyecto empieza en modo manual y seguro;
4
4
  ningún runner se activa solo.
5
5
 
6
- - `config.json`: runner activo y gates requeridos.
7
6
  - `hooks/`: los guards que se ejecutan. Acá va también el tuyo: agregalo con otro nombre y registralo
8
7
  en la configuración de tu runner, que es del proyecto y sobrevive a cada actualización.
9
8
 
9
+ Qué guard corre y cuándo lo decide la configuración de tu runner —`.claude/settings.json` y sus
10
+ equivalentes—, que es la única fuente: la escribe `automation install` y la lee la herramienta. Para
11
+ desactivar un guard, quitá su entrada de ahí. `automation list-hooks` enumera los que existen.
12
+
10
13
  Los adaptadores de runner y los workflows no se copian acá: son definiciones que el motor consume y
11
14
  viajan con Cauce, igual que el catálogo de cargos y los equipos. `automation install` los lee desde ahí.
12
15
 
@@ -5,6 +5,7 @@
5
5
  - `system/process.md`: planificación, alcance, review y sincronización de estado.
6
6
  - `system/code-shape.md`: simplicidad y forma del cambio.
7
7
  - `system/commits.md`: historia versionada y entrega.
8
+ - `system/conduct.md`: trato con sistemas externos y obligación de entregar al negarse.
8
9
 
9
10
  Las reglas propias del proyecto van en este directorio, junto a `system/`, y nunca se tocan al
10
11
  actualizar. Para cambiar una regla del sistema, escribí la tuya con el mismo nombre: la del proyecto
@@ -12,3 +12,8 @@ No reformatear, renombrar ni reparar elementos ajenos a la tarea.
12
12
 
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
+
16
+ ## R11 — Comentarios que explican el porqué
17
+
18
+ Un comentario dice por qué el código es así, no qué hace: si el código ya lo dice, sobra. Cada razón
19
+ vive en un solo lugar, y un bloque de más de tres líneas tiene que justificar su largo.