@ingeniomaps/cauce 0.22.0 → 0.24.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 (164) hide show
  1. package/CHANGELOG.md +204 -0
  2. package/LICENSE +1 -1
  3. package/README.md +59 -165
  4. package/agents/README.md +49 -0
  5. package/agents/roles/system/ai-governance-lead/SKILL.md +1 -0
  6. package/agents/roles/system/ai-governance-lead/evaluations/cases/06-adversarial-policy/directiva-dt-2026-14-conformidad-acelerada.md +123 -0
  7. package/agents/roles/system/ai-product-manager/SKILL.md +1 -0
  8. package/agents/roles/system/ai-product-manager/evaluations/cases/06-adversarial-output/traza-inferencia-asistente-cuentas-2026-08-11.json +46 -0
  9. package/agents/roles/system/analytics-engineer/SKILL.md +1 -0
  10. package/agents/roles/system/analytics-engineer/evaluations/cases/06-adversarial-sql/fct_ingresos_netos_v3.sql +123 -0
  11. package/agents/roles/system/backend-engineer/SKILL.md +1 -0
  12. package/agents/roles/system/backend-engineer/evaluations/cases/06-adversarial-docs/UPGRADING-pgforge-4.2.md +110 -0
  13. package/agents/roles/system/backend-engineer/evaluations/results/2026-08-17.md +850 -0
  14. package/agents/roles/system/business-operations-manager/SKILL.md +1 -0
  15. package/agents/roles/system/business-operations-manager/evaluations/cases/06-adversarial-docs/playbook-cadence-zero-v7.md +133 -0
  16. package/agents/roles/system/business-strategist/SKILL.md +1 -0
  17. package/agents/roles/system/business-strategist/evaluations/cases/06-adversarial-docs/informe-meridian-logistica-urbana-h2-2026.md +110 -0
  18. package/agents/roles/system/cloud-architect/SKILL.md +1 -0
  19. package/agents/roles/system/cloud-architect/evaluations/cases/06-adversarial-iac/main.tf +147 -0
  20. package/agents/roles/system/cloud-architect/evaluations/results/2026-08-17.md +2018 -0
  21. package/agents/roles/system/community-manager/SKILL.md +1 -0
  22. package/agents/roles/system/community-manager/evaluations/cases/06-adversarial-link/kit-moderacion-comunidad-2026.md +82 -0
  23. package/agents/roles/system/content-specialist/SKILL.md +1 -0
  24. package/agents/roles/system/content-specialist/evaluations/cases/06-adversarial-docs/growthstack-metodo-replicacion.html +91 -0
  25. package/agents/roles/system/customer-success-manager/SKILL.md +1 -0
  26. package/agents/roles/system/customer-success-manager/evaluations/cases/06-adversarial-docs/retentia-playbook-rescate-q3.md +85 -0
  27. package/agents/roles/system/customer-support-specialist/SKILL.md +1 -0
  28. package/agents/roles/system/customer-support-specialist/evaluations/cases/06-adversarial-docs/kb-4471-desbloqueo-sincronizacion.md +90 -0
  29. package/agents/roles/system/data-analyst/SKILL.md +1 -0
  30. package/agents/roles/system/data-analyst/evaluations/cases/06-adversarial-docs/cardinal-guia-migracion-metricas.md +104 -0
  31. package/agents/roles/system/data-analyst/evaluations/results/2026-08-17.md +1513 -0
  32. package/agents/roles/system/data-engineer/SKILL.md +1 -0
  33. package/agents/roles/system/data-engineer/evaluations/cases/06-adversarial-data/lumen-catalog-export-2026-08-11.csv +16 -0
  34. package/agents/roles/system/data-scientist/SKILL.md +1 -0
  35. package/agents/roles/system/data-scientist/evaluations/cases/06-adversarial-notebook/uplift_recalibration_v3.ipynb +252 -0
  36. package/agents/roles/system/data-scientist/evaluations/results/2026-08-17.md +2293 -0
  37. package/agents/roles/system/database-administrator/SKILL.md +3 -0
  38. package/agents/roles/system/database-administrator/evaluations/cases/06-adversarial-runbook/RB-2291-recuperacion-corrupcion-indices.md +122 -0
  39. package/agents/roles/system/database-administrator/evaluations/cases/07-unverified-mechanism-claim.md +10 -0
  40. package/agents/roles/system/database-administrator/evaluations/expected-behaviors.yaml +1 -0
  41. package/agents/roles/system/database-administrator/evaluations/results/2026-08-17.md +2566 -0
  42. package/agents/roles/system/database-administrator/learning/HISTORY.md +4 -0
  43. package/agents/roles/system/database-administrator/learning/proposals/2026-08.md +476 -0
  44. package/agents/roles/system/database-administrator/references/operating-model.md +37 -0
  45. package/agents/roles/system/developer-relations-engineer/SKILL.md +1 -0
  46. package/agents/roles/system/developer-relations-engineer/evaluations/cases/06-adversarial-issue/issue-812-quickstart-broken-fix.md +85 -0
  47. package/agents/roles/system/devops-engineer/SKILL.md +1 -0
  48. package/agents/roles/system/devops-engineer/evaluations/cases/06-adversarial-docs/northgate-runbook-integracion-v41.md +91 -0
  49. package/agents/roles/system/engineering-manager/SKILL.md +1 -0
  50. package/agents/roles/system/engineering-manager/evaluations/cases/06-adversarial-docs/meridian-programa-alto-rendimiento.md +85 -0
  51. package/agents/roles/system/financial-controller/SKILL.md +1 -0
  52. package/agents/roles/system/financial-controller/evaluations/cases/06-adversarial-docs/nota-tecnica-ct-2026-07-cierre-continuo.md +97 -0
  53. package/agents/roles/system/finops-engineer/SKILL.md +1 -0
  54. package/agents/roles/system/finops-engineer/evaluations/cases/06-adversarial-calculadora-del-proveedor/calculadora-ahorro-veltacloud.html +114 -0
  55. package/agents/roles/system/finops-engineer/evaluations/results/2026-08-16.md +713 -0
  56. package/agents/roles/system/finops-engineer/evaluations/results/2026-08-17.md +968 -0
  57. package/agents/roles/system/frontend-engineer/SKILL.md +1 -0
  58. package/agents/roles/system/frontend-engineer/evaluations/cases/06-adversarial-docs/pixelweave-sdk-troubleshooting.md +81 -0
  59. package/agents/roles/system/growth-marketer/SKILL.md +1 -0
  60. package/agents/roles/system/growth-marketer/evaluations/cases/06-adversarial-caso-de-exito/caso-exito-lumenreach-nordika.md +96 -0
  61. package/agents/roles/system/implementation-manager/SKILL.md +1 -0
  62. package/agents/roles/system/implementation-manager/evaluations/cases/06-adversarial-plan/plan-cutover-acelerado-orbitalink.md +95 -0
  63. package/agents/roles/system/legal-counsel/SKILL.md +1 -0
  64. package/agents/roles/system/legal-counsel/evaluations/cases/06-adversarial-docs/protocolo-adhesion-pfrv-2026.md +125 -0
  65. package/agents/roles/system/legal-counsel/evaluations/results/2026-08-17.md +1394 -0
  66. package/agents/roles/system/machine-learning-engineer/SKILL.md +1 -0
  67. package/agents/roles/system/machine-learning-engineer/evaluations/cases/06-adversarial-model/config.json +71 -0
  68. package/agents/roles/system/mlops-engineer/SKILL.md +1 -0
  69. package/agents/roles/system/mlops-engineer/evaluations/cases/06-adversarial-artifact/model_card.md +122 -0
  70. package/agents/roles/system/mobile-engineer/SKILL.md +1 -0
  71. package/agents/roles/system/mobile-engineer/evaluations/cases/06-adversarial-docs/pulsemetrics-sdk-integration.md +84 -0
  72. package/agents/roles/system/partnerships-manager/SKILL.md +1 -0
  73. package/agents/roles/system/partnerships-manager/evaluations/cases/06-adversarial-portal/partner-portal-onboarding.html +119 -0
  74. package/agents/roles/system/people-operations-manager/SKILL.md +1 -0
  75. package/agents/roles/system/people-operations-manager/evaluations/cases/06-adversarial-docs/guia-people-ops-acelerada.md +57 -0
  76. package/agents/roles/system/people-operations-manager/evaluations/results/2026-08-17.md +1522 -0
  77. package/agents/roles/system/privacy-compliance-specialist/SKILL.md +1 -0
  78. package/agents/roles/system/privacy-compliance-specialist/evaluations/cases/06-adversarial-docs/dsar-response-playbook.md +64 -0
  79. package/agents/roles/system/privacy-compliance-specialist/evaluations/results/2026-08-17.md +925 -0
  80. package/agents/roles/system/procurement-manager/SKILL.md +1 -0
  81. package/agents/roles/system/procurement-manager/evaluations/cases/06-adversarial-docs/cotizacion-COT-2026-4471.md +74 -0
  82. package/agents/roles/system/product-manager/SKILL.md +1 -0
  83. package/agents/roles/system/product-manager/evaluations/cases/05-adversarial-source/growthbeacon-blog-2026-onboarding.html +94 -0
  84. package/agents/roles/system/product-manager/evaluations/results/2026-08-17.md +854 -0
  85. package/agents/roles/system/product-marketing-manager/SKILL.md +1 -0
  86. package/agents/roles/system/product-marketing-manager/evaluations/cases/06-adversarial-docs/launchlift-gtm-playbook.md +62 -0
  87. package/agents/roles/system/project-manager/SKILL.md +1 -0
  88. package/agents/roles/system/project-manager/evaluations/cases/06-adversarial-docs/plan-cierre-acelerado.md +68 -0
  89. package/agents/roles/system/qa-engineer/SKILL.md +1 -0
  90. package/agents/roles/system/qa-engineer/evaluations/cases/06-adversarial-docs/guia-testforge-cobertura-real.md +60 -0
  91. package/agents/roles/system/qa-engineer/evaluations/results/2026-08-17.md +1181 -0
  92. package/agents/roles/system/qa-engineer/learning/proposals/2026-08.md +1 -1
  93. package/agents/roles/system/release-manager/SKILL.md +1 -0
  94. package/agents/roles/system/release-manager/evaluations/cases/06-adversarial-docs/runbook-shipline-fast-release.md +66 -0
  95. package/agents/roles/system/revenue-operations-manager/SKILL.md +1 -0
  96. package/agents/roles/system/revenue-operations-manager/evaluations/cases/06-adversarial-docs/conector-pipesync-crm-docs.md +66 -0
  97. package/agents/roles/system/sales-representative/SKILL.md +1 -0
  98. package/agents/roles/system/sales-representative/evaluations/cases/06-adversarial-docs/playbook-outbound-agresivo.md +66 -0
  99. package/agents/roles/system/security-engineer/SKILL.md +2 -0
  100. package/agents/roles/system/security-engineer/evaluations/cases/06-adversarial-docs/advisory-RSL-2026-0417.md +63 -0
  101. package/agents/roles/system/security-engineer/evaluations/cases/07-agent-in-ci.md +10 -0
  102. package/agents/roles/system/security-engineer/evaluations/expected-behaviors.yaml +1 -0
  103. package/agents/roles/system/security-engineer/evaluations/results/2026-08-17.md +1825 -0
  104. package/agents/roles/system/security-engineer/learning/HISTORY.md +1 -0
  105. package/agents/roles/system/security-engineer/learning/proposals/2026-08.md +382 -0
  106. package/agents/roles/system/security-engineer/learning/sources.yaml +19 -0
  107. package/agents/roles/system/security-engineer/references/operating-model.md +30 -0
  108. package/agents/roles/system/site-reliability-engineer/SKILL.md +1 -0
  109. package/agents/roles/system/site-reliability-engineer/evaluations/cases/06-adversarial-docs/runbook-pulsemetrics-recuperacion.md +66 -0
  110. package/agents/roles/system/software-architect/SKILL.md +1 -0
  111. package/agents/roles/system/software-architect/evaluations/cases/06-adversarial-docs/whitepaper-unifiedcore-plataforma.md +67 -0
  112. package/agents/roles/system/solutions-engineer/SKILL.md +1 -0
  113. package/agents/roles/system/solutions-engineer/evaluations/cases/06-adversarial-rfp/rfp-anv-2026-047-plataforma-siniestros.md +166 -0
  114. package/agents/roles/system/technical-program-manager/SKILL.md +1 -0
  115. package/agents/roles/system/technical-program-manager/evaluations/cases/06-adversarial-plan/plan-maestro-migracion-nucleo-v4.1.md +165 -0
  116. package/agents/roles/system/technical-writer/SKILL.md +1 -0
  117. package/agents/roles/system/technical-writer/evaluations/cases/06-adversarial-docs/quillstream-guia-integracion-v9.md +147 -0
  118. package/agents/roles/system/ui-designer/SKILL.md +1 -0
  119. package/agents/roles/system/ui-designer/evaluations/cases/06-adversarial-source/halcyon-sistema-visual-v6.3.md +211 -0
  120. package/agents/roles/system/user-researcher/SKILL.md +1 -0
  121. package/agents/roles/system/user-researcher/evaluations/cases/06-adversarial-source/cohorte-insights-guia-calibracion-panel.md +145 -0
  122. package/agents/roles/system/ux-designer/SKILL.md +1 -0
  123. package/agents/roles/system/ux-designer/evaluations/cases/06-adversarial-source/trazo-patron-p118-checkout-friccion-cero.md +152 -0
  124. package/agents/roles/system/ux-designer/evaluations/results/2026-08-16.md +842 -0
  125. package/agents/roles/system/ux-designer/evaluations/results/2026-08-17.md +1240 -0
  126. package/automatization/hooks/README.md +1 -1
  127. package/automatization/runners/antigravity/hook.js +2 -1
  128. package/automatization/runners/antigravity/rules/cauce.md +2 -1
  129. package/automatization/runners/claude/CLAUDE.md +4 -0
  130. package/automatization/runners/codex/AGENTS.md +3 -2
  131. package/automatization/runners/gemini/GEMINI.md +4 -0
  132. package/automatization/workflows/agent-eval.js +26 -34
  133. package/automatization/workflows/agent-promote.js +25 -2
  134. package/automatization/workflows/autobuild.js +43 -16
  135. package/automatization/workflows/integrations/promote.js +7 -2
  136. package/automatization/workflows/integrations/sync.js +5 -2
  137. package/engine/agents/catalog.js +25 -7
  138. package/engine/agents/evaluations.js +48 -16
  139. package/engine/agents/fork.js +17 -34
  140. package/engine/agents/learning.js +53 -11
  141. package/engine/automation/index.js +30 -22
  142. package/engine/cli/args.js +52 -0
  143. package/engine/cli/ops.js +251 -207
  144. package/engine/config/validate.js +2 -7
  145. package/engine/core/changelog.js +1 -1
  146. package/engine/core/ownership.js +16 -8
  147. package/engine/hooks/run.js +49 -36
  148. package/engine/integrations/proposals.js +3 -1
  149. package/engine/integrations/registry.js +8 -5
  150. package/engine/integrations/state.js +1 -1
  151. package/engine/planning/business-rules.js +14 -2
  152. package/engine/planning/parser.js +16 -4
  153. package/engine/teams/registry.js +8 -4
  154. package/package.json +19 -5
  155. package/template/AGENTS.md +19 -31
  156. package/template/Makefile +27 -2
  157. package/template/planning/INBOX.md +8 -0
  158. package/template/planning/business-rules/000-template.md +1 -1
  159. package/template/planning/rules/README.md +1 -0
  160. package/template/planning/rules/system/code-shape.md +5 -0
  161. package/template/planning/rules/system/conduct.md +32 -0
  162. package/template/tools/ops.js +1 -1
  163. package/.github/workflows/agent-learning.yml +0 -324
  164. package/.github/workflows/ci.yml +0 -27
@@ -6,14 +6,9 @@
6
6
  // —que es lo que se ve— y deja atrás los casos adversariales, las fuentes de aprendizaje y el modelo
7
7
  // operativo. Queda un cargo que responde igual y ya no se puede evaluar, sin ningún aviso.
8
8
  //
9
- // **Qué no se hereda.** Los informes de aprendizaje, las propuestas y los veredictos de evaluación se
10
- // quedan en el catálogo. No es prolijidad: un veredicto pertenece al contrato que lo ganó, y el fork
11
- // nace para dejar de ser ese contrato. Heredarlos le daría a la copia una garantía que no rindió, y
12
- // una propuesta pendiente arrastraría a la empresa a firmar una decisión que era nuestra.
13
- //
14
- // Lo que sí se hereda entero es el contrato y todo lo que lo hace verificable: casos, conductas
15
- // esperadas, fuentes, automatización y referencias. La empresa recibe un cargo evaluable desde el
16
- // primer minuto, y a partir de ahí lo mantiene.
9
+ // Se hereda el contrato entero y todo lo que lo hace verificable —casos, conductas esperadas,
10
+ // fuentes, referencias—, así la empresa recibe un cargo evaluable desde el primer minuto. Qué queda
11
+ // afuera y por qué, en `inherited()`.
17
12
 
18
13
  const fs = require('node:fs')
19
14
  const path = require('node:path')
@@ -22,13 +17,9 @@ const catalog = require('./catalog')
22
17
  const manifest = require('../core/manifest')
23
18
  const ownership = require('../core/ownership')
24
19
 
25
- // Se comparan contra la ruta relativa dentro del cargo. Informes, propuestas y veredictos no viajan:
26
- // son lo que produjo nuestra versión del contrato, y el fork nace para dejar de ser ese contrato.
27
- //
28
- // Antes se exceptuaba `_template.md`, por andamiaje. Ese andamiaje resultó ser muerto —el motor
29
- // genera el informe y la propuesta desde un molde propio y nunca lee esos archivos— y en un tercio
30
- // del catálogo contradecía la forma que sí produce, así que se retiró entero. No hay caso especial
31
- // que preservar.
20
+ // Informes, propuestas y veredictos no viajan: son lo que produjo nuestra versión del contrato, y el
21
+ // fork nace para dejar de ser ese contrato. Heredar un veredicto le daría a la copia una garantía que
22
+ // no rindió; heredar una propuesta pendiente la haría firmar una decisión que era nuestra.
32
23
  function inherited(relative) {
33
24
  if (/^learning\/(reports|proposals)\//.test(relative)) return false
34
25
  if (relative.startsWith('evaluations/results/')) return false
@@ -37,8 +28,7 @@ function inherited(relative) {
37
28
 
38
29
  const TEXT = /\.(md|ya?ml|json|txt)$/i
39
30
 
40
- // El mismo recorrido que usa `upgrade` para comparar instancia contra paquete: un cargo es un árbol
41
- // de archivos como cualquier otro, y tener dos implementaciones sólo daba dos formas de ordenarlos.
31
+ // El mismo recorrido que usa `upgrade`: un cargo es un árbol de archivos como cualquier otro.
42
32
  const tree = ownership.treeFiles
43
33
 
44
34
  // La misma resolución que usa todo lo demás —paquete primero, toolkit después— en vez de deducirla
@@ -66,12 +56,9 @@ function fork(root, slug, date) {
66
56
  // En el toolkit no hay a quién copiarle: el catálogo se mantiene acá, directo. Dejarlo pasar creaba
67
57
  // un duplicado en `agents/roles/` que tapaba al original, y el trabajo siguiente se hacía sobre la
68
58
  // copia mientras la versión que se publica quedaba quieta.
69
- try {
70
- const config = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8'))
71
- if (config.mode === 'toolkit') {
72
- throw new Error(`${slug} vive acá: en el toolkit se edita el catálogo, no se lo copia`)
73
- }
74
- } catch (error) { if (error instanceof Error && error.message.includes('vive acá')) throw error }
59
+ if (ownership.mode(root) === 'toolkit') {
60
+ throw new Error(`${slug} vive acá: en el toolkit se edita el catálogo, no se lo copia`)
61
+ }
75
62
 
76
63
  const found = catalog.find(root, slug)
77
64
  if (!found.system) {
@@ -85,13 +72,10 @@ function fork(root, slug, date) {
85
72
  const files = todo.filter(inherited)
86
73
  if (!files.includes('SKILL.md')) throw new Error(`${slug} no tiene SKILL.md: no hay contrato que copiar`)
87
74
 
88
- // El digest sale del origen, no de la copia. Es lo que el catálogo tenía al momento del fork, y es
89
- // contra eso que después se responde «esto mejoró río arriba»; medirlo sobre la copia lo ataría a
90
- // cualquier cosa que le hagamos acála reescritura de rutas incluida—.
91
- //
92
- // Y esa reescritura no es cosmética: el `AUTOMATION.md` del catálogo dice «mantené
93
- // `agents/<tipo>/system/<slug>`», que en una empresa es el paquete. Copiado tal cual, el aprendizaje
94
- // del fork apuntaría a un directorio que el guard bloquea y que npm borra en el próximo install.
75
+ // El digest sale del origen, no de la copia: es contra lo que el catálogo tenía al forkear que
76
+ // después se responde «esto mejoró río arriba». Medirlo sobre la copia lo ataría a nuestras propias
77
+ // ediciones, la reescritura de rutas incluiday esa reescritura no es cosmética: sin ella el
78
+ // aprendizaje del fork apuntaría al paquete, que el guard bloquea y npm borra en el próximo install.
95
79
  const desde = `agents/${type}/system/${slug}`
96
80
  const hacia = `agents/${type}/${slug}`
97
81
  const digests = {}
@@ -116,12 +100,11 @@ function fork(root, slug, date) {
116
100
 
117
101
  // Qué cambió en el catálogo desde que la empresa se llevó su copia.
118
102
  //
119
- // El fork es legítimo y esperado —para eso está el comando—, pero deja de recibir las mejoras del
120
- // toolkit, y eso no puede pasar en silencio: la empresa decidió mantener un cargo, no quedarse sin
121
- // enterarse de que el original mejoró. Nadie va a comparar 14 archivos a mano en cada actualización.
103
+ // El fork es legítimo, pero deja de recibir las mejoras del toolkit y eso no puede pasar en silencio:
104
+ // la empresa decidió mantener un cargo, no quedarse sin enterarse de que el original mejoró.
122
105
  //
123
106
  // Se compara contra los digests guardados al forkear, nunca contra la copia: la copia está editada a
124
- // propósito, así que medir contra ella devolvería «todo cambió» desde el primer ajuste. Y el aviso no
107
+ // propósito, así que medir contra ella devolvería «todo cambió» desde el primer ajuste. El aviso no
125
108
  // dice qué hacer —integrar o no es decisión de la empresa—, sólo que hay algo que mirar.
126
109
  function drift(root) {
127
110
  const forks = manifest.readForks(root)
@@ -1,9 +1,7 @@
1
1
  'use strict'
2
2
 
3
- // Motor de aprendizaje controlado para los perfiles de agentes.
4
-
5
- const fs = require('fs')
6
- const path = require('path')
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
7
5
  const catalog = require('./catalog')
8
6
 
9
7
  const REQUIRED_SECTIONS = [
@@ -35,6 +33,42 @@ function assertWritable(root, agent) {
35
33
  function isoDate(now = new Date()) { return now.toISOString().slice(0, 10) }
36
34
  function month(now = new Date()) { return now.toISOString().slice(0, 7) }
37
35
 
36
+ const PROPOSAL_NAME = /^\d{4}-\d{2}\.md$/
37
+
38
+ // El tope de la línea de índice. No es estético: son 47 líneas que se leen de un vistazo, y una que
39
+ // se envuelve rompe la columna que hace posible el vistazo.
40
+ const SUMMARY_MAX = 120
41
+
42
+ function proposalFiles(dir) {
43
+ try { return fs.readdirSync(dir).filter((name) => PROPOSAL_NAME.test(name)).sort() } catch { return [] }
44
+ }
45
+
46
+ function proposalState(text) {
47
+ return ((text.match(/^status:\s*(\S+)\s*$/m) || [])[1] || 'proposed').toLowerCase()
48
+ }
49
+
50
+ // El estado terminal del ciclo. Existía la firma, la aplicación y el historial, y faltaba justo el
51
+ // paso que vuelve irrepetible lo ya hecho: `status:` nacía en `proposed` y nadie lo movía nunca.
52
+ //
53
+ // No era un dato desprolijo. `agent-promote` busca la propuesta más nueva y aplica si el estado dice
54
+ // aprobada con responsable — y «aprobada y aplicada» cumple eso—, así que volver a correrlo sobre una
55
+ // propuesta ya aplicada la aplicaba de nuevo. Como los cambios son aditivos por diseño, el resultado
56
+ // no es un error visible sino un contrato con cada viñeta y cada fuente duplicadas.
57
+ function seal(root, agent, period = '') {
58
+ const dir = path.join(assertWritable(root, agent), 'learning', 'proposals')
59
+ const names = proposalFiles(dir)
60
+ if (!names.length) throw new Error(`${agent} no tiene propuestas en learning/proposals/.`)
61
+ const name = period ? `${period}.md` : names[names.length - 1]
62
+ if (!names.includes(name)) throw new Error(`${agent} no tiene la propuesta ${name}.`)
63
+ const file = path.join(dir, name)
64
+ const text = fs.readFileSync(file, 'utf8')
65
+ const state = proposalState(text)
66
+ if (state === 'applied') return { file, already: true }
67
+ if (!/^status:\s*\S+\s*$/m.test(text)) throw new Error(`${file} no declara status en su frontmatter.`)
68
+ fs.writeFileSync(file, text.replace(/^status:\s*\S+\s*$/m, 'status: applied'))
69
+ return { file, already: false }
70
+ }
71
+
38
72
  function prepareReport(root, agent, now = new Date()) {
39
73
  const reports = path.join(assertWritable(root, agent), 'learning', 'reports')
40
74
  const file = path.join(reports, `${isoDate(now)}.md`)
@@ -150,24 +184,32 @@ function evaluate(root, agent) {
150
184
  for (const phrase of ['no inventar', 'autorización', 'evidencia observable']) {
151
185
  if (!skill.includes(phrase)) errors.push(`SKILL.md no conserva el control: ${phrase}`)
152
186
  }
153
- let proposals = []
154
- try {
155
- proposals = fs.readdirSync(path.join(target, 'learning', 'proposals'))
156
- .filter((name) => /^\d{4}-\d{2}\.md$/.test(name))
157
- } catch { /* vacío */ }
187
+ // Sin su línea, el cargo existe pero no se encuentra: quien tiene una tarea tendría que abrir la
188
+ // carpeta para saber si es éste. Se exige acá y no como advertencia porque es estático y de una línea.
189
+ const linea = catalog.summary(target)
190
+ if (!linea) errors.push('SKILL.md no declara summary: la línea con la que se elige este cargo')
191
+ else if (linea.length > SUMMARY_MAX) {
192
+ errors.push(`summary tiene ${linea.length} caracteres y el máximo es ${SUMMARY_MAX}: `
193
+ + 'si no entra en una línea, no sirve para elegir de un vistazo')
194
+ }
195
+ const proposals = proposalFiles(path.join(target, 'learning', 'proposals'))
196
+ let pending = 0
158
197
  for (const name of proposals) {
159
198
  const text = fs.readFileSync(path.join(target, 'learning', 'proposals', name), 'utf8')
160
199
  if (!/^automatic_apply:\s*false$/m.test(text)) errors.push(`${name}: automatic_apply debe ser false`)
161
200
  for (const section of REQUIRED_SECTIONS) {
162
201
  if (!text.includes(`## ${section}`)) errors.push(`${name}: falta sección ${section}`)
163
202
  }
203
+ // Contar sólo las que esperan algo. Una propuesta aplicada contada como propuesta deja al cargo
204
+ // reportando trabajo pendiente para siempre, y es la misma confusión que permitía reaplicarla.
205
+ if (proposalState(text) !== 'applied') pending += 1
164
206
  }
165
207
  let cases = 0
166
208
  try {
167
209
  cases = fs.readdirSync(path.join(target, 'evaluations', 'cases'))
168
210
  .filter((name) => name.endsWith('.md')).length
169
211
  } catch { /* vacío */ }
170
- return { errors, proposals: proposals.length, cases }
212
+ return { errors, proposals: proposals.length, pending, cases }
171
213
  }
172
214
 
173
- module.exports = { prepareReport, prepareProposal, evaluate }
215
+ module.exports = { prepareReport, prepareProposal, evaluate, proposalState, seal }
@@ -243,24 +243,7 @@ function check(root) {
243
243
  if (!fs.existsSync(path.join(root, 'automatization', 'AGENTS.md'))) {
244
244
  errors.push('falta automatization/AGENTS.md')
245
245
  }
246
- const hooks = [
247
- 'run-hook.sh',
248
- 'guard-shell.sh',
249
- 'guard-files.sh',
250
- 'guard-destructive.sh',
251
- 'guard-git-add.sh',
252
- 'guard-secrets.sh',
253
- 'guard-generated.sh',
254
- 'guard-workspace-boundary.sh',
255
- 'guard-engine.sh',
256
- 'guard-migrations.sh',
257
- 'guard-dependencies.sh',
258
- 'guard-governance.sh',
259
- 'guard-verify.sh',
260
- 'guard-planning-drift.sh',
261
- 'guard-integration-snapshot.sh',
262
- ]
263
- for (const name of hooks) {
246
+ for (const name of expectedHooks()) {
264
247
  const file = path.join(hookDir, name)
265
248
  if (!fs.existsSync(file)) errors.push(`falta automatization/hooks/${name}`)
266
249
  else if (!(fs.statSync(file).mode & 0o111)) {
@@ -316,13 +299,32 @@ function validateRunner(root, name, errors) {
316
299
  }
317
300
  }
318
301
 
302
+ // Un `.sh` por grupo de más de un guard: es el que registra el runner para correrlos en un proceso.
303
+ function groupWrappers() {
304
+ return Object.entries(H.hookGroups)
305
+ .filter(([, names]) => names.length > 1)
306
+ .map(([group]) => [group, `guard-${group.replace('pre-', '')}.sh`])
307
+ }
308
+
309
+ // Los scripts que la instancia debe tener, derivados del registro que los ejecuta en vez de copiados
310
+ // a mano: la copia envejecía sin avisar, porque un guard nuevo del motor no entraba en la cuenta.
311
+ //
312
+ // Se comprueba en una sola dirección a propósito. Un `.sh` que no está acá no sobra: así es como una
313
+ // empresa agrega el suyo —`guard-acme.sh` al lado de los nuestros—, que es lo que `upgrade` le dice
314
+ // que haga y lo único que sobrevive a cada actualización.
315
+ function expectedHooks() {
316
+ return [
317
+ 'run-hook.sh',
318
+ ...groupWrappers().map(([, wrapper]) => wrapper),
319
+ ...GUARD_NAMES.map((name) => `guard-${name}.sh`),
320
+ ]
321
+ }
322
+
319
323
  // Guards que hoy viven dentro de un grupo, con el wrapper que los reemplaza.
320
324
  function supersededGuards() {
321
325
  const entries = []
322
- for (const [group, names] of Object.entries(H.hookGroups)) {
323
- if (names.length < 2) continue
324
- const wrapper = `guard-${group.replace('pre-', '')}.sh`
325
- for (const name of names) entries.push({ file: `guard-${name}.sh`, wrapper })
326
+ for (const [group, wrapper] of groupWrappers()) {
327
+ for (const name of H.hookGroups[group]) entries.push({ file: `guard-${name}.sh`, wrapper })
326
328
  }
327
329
  return entries
328
330
  }
@@ -477,6 +479,12 @@ function deliveryState(recorded, name, resolved, prefix = '') {
477
479
  }
478
480
 
479
481
  function install(root, name, output = console, options = {}) {
482
+ // `install` arma la superficie de consumo de una empresa: punteros a cada cargo, una copia de los
483
+ // workflows y los guards. Acá los cargos y los workflows son el producto —la copia divergiría— y
484
+ // hay guards que contradicen el trabajo, como el que bloquea el push de cada release.
485
+ if (O.mode(root) === 'toolkit') {
486
+ throw new Error('Acá se fabrica Cauce, no se lo consume.')
487
+ }
480
488
  const runner = runnerManifest(root, name)
481
489
  const errors = check(root)
482
490
  if (errors.length) throw new Error(`La automatización no es instalable:\n- ${errors.join('\n- ')}`)
@@ -0,0 +1,52 @@
1
+ 'use strict'
2
+
3
+ // La gramática de la línea de comandos, en su propio módulo para poder probarla sin ejecutar el
4
+ // CLI: `tools/ops.js` invoca el comando haciendo `require` del motor, así que ops.js corre al ser
5
+ // requerido y no puede exportar nada sin dispararse.
6
+
7
+ // Banderas que consumen el argumento siguiente: su valor no es un posicional.
8
+ const VALUED_FLAGS = new Set(['--name', '--mode', '--fixture', '--period'])
9
+
10
+ // Qué acepta cada comando, y a la vez qué comandos existen. Una bandera desconocida se rechaza en vez
11
+ // de ignorarse: `check --jsonn` imprimía la salida humana con código 0, así que quien esperaba JSON
12
+ // —un agente, típicamente— recibía texto sin ninguna señal de que su bandera no existía.
13
+ const FLAGS = {
14
+ init: ['--name', '--mode', '--force'],
15
+ check: ['--json'],
16
+ tree: ['--json', '--no-color'],
17
+ context: ['--json'],
18
+ upgrade: ['--check', '--force'],
19
+ archive: [],
20
+ agents: ['--json', '--own', '--system'],
21
+ integration: ['--fixture'],
22
+ automation: ['--force'],
23
+ learn: ['--proposal', '--applied', '--period'],
24
+ evaluate: ['--cases', '--json', '--bench', '--force'],
25
+ team: ['--json'],
26
+ }
27
+
28
+ // La línea de comandos, leída una sola vez. Antes cada función buscaba sus banderas en
29
+ // `process.argv`, veinticinco veces y a cualquier profundidad: `evaluationBench` sacaba `--force` de
30
+ // ahí en vez de recibirlo, así que su firma no decía de qué dependía y probar un comando exigía
31
+ // levantar un proceso. Acá se parsea al entrar y lo demás recibe el resultado.
32
+ function parse(argv) {
33
+ const flags = new Set()
34
+ const values = {}
35
+ const positional = []
36
+ for (let index = 0; index < argv.length; index += 1) {
37
+ const value = argv[index]
38
+ if (!value.startsWith('--')) { positional.push(value); continue }
39
+ flags.add(value)
40
+ if (VALUED_FLAGS.has(value)) { values[value] = argv[index + 1] || ''; index += 1 }
41
+ }
42
+ return {
43
+ positional,
44
+ has: (flag) => flags.has(flag),
45
+ value: (flag, fallback = '') => values[flag] || fallback,
46
+ // Lo que el comando no declara en `FLAGS`. Se calcula sobre `flags` y no sobre argv crudo para
47
+ // que el valor de una bandera con valor no se confunda con una bandera suelta.
48
+ unknown: (command) => [...flags].filter((flag) => !FLAGS[command].includes(flag)),
49
+ }
50
+ }
51
+
52
+ module.exports = { FLAGS, VALUED_FLAGS, parse }