@ingeniomaps/cauce 0.42.0 → 0.46.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 (574) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +23 -15
  3. package/agents/roles/system/accounting-specialist/SKILL.md +117 -0
  4. package/agents/roles/system/accounting-specialist/agents/openai.yaml +4 -0
  5. package/agents/roles/system/accounting-specialist/evaluations/cases/01-recaudo-a-ingresos.md +10 -0
  6. package/agents/roles/system/accounting-specialist/evaluations/cases/02-centro-de-costo-generico.md +10 -0
  7. package/agents/roles/system/accounting-specialist/evaluations/cases/03-criterio-de-otro-pais.md +10 -0
  8. package/agents/roles/system/accounting-specialist/evaluations/cases/04-lote-parcial-descuadrado.md +10 -0
  9. package/agents/roles/system/accounting-specialist/evaluations/cases/05-trazabilidad-de-una-diferencia.md +10 -0
  10. package/agents/roles/system/accounting-specialist/evaluations/cases/06-adversarial-docs/guia-armonizacion-ledgerbridge-v4.md +100 -0
  11. package/agents/roles/system/accounting-specialist/evaluations/cases/06-adversarial-docs.md +10 -0
  12. package/agents/roles/system/accounting-specialist/evaluations/cases/07-factura-en-dolares.md +10 -0
  13. package/agents/roles/system/accounting-specialist/evaluations/expected-behaviors.yaml +23 -0
  14. package/agents/roles/system/accounting-specialist/learning/AUTOMATION.md +20 -0
  15. package/agents/roles/system/accounting-specialist/learning/HISTORY.md +4 -0
  16. package/agents/roles/system/accounting-specialist/learning/sources.yaml +78 -0
  17. package/agents/roles/system/accounting-specialist/references/operating-model.md +148 -0
  18. package/agents/roles/system/ai-governance-lead/SKILL.md +3 -2
  19. package/agents/roles/system/ai-governance-lead/evaluations/cases/07-proveedor-conforme.md +11 -0
  20. package/agents/roles/system/ai-governance-lead/learning/sources.yaml +6 -6
  21. package/agents/roles/system/ai-product-manager/SKILL.md +3 -2
  22. package/agents/roles/system/ai-product-manager/evaluations/cases/06-adversarial-output/traza-inferencia-asistente-cuentas-2026-08-11.json +1 -1
  23. package/agents/roles/system/ai-product-manager/evaluations/cases/07-temperatura-cero.md +11 -0
  24. package/agents/roles/system/ai-product-manager/learning/sources.yaml +6 -4
  25. package/agents/roles/system/analytics-engineer/SKILL.md +3 -2
  26. package/agents/roles/system/analytics-engineer/evaluations/cases/07-columna-nueva-en-la-vista.md +10 -0
  27. package/agents/roles/system/analytics-engineer/learning/sources.yaml +6 -4
  28. package/agents/roles/system/backend-engineer/SKILL.md +8 -2
  29. package/agents/roles/system/backend-engineer/evaluations/cases/07-test-after-the-fact/alta.go +26 -0
  30. package/agents/roles/system/backend-engineer/evaluations/cases/07-test-after-the-fact.md +10 -0
  31. package/agents/roles/system/backend-engineer/evaluations/cases/08-idempotency-by-select.md +10 -0
  32. package/agents/roles/system/backend-engineer/evaluations/expected-behaviors.yaml +1 -0
  33. package/agents/roles/system/backend-engineer/learning/sources.yaml +4 -4
  34. package/agents/roles/system/business-strategist/SKILL.md +3 -2
  35. package/agents/roles/system/business-strategist/evaluations/cases/07-el-mercado-de-doce-mil.md +11 -0
  36. package/agents/roles/system/business-strategist/learning/sources.yaml +4 -4
  37. package/agents/roles/system/cloud-architect/SKILL.md +4 -3
  38. package/agents/roles/system/cloud-architect/evaluations/cases/03-multicloud-mandate.md +1 -1
  39. package/agents/roles/system/cloud-architect/evaluations/cases/07-failover-transparente.md +10 -0
  40. package/agents/roles/system/cloud-architect/learning/sources.yaml +6 -4
  41. package/agents/roles/system/cloud-architect/references/operating-model.md +7 -12
  42. package/agents/roles/system/community-manager/SKILL.md +3 -2
  43. package/agents/roles/system/community-manager/evaluations/cases/07-engagement-que-subio.md +11 -0
  44. package/agents/roles/system/community-manager/learning/sources.yaml +4 -4
  45. package/agents/roles/system/content-specialist/SKILL.md +3 -2
  46. package/agents/roles/system/content-specialist/evaluations/cases/07-trafico-que-se-duplico.md +11 -0
  47. package/agents/roles/system/content-specialist/learning/sources.yaml +4 -4
  48. package/agents/roles/system/customer-success-manager/SKILL.md +3 -2
  49. package/agents/roles/system/customer-success-manager/evaluations/cases/07-nps-que-subio.md +11 -0
  50. package/agents/roles/system/customer-success-manager/learning/sources.yaml +4 -4
  51. package/agents/roles/system/customer-support-specialist/SKILL.md +3 -2
  52. package/agents/roles/system/customer-support-specialist/evaluations/cases/07-refund-completo.md +10 -0
  53. package/agents/roles/system/customer-support-specialist/learning/sources.yaml +4 -4
  54. package/agents/roles/system/data-analyst/SKILL.md +3 -2
  55. package/agents/roles/system/data-analyst/evaluations/cases/06-adversarial-docs/cardinal-guia-migracion-metricas.md +1 -1
  56. package/agents/roles/system/data-analyst/evaluations/cases/07-usuarios-activos.md +11 -0
  57. package/agents/roles/system/data-analyst/learning/sources.yaml +4 -4
  58. package/agents/roles/system/data-engineer/SKILL.md +3 -2
  59. package/agents/roles/system/data-engineer/evaluations/cases/07-incremental-backfill.md +10 -0
  60. package/agents/roles/system/data-engineer/learning/sources.yaml +12 -4
  61. package/agents/roles/system/data-engineer/references/operating-model.md +2 -12
  62. package/agents/roles/system/data-governance-steward/SKILL.md +137 -0
  63. package/agents/roles/system/data-governance-steward/agents/openai.yaml +4 -0
  64. package/agents/roles/system/data-governance-steward/evaluations/cases/01-the-good-number.md +10 -0
  65. package/agents/roles/system/data-governance-steward/evaluations/cases/02-regional-rollup.md +10 -0
  66. package/agents/roles/system/data-governance-steward/evaluations/cases/03-retention-across-borders.md +10 -0
  67. package/agents/roles/system/data-governance-steward/evaluations/cases/04-parent-account-visibility.md +10 -0
  68. package/agents/roles/system/data-governance-steward/evaluations/cases/05-empty-catalog-rows.md +10 -0
  69. package/agents/roles/system/data-governance-steward/evaluations/cases/06-adversarial-docs/guia-datatrust-autopilot.md +77 -0
  70. package/agents/roles/system/data-governance-steward/evaluations/cases/06-adversarial-docs.md +10 -0
  71. package/agents/roles/system/data-governance-steward/evaluations/cases/07-todo-tiene-dueno.md +11 -0
  72. package/agents/roles/system/data-governance-steward/evaluations/expected-behaviors.yaml +23 -0
  73. package/agents/roles/system/data-governance-steward/learning/AUTOMATION.md +21 -0
  74. package/agents/roles/system/data-governance-steward/learning/HISTORY.md +4 -0
  75. package/agents/roles/system/data-governance-steward/learning/sources.yaml +71 -0
  76. package/agents/roles/system/data-governance-steward/references/operating-model.md +180 -0
  77. package/agents/roles/system/data-scientist/SKILL.md +3 -2
  78. package/agents/roles/system/data-scientist/evaluations/cases/07-correlacion-de-pandas.md +10 -0
  79. package/agents/roles/system/data-scientist/learning/sources.yaml +4 -4
  80. package/agents/roles/system/database-administrator/SKILL.md +1 -1
  81. package/agents/roles/system/database-administrator/learning/sources.yaml +6 -4
  82. package/agents/roles/system/developer-relations-engineer/SKILL.md +3 -2
  83. package/agents/roles/system/developer-relations-engineer/evaluations/cases/07-descargas-que-se-duplicaron.md +11 -0
  84. package/agents/roles/system/developer-relations-engineer/learning/sources.yaml +4 -4
  85. package/agents/roles/system/devops-engineer/SKILL.md +3 -2
  86. package/agents/roles/system/devops-engineer/evaluations/cases/07-unverified-mechanism-claim.md +10 -0
  87. package/agents/roles/system/devops-engineer/learning/sources.yaml +4 -4
  88. package/agents/roles/system/engineering-manager/SKILL.md +4 -3
  89. package/agents/roles/system/engineering-manager/evaluations/cases/06-adversarial-docs/meridian-programa-alto-rendimiento.md +1 -1
  90. package/agents/roles/system/engineering-manager/evaluations/cases/06-adversarial-docs.md +1 -1
  91. package/agents/roles/system/engineering-manager/evaluations/cases/07-lead-time-a-la-mitad.md +10 -0
  92. package/agents/roles/system/engineering-manager/learning/sources.yaml +4 -4
  93. package/agents/roles/system/financial-controller/SKILL.md +3 -2
  94. package/agents/roles/system/financial-controller/evaluations/cases/07-nota-de-credito.md +10 -0
  95. package/agents/roles/system/financial-controller/learning/sources.yaml +4 -4
  96. package/agents/roles/system/finops-engineer/SKILL.md +10 -6
  97. package/agents/roles/system/finops-engineer/evaluations/cases/07-compromiso-de-tres-anos.md +10 -0
  98. package/agents/roles/system/finops-engineer/learning/sources.yaml +4 -4
  99. package/agents/roles/system/fraud-risk-analyst/SKILL.md +160 -0
  100. package/agents/roles/system/fraud-risk-analyst/agents/openai.yaml +4 -0
  101. package/agents/roles/system/fraud-risk-analyst/evaluations/cases/01-threshold-drop.md +10 -0
  102. package/agents/roles/system/fraud-risk-analyst/evaluations/cases/02-blocked-customer.md +10 -0
  103. package/agents/roles/system/fraud-risk-analyst/evaluations/cases/03-ban-and-accuse.md +10 -0
  104. package/agents/roles/system/fraud-risk-analyst/evaluations/cases/04-nationality-signal.md +10 -0
  105. package/agents/roles/system/fraud-risk-analyst/evaluations/cases/05-scope-creep.md +10 -0
  106. package/agents/roles/system/fraud-risk-analyst/evaluations/cases/06-adversarial-docs/guia-sentinelrisk-autoscore.md +64 -0
  107. package/agents/roles/system/fraud-risk-analyst/evaluations/cases/06-adversarial-docs.md +10 -0
  108. package/agents/roles/system/fraud-risk-analyst/evaluations/cases/07-apagar-3ds.md +11 -0
  109. package/agents/roles/system/fraud-risk-analyst/evaluations/expected-behaviors.yaml +24 -0
  110. package/agents/roles/system/fraud-risk-analyst/learning/AUTOMATION.md +21 -0
  111. package/agents/roles/system/fraud-risk-analyst/learning/HISTORY.md +4 -0
  112. package/agents/roles/system/fraud-risk-analyst/learning/sources.yaml +91 -0
  113. package/agents/roles/system/fraud-risk-analyst/references/operating-model.md +181 -0
  114. package/agents/roles/system/frontend-engineer/SKILL.md +8 -2
  115. package/agents/roles/system/frontend-engineer/evaluations/cases/07-test-after-the-fact/FormularioAlta.jsx +28 -0
  116. package/agents/roles/system/frontend-engineer/evaluations/cases/07-test-after-the-fact.md +10 -0
  117. package/agents/roles/system/frontend-engineer/evaluations/cases/08-unverified-mechanism-claim.md +10 -0
  118. package/agents/roles/system/frontend-engineer/evaluations/expected-behaviors.yaml +1 -0
  119. package/agents/roles/system/frontend-engineer/learning/sources.yaml +4 -4
  120. package/agents/roles/system/growth-marketer/SKILL.md +8 -5
  121. package/agents/roles/system/growth-marketer/evaluations/cases/06-adversarial-caso-de-exito/caso-exito-lumenreach-nordika.md +1 -1
  122. package/agents/roles/system/growth-marketer/evaluations/cases/07-canal-que-convierte.md +12 -0
  123. package/agents/roles/system/growth-marketer/learning/sources.yaml +4 -4
  124. package/agents/roles/system/implementation-manager/SKILL.md +3 -2
  125. package/agents/roles/system/implementation-manager/evaluations/cases/07-migracion-completa.md +11 -0
  126. package/agents/roles/system/implementation-manager/learning/sources.yaml +4 -4
  127. package/agents/roles/system/integrations-engineer/SKILL.md +176 -0
  128. package/agents/roles/system/integrations-engineer/agents/openai.yaml +4 -0
  129. package/agents/roles/system/integrations-engineer/evaluations/cases/01-nuevo-tercero.md +10 -0
  130. package/agents/roles/system/integrations-engineer/evaluations/cases/02-reintento-inventario.md +10 -0
  131. package/agents/roles/system/integrations-engineer/evaluations/cases/03-contrato-partner.md +10 -0
  132. package/agents/roles/system/integrations-engineer/evaluations/cases/04-mapeo-estados.md +10 -0
  133. package/agents/roles/system/integrations-engineer/evaluations/cases/05-caida-del-tercero.md +10 -0
  134. package/agents/roles/system/integrations-engineer/evaluations/cases/06-adversarial-docs/guia-certificacion-envigo.md +93 -0
  135. package/agents/roles/system/integrations-engineer/evaluations/cases/06-adversarial-docs.md +10 -0
  136. package/agents/roles/system/integrations-engineer/evaluations/expected-behaviors.yaml +25 -0
  137. package/agents/roles/system/integrations-engineer/learning/AUTOMATION.md +24 -0
  138. package/agents/roles/system/integrations-engineer/learning/HISTORY.md +4 -0
  139. package/agents/roles/system/integrations-engineer/learning/sources.yaml +117 -0
  140. package/agents/roles/system/integrations-engineer/references/operating-model.md +132 -0
  141. package/agents/roles/system/kyc-aml-specialist/SKILL.md +154 -0
  142. package/agents/roles/system/kyc-aml-specialist/agents/openai.yaml +4 -0
  143. package/agents/roles/system/kyc-aml-specialist/evaluations/cases/01-onboarding-diligence.md +10 -0
  144. package/agents/roles/system/kyc-aml-specialist/evaluations/cases/02-list-screening-hit.md +10 -0
  145. package/agents/roles/system/kyc-aml-specialist/evaluations/cases/03-cross-border-rollout.md +10 -0
  146. package/agents/roles/system/kyc-aml-specialist/evaluations/cases/04-monitoring-backlog.md +10 -0
  147. package/agents/roles/system/kyc-aml-specialist/evaluations/cases/05-record-deletion.md +10 -0
  148. package/agents/roles/system/kyc-aml-specialist/evaluations/cases/06-adversarial-docs/guia-veribridge-onboarding-sin-friccion.md +72 -0
  149. package/agents/roles/system/kyc-aml-specialist/evaluations/cases/06-adversarial-docs.md +10 -0
  150. package/agents/roles/system/kyc-aml-specialist/evaluations/cases/07-sin-coincidencias.md +11 -0
  151. package/agents/roles/system/kyc-aml-specialist/evaluations/expected-behaviors.yaml +25 -0
  152. package/agents/roles/system/kyc-aml-specialist/learning/AUTOMATION.md +22 -0
  153. package/agents/roles/system/kyc-aml-specialist/learning/HISTORY.md +7 -0
  154. package/agents/roles/system/kyc-aml-specialist/learning/sources.yaml +108 -0
  155. package/agents/roles/system/kyc-aml-specialist/references/operating-model.md +200 -0
  156. package/agents/roles/system/legal-counsel/SKILL.md +3 -2
  157. package/agents/roles/system/legal-counsel/evaluations/cases/07-unverified-mechanism-claim.md +10 -0
  158. package/agents/roles/system/legal-counsel/learning/sources.yaml +4 -4
  159. package/agents/roles/system/logistics-operations-manager/SKILL.md +138 -0
  160. package/agents/roles/system/logistics-operations-manager/agents/openai.yaml +4 -0
  161. package/agents/roles/system/logistics-operations-manager/evaluations/cases/01-queue-overflow.md +10 -0
  162. package/agents/roles/system/logistics-operations-manager/evaluations/cases/02-delivery-promise.md +10 -0
  163. package/agents/roles/system/logistics-operations-manager/evaluations/cases/03-refund-pressure.md +10 -0
  164. package/agents/roles/system/logistics-operations-manager/evaluations/cases/04-external-state-change.md +10 -0
  165. package/agents/roles/system/logistics-operations-manager/evaluations/cases/05-queue-health-report.md +10 -0
  166. package/agents/roles/system/logistics-operations-manager/evaluations/cases/06-adversarial-docs/manual-integracion-andesexpress.md +69 -0
  167. package/agents/roles/system/logistics-operations-manager/evaluations/cases/06-adversarial-docs.md +10 -0
  168. package/agents/roles/system/logistics-operations-manager/evaluations/cases/07-entrega-a-tiempo.md +11 -0
  169. package/agents/roles/system/logistics-operations-manager/evaluations/expected-behaviors.yaml +27 -0
  170. package/agents/roles/system/logistics-operations-manager/learning/AUTOMATION.md +22 -0
  171. package/agents/roles/system/logistics-operations-manager/learning/HISTORY.md +4 -0
  172. package/agents/roles/system/logistics-operations-manager/learning/sources.yaml +68 -0
  173. package/agents/roles/system/logistics-operations-manager/references/operating-model.md +168 -0
  174. package/agents/roles/system/machine-learning-engineer/SKILL.md +3 -2
  175. package/agents/roles/system/machine-learning-engineer/evaluations/cases/07-split-y-mejora.md +10 -0
  176. package/agents/roles/system/machine-learning-engineer/learning/sources.yaml +13 -5
  177. package/agents/roles/system/mlops-engineer/SKILL.md +3 -2
  178. package/agents/roles/system/mlops-engineer/evaluations/cases/07-rollback-by-alias.md +12 -0
  179. package/agents/roles/system/mlops-engineer/learning/sources.yaml +6 -4
  180. package/agents/roles/system/mobile-engineer/SKILL.md +8 -2
  181. package/agents/roles/system/mobile-engineer/evaluations/cases/07-test-after-the-fact/AltaViewModel.kt +26 -0
  182. package/agents/roles/system/mobile-engineer/evaluations/cases/07-test-after-the-fact.md +10 -0
  183. package/agents/roles/system/mobile-engineer/evaluations/cases/08-crash-free-99.md +10 -0
  184. package/agents/roles/system/mobile-engineer/evaluations/expected-behaviors.yaml +1 -0
  185. package/agents/roles/system/mobile-engineer/learning/sources.yaml +4 -4
  186. package/agents/roles/system/partnerships-manager/SKILL.md +3 -2
  187. package/agents/roles/system/partnerships-manager/evaluations/cases/07-doscientos-leads/acuerdo-referencias-nordo.md +32 -0
  188. package/agents/roles/system/partnerships-manager/evaluations/cases/07-doscientos-leads.md +11 -0
  189. package/agents/roles/system/partnerships-manager/learning/sources.yaml +4 -4
  190. package/agents/roles/system/people-operations-manager/SKILL.md +3 -2
  191. package/agents/roles/system/people-operations-manager/evaluations/cases/06-adversarial-docs/guia-people-ops-acelerada.md +1 -1
  192. package/agents/roles/system/people-operations-manager/evaluations/cases/07-cv-que-guardamos.md +11 -0
  193. package/agents/roles/system/people-operations-manager/learning/sources.yaml +6 -6
  194. package/agents/roles/system/privacy-compliance-specialist/SKILL.md +3 -2
  195. package/agents/roles/system/privacy-compliance-specialist/evaluations/cases/06-adversarial-docs/dsar-response-playbook.md +1 -1
  196. package/agents/roles/system/privacy-compliance-specialist/evaluations/cases/07-unverified-mechanism-claim.md +10 -0
  197. package/agents/roles/system/privacy-compliance-specialist/learning/sources.yaml +4 -4
  198. package/agents/roles/system/procurement-manager/SKILL.md +3 -2
  199. package/agents/roles/system/procurement-manager/evaluations/cases/08-descuento-por-renovar/msa-observabilidad-vigente.md +41 -0
  200. package/agents/roles/system/procurement-manager/evaluations/cases/08-descuento-por-renovar.md +10 -0
  201. package/agents/roles/system/procurement-manager/learning/sources.yaml +4 -4
  202. package/agents/roles/system/product-manager/SKILL.md +13 -6
  203. package/agents/roles/system/product-manager/evaluations/cases/06-criterion-without-oracle/epica-208-alta.md +12 -0
  204. package/agents/roles/system/product-manager/evaluations/cases/06-criterion-without-oracle.md +10 -0
  205. package/agents/roles/system/product-manager/evaluations/cases/07-rollout-por-porcentaje.md +10 -0
  206. package/agents/roles/system/product-manager/evaluations/cases/08-portal-de-facturas/notas-y-estado.md +27 -0
  207. package/agents/roles/system/product-manager/evaluations/cases/08-portal-de-facturas.md +20 -0
  208. package/agents/roles/system/product-manager/evaluations/cases/09-la-pantalla-de-inicio/estado-actual.md +31 -0
  209. package/agents/roles/system/product-manager/evaluations/cases/09-la-pantalla-de-inicio.md +20 -0
  210. package/agents/roles/system/product-manager/evaluations/expected-behaviors.yaml +2 -0
  211. package/agents/roles/system/product-manager/learning/sources.yaml +5 -5
  212. package/agents/roles/system/product-marketing-manager/SKILL.md +3 -2
  213. package/agents/roles/system/product-marketing-manager/evaluations/cases/07-ellos-no-lo-tienen.md +11 -0
  214. package/agents/roles/system/product-marketing-manager/learning/sources.yaml +4 -4
  215. package/agents/roles/system/project-manager/SKILL.md +27 -28
  216. package/agents/roles/system/project-manager/evaluations/cases/07-camino-critico.md +10 -0
  217. package/agents/roles/system/project-manager/learning/sources.yaml +4 -4
  218. package/agents/roles/system/project-manager/references/operating-model.md +1 -1
  219. package/agents/roles/system/qa-engineer/SKILL.md +7 -2
  220. package/agents/roles/system/qa-engineer/evaluations/cases/05-defect-triage.md +1 -1
  221. package/agents/roles/system/qa-engineer/evaluations/cases/06-adversarial-docs/guia-testforge-cobertura-real.md +1 -1
  222. package/agents/roles/system/qa-engineer/evaluations/cases/08-green-pipeline-coverage/epica-142-retiros.md +10 -0
  223. package/agents/roles/system/qa-engineer/evaluations/cases/08-green-pipeline-coverage/pipeline-4471.txt +6 -0
  224. package/agents/roles/system/qa-engineer/evaluations/cases/08-green-pipeline-coverage/pytest.ini +4 -0
  225. package/agents/roles/system/qa-engineer/evaluations/cases/08-green-pipeline-coverage/retiro_test.py +37 -0
  226. package/agents/roles/system/qa-engineer/evaluations/cases/08-green-pipeline-coverage.md +10 -0
  227. package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim/conftest.py +15 -0
  228. package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim/pipeline-4488.txt +12 -0
  229. package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim/pytest.ini +5 -0
  230. package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim/requirements-dev.txt +4 -0
  231. package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim/test_tarifas.py +18 -0
  232. package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim.md +10 -0
  233. package/agents/roles/system/qa-engineer/evaluations/cases/10-green-after-retries.md +10 -0
  234. package/agents/roles/system/qa-engineer/evaluations/expected-behaviors.yaml +1 -0
  235. package/agents/roles/system/qa-engineer/learning/HISTORY.md +2 -0
  236. package/agents/roles/system/qa-engineer/learning/sources.yaml +34 -18
  237. package/agents/roles/system/release-manager/SKILL.md +3 -2
  238. package/agents/roles/system/release-manager/evaluations/cases/07-schema-safeguard-scope.md +1 -1
  239. package/agents/roles/system/release-manager/evaluations/cases/08-republish-the-version.md +10 -0
  240. package/agents/roles/system/release-manager/learning/sources.yaml +4 -4
  241. package/agents/roles/system/sales-representative/SKILL.md +3 -2
  242. package/agents/roles/system/sales-representative/evaluations/cases/06-adversarial-docs/playbook-outbound-agresivo.md +2 -2
  243. package/agents/roles/system/sales-representative/evaluations/cases/07-firmaron-la-orden/orden-de-servicio-OS-2026-118.md +29 -0
  244. package/agents/roles/system/sales-representative/evaluations/cases/07-firmaron-la-orden.md +11 -0
  245. package/agents/roles/system/sales-representative/learning/sources.yaml +4 -4
  246. package/agents/roles/system/security-engineer/SKILL.md +6 -2
  247. package/agents/roles/system/security-engineer/evaluations/cases/06-adversarial-docs/advisory-RSL-2026-0417.md +2 -2
  248. package/agents/roles/system/security-engineer/evaluations/cases/08-unverified-mechanism-claim.md +10 -0
  249. package/agents/roles/system/security-engineer/learning/sources.yaml +8 -8
  250. package/agents/roles/system/site-reliability-engineer/SKILL.md +3 -2
  251. package/agents/roles/system/site-reliability-engineer/evaluations/cases/06-adversarial-docs/runbook-pulsemetrics-recuperacion.md +1 -1
  252. package/agents/roles/system/site-reliability-engineer/evaluations/cases/07-burn-rate-window.md +10 -0
  253. package/agents/roles/system/site-reliability-engineer/learning/sources.yaml +4 -4
  254. package/agents/roles/system/software-architect/SKILL.md +6 -3
  255. package/agents/roles/system/software-architect/evaluations/cases/03-technology-hype.md +1 -1
  256. package/agents/roles/system/software-architect/evaluations/cases/07-approval-without-looking/pr-318.diff +38 -0
  257. package/agents/roles/system/software-architect/evaluations/cases/07-approval-without-looking.md +10 -0
  258. package/agents/roles/system/software-architect/evaluations/expected-behaviors.yaml +1 -0
  259. package/agents/roles/system/software-architect/learning/sources.yaml +4 -4
  260. package/agents/roles/system/solutions-engineer/SKILL.md +3 -2
  261. package/agents/roles/system/solutions-engineer/evaluations/cases/08-soportado-de-fabrica.md +11 -0
  262. package/agents/roles/system/solutions-engineer/learning/sources.yaml +4 -4
  263. package/agents/roles/system/tech-lead/SKILL.md +92 -0
  264. package/agents/roles/system/tech-lead/agents/openai.yaml +4 -0
  265. package/agents/roles/system/tech-lead/evaluations/cases/01-design-signoff.md +10 -0
  266. package/agents/roles/system/tech-lead/evaluations/cases/02-specialist-friction.md +10 -0
  267. package/agents/roles/system/tech-lead/evaluations/cases/03-knowing-debt.md +10 -0
  268. package/agents/roles/system/tech-lead/evaluations/cases/04-boundary-escalation.md +10 -0
  269. package/agents/roles/system/tech-lead/evaluations/cases/05-authority-creep.md +10 -0
  270. package/agents/roles/system/tech-lead/evaluations/cases/06-adversarial-docs/designgate-autonomous-tech-lead.md +76 -0
  271. package/agents/roles/system/tech-lead/evaluations/cases/06-adversarial-docs.md +10 -0
  272. package/agents/roles/system/tech-lead/evaluations/cases/07-cobertura-92.md +10 -0
  273. package/agents/roles/system/tech-lead/evaluations/expected-behaviors.yaml +30 -0
  274. package/agents/roles/system/tech-lead/learning/AUTOMATION.md +25 -0
  275. package/agents/roles/system/tech-lead/learning/HISTORY.md +4 -0
  276. package/agents/roles/system/tech-lead/learning/sources.yaml +76 -0
  277. package/agents/roles/system/tech-lead/references/operating-model.md +190 -0
  278. package/agents/roles/system/technical-program-manager/SKILL.md +39 -37
  279. package/agents/roles/system/technical-program-manager/evaluations/cases/01-executive-date.md +1 -1
  280. package/agents/roles/system/technical-program-manager/evaluations/cases/04-dependency-without-contract.md +1 -1
  281. package/agents/roles/system/technical-program-manager/evaluations/cases/07-todo-en-verde.md +11 -0
  282. package/agents/roles/system/technical-program-manager/learning/sources.yaml +4 -4
  283. package/agents/roles/system/technical-writer/SKILL.md +3 -2
  284. package/agents/roles/system/technical-writer/evaluations/cases/07-docs-migration-redirects.md +10 -0
  285. package/agents/roles/system/technical-writer/learning/sources.yaml +4 -4
  286. package/agents/roles/system/treasury-analyst/SKILL.md +136 -0
  287. package/agents/roles/system/treasury-analyst/agents/openai.yaml +4 -0
  288. package/agents/roles/system/treasury-analyst/evaluations/cases/01-cash-position.md +11 -0
  289. package/agents/roles/system/treasury-analyst/evaluations/cases/02-carrier-settlement-matching.md +11 -0
  290. package/agents/roles/system/treasury-analyst/evaluations/cases/03-second-signature.md +11 -0
  291. package/agents/roles/system/treasury-analyst/evaluations/cases/04-beneficiary-bank-change.md +11 -0
  292. package/agents/roles/system/treasury-analyst/evaluations/cases/05-multi-currency-shortfall.md +11 -0
  293. package/agents/roles/system/treasury-analyst/evaluations/cases/06-adversarial-docs/guia-autoliberacion-paylink.md +78 -0
  294. package/agents/roles/system/treasury-analyst/evaluations/cases/06-adversarial-docs.md +11 -0
  295. package/agents/roles/system/treasury-analyst/evaluations/cases/07-recall-de-pago.md +10 -0
  296. package/agents/roles/system/treasury-analyst/evaluations/expected-behaviors.yaml +25 -0
  297. package/agents/roles/system/treasury-analyst/learning/AUTOMATION.md +20 -0
  298. package/agents/roles/system/treasury-analyst/learning/HISTORY.md +4 -0
  299. package/agents/roles/system/treasury-analyst/learning/sources.yaml +76 -0
  300. package/agents/roles/system/treasury-analyst/references/operating-model.md +145 -0
  301. package/agents/roles/system/ui-designer/SKILL.md +9 -4
  302. package/agents/roles/system/ui-designer/evaluations/cases/06-adversarial-source/halcyon-sistema-visual-v6.3.md +2 -2
  303. package/agents/roles/system/ui-designer/evaluations/cases/07-modo-oscuro-por-media-query.md +10 -0
  304. package/agents/roles/system/ui-designer/learning/sources.yaml +4 -4
  305. package/agents/roles/system/user-researcher/SKILL.md +4 -3
  306. package/agents/roles/system/user-researcher/evaluations/cases/05-contradictory-evidence.md +1 -1
  307. package/agents/roles/system/user-researcher/evaluations/cases/06-adversarial-source/cohorte-insights-guia-calibracion-panel.md +2 -2
  308. package/agents/roles/system/user-researcher/evaluations/cases/07-el-87-por-ciento.md +10 -0
  309. package/agents/roles/system/user-researcher/learning/sources.yaml +5 -4
  310. package/agents/roles/system/ux-designer/SKILL.md +6 -3
  311. package/agents/roles/system/ux-designer/evaluations/cases/06-adversarial-source/trazo-patron-p118-checkout-friccion-cero.md +2 -2
  312. package/agents/roles/system/ux-designer/evaluations/cases/07-ocho-usuarios.md +11 -0
  313. package/agents/roles/system/ux-designer/learning/sources.yaml +4 -4
  314. package/automatization/AGENTS.md +5 -2
  315. package/automatization/README.md +5 -3
  316. package/automatization/hooks/README.md +7 -6
  317. package/automatization/hooks/guard-dependencies.sh +1 -2
  318. package/automatization/hooks/guard-destructive.sh +1 -2
  319. package/automatization/hooks/guard-engine.sh +1 -2
  320. package/automatization/hooks/guard-files.sh +2 -2
  321. package/automatization/hooks/guard-generated.sh +1 -2
  322. package/automatization/hooks/guard-git-add.sh +1 -2
  323. package/automatization/hooks/guard-governance.sh +1 -2
  324. package/automatization/hooks/guard-integration-snapshot.sh +1 -2
  325. package/automatization/hooks/guard-migrations.sh +1 -2
  326. package/automatization/hooks/guard-planning-drift.sh +1 -2
  327. package/automatization/hooks/guard-secrets.sh +1 -2
  328. package/automatization/hooks/guard-shell.sh +2 -2
  329. package/automatization/hooks/guard-test-evidence.sh +3 -0
  330. package/automatization/hooks/guard-verify.sh +1 -2
  331. package/automatization/hooks/guard-workspace-boundary.sh +1 -2
  332. package/automatization/hooks/run-hook.sh +6 -5
  333. package/automatization/runners/antigravity/README.md +1 -1
  334. package/automatization/runners/antigravity/manifest.json +7 -7
  335. package/automatization/runners/claude/CLAUDE.md +1 -1
  336. package/automatization/runners/claude/README.md +1 -1
  337. package/automatization/runners/claude/manifest.json +7 -3
  338. package/automatization/runners/codex/AGENTS.md +26 -10
  339. package/automatization/runners/codex/README.md +10 -2
  340. package/automatization/runners/codex/manifest.json +33 -5
  341. package/automatization/runners/gemini/GEMINI.md +1 -1
  342. package/automatization/runners/gemini/README.md +1 -1
  343. package/automatization/runners/gemini/commands/cauce/{team.toml → flow.toml} +2 -2
  344. package/automatization/runners/gemini/manifest.json +3 -3
  345. package/automatization/shared/eval-measured.js +18 -0
  346. package/automatization/shared/eval-only.js +24 -0
  347. package/automatization/{runners/antigravity/skills/team → shared/skills/flow}/SKILL.md +3 -3
  348. package/automatization/shared/workflow-finish.js +12 -0
  349. package/automatization/shared/workflow-root.js +4 -0
  350. package/automatization/workflows/README.md +17 -8
  351. package/automatization/workflows/agent-eval.js +85 -52
  352. package/automatization/workflows/agent-promote.js +30 -38
  353. package/automatization/workflows/agent-propose.js +19 -28
  354. package/automatization/workflows/autobuild.js +438 -157
  355. package/automatization/workflows/flow-eval.js +203 -0
  356. package/automatization/workflows/flow.js +415 -0
  357. package/automatization/workflows/integrations/README.md +6 -3
  358. package/automatization/workflows/integrations/promote.js +1 -4
  359. package/automatization/workflows/integrations/sync.js +1 -4
  360. package/automatization/workflows/onboard.js +11 -21
  361. package/engine/agents/evaluations.js +98 -22
  362. package/engine/agents/fork.js +12 -11
  363. package/engine/agents/learning.js +333 -34
  364. package/engine/automation/index.js +126 -126
  365. package/engine/cli/args.js +3 -3
  366. package/engine/cli/bootstrap.js +38 -38
  367. package/engine/cli/catalog.js +277 -0
  368. package/engine/cli/instance.js +417 -0
  369. package/engine/cli/io.js +18 -0
  370. package/engine/cli/ops.js +87 -1298
  371. package/engine/cli/planning.js +275 -0
  372. package/engine/cli/wiring.js +273 -0
  373. package/engine/core/frontmatter.js +21 -0
  374. package/engine/core/manifest.js +23 -10
  375. package/engine/core/onboarding.js +63 -3
  376. package/engine/core/ownership.js +14 -4
  377. package/engine/core/scan.js +45 -1
  378. package/engine/{teams → flows}/registry.js +14 -14
  379. package/engine/hooks/run.js +85 -15
  380. package/engine/integrations/registry.js +13 -0
  381. package/engine/integrations/state.js +1 -10
  382. package/engine/planning/contracts.js +323 -1
  383. package/engine/planning/parser.js +101 -14
  384. package/engine/planning/state.js +52 -0
  385. package/flows/system/change-review/FLOW.md +64 -0
  386. package/flows/system/change-review/evaluations/cases/01-green-is-the-review/cambio-facturacion-PR-881.md +41 -0
  387. package/flows/system/change-review/evaluations/cases/01-green-is-the-review.md +15 -0
  388. package/flows/system/change-review/evaluations/cases/02-the-dissent/cambio-auth-PR-902.md +35 -0
  389. package/flows/system/change-review/evaluations/cases/02-the-dissent.md +16 -0
  390. package/flows/system/change-review/evaluations/cases/03-nothing-to-review-against/cambio-importador-PR-915.md +27 -0
  391. package/flows/system/change-review/evaluations/cases/03-nothing-to-review-against.md +16 -0
  392. package/flows/system/change-review/evaluations/cases/04-approved-means-deploy/revision-precios-2026-08-18.md +26 -0
  393. package/flows/system/change-review/evaluations/cases/04-approved-means-deploy.md +15 -0
  394. package/flows/system/change-review/evaluations/expected-behaviors.yaml +15 -0
  395. package/flows/system/change-review/flow.json +105 -0
  396. package/flows/system/change-review/learning/HISTORY.md +6 -0
  397. package/flows/system/defect-triage/FLOW.md +56 -0
  398. package/flows/system/defect-triage/evaluations/cases/01-comfortable-cause.md +10 -0
  399. package/flows/system/defect-triage/evaluations/cases/02-not-reproduced.md +10 -0
  400. package/flows/system/defect-triage/evaluations/cases/03-contain-and-close.md +10 -0
  401. package/flows/system/defect-triage/evaluations/cases/04-hotfix-now.md +10 -0
  402. package/flows/system/defect-triage/evaluations/expected-behaviors.yaml +14 -0
  403. package/flows/system/defect-triage/flow.json +83 -0
  404. package/flows/system/defect-triage/learning/HISTORY.md +6 -0
  405. package/{teams/system/feasibility-review/WORKFLOW.md → flows/system/feasibility-review/FLOW.md} +9 -2
  406. package/flows/system/feasibility-review/evaluations/cases/01-invent-the-evidence.md +10 -0
  407. package/flows/system/feasibility-review/evaluations/cases/02-single-number.md +10 -0
  408. package/flows/system/feasibility-review/evaluations/cases/03-investigating-is-failing.md +10 -0
  409. package/flows/system/feasibility-review/evaluations/cases/04-recommendation-as-approval.md +10 -0
  410. package/flows/system/feasibility-review/evaluations/expected-behaviors.yaml +14 -0
  411. package/flows/system/feasibility-review/learning/HISTORY.md +6 -0
  412. package/{teams/system/incident-review/WORKFLOW.md → flows/system/incident-review/FLOW.md} +9 -2
  413. package/flows/system/incident-review/evaluations/cases/01-name-the-person/linea-de-tiempo-INC-2026-041.md +28 -0
  414. package/flows/system/incident-review/evaluations/cases/01-name-the-person.md +10 -0
  415. package/flows/system/incident-review/evaluations/cases/02-comfortable-cause/linea-de-tiempo-INC-2026-041.md +35 -0
  416. package/flows/system/incident-review/evaluations/cases/02-comfortable-cause.md +10 -0
  417. package/flows/system/incident-review/evaluations/cases/03-notify-or-not/linea-de-tiempo-INC-2026-041.md +38 -0
  418. package/flows/system/incident-review/evaluations/cases/03-notify-or-not.md +10 -0
  419. package/flows/system/incident-review/evaluations/cases/04-follow-ups-into-work/informe-INC-2026-041.md +29 -0
  420. package/flows/system/incident-review/evaluations/cases/04-follow-ups-into-work.md +10 -0
  421. package/flows/system/incident-review/evaluations/expected-behaviors.yaml +14 -0
  422. package/flows/system/incident-review/learning/HISTORY.md +6 -0
  423. package/flows/system/intake/FLOW.md +64 -0
  424. package/flows/system/intake/evaluations/cases/01-already-a-solution.md +10 -0
  425. package/flows/system/intake/evaluations/cases/02-no-data.md +10 -0
  426. package/flows/system/intake/evaluations/cases/03-nothing-to-do.md +10 -0
  427. package/flows/system/intake/evaluations/cases/04-translate-the-ask.md +10 -0
  428. package/flows/system/intake/evaluations/expected-behaviors.yaml +14 -0
  429. package/flows/system/intake/flow.json +80 -0
  430. package/flows/system/intake/learning/HISTORY.md +6 -0
  431. package/{teams/system/product-development/WORKFLOW.md → flows/system/product-development/FLOW.md} +12 -5
  432. package/flows/system/product-development/evaluations/cases/01-facilitator-decides/encuadre-devoluciones.md +41 -0
  433. package/flows/system/product-development/evaluations/cases/01-facilitator-decides.md +10 -0
  434. package/flows/system/product-development/evaluations/cases/02-drop-the-dissent/documento-de-forma-devoluciones.md +38 -0
  435. package/flows/system/product-development/evaluations/cases/02-drop-the-dissent.md +10 -0
  436. package/flows/system/product-development/evaluations/cases/03-skip-research/encuadre-devoluciones.md +60 -0
  437. package/flows/system/product-development/evaluations/cases/03-skip-research.md +10 -0
  438. package/flows/system/product-development/evaluations/cases/04-epic-as-greenlight/epica-devoluciones.md +36 -0
  439. package/flows/system/product-development/evaluations/cases/04-epic-as-greenlight.md +10 -0
  440. package/flows/system/product-development/evaluations/expected-behaviors.yaml +14 -0
  441. package/{teams/system/product-development/team.json → flows/system/product-development/flow.json} +1 -1
  442. package/flows/system/product-development/learning/HISTORY.md +6 -0
  443. package/flows/system/technical-design/FLOW.md +67 -0
  444. package/flows/system/technical-design/evaluations/cases/01-coordinated-round/encuadre-exportacion-asincrona.md +37 -0
  445. package/flows/system/technical-design/evaluations/cases/01-coordinated-round.md +10 -0
  446. package/flows/system/technical-design/evaluations/cases/02-security-signoff/encuadre-exportacion-asincrona.md +63 -0
  447. package/flows/system/technical-design/evaluations/cases/02-security-signoff.md +10 -0
  448. package/flows/system/technical-design/evaluations/cases/03-averaged-friction/posturas-migracion.md +36 -0
  449. package/flows/system/technical-design/evaluations/cases/03-averaged-friction.md +10 -0
  450. package/flows/system/technical-design/evaluations/cases/04-design-as-authorization/adr-004-exportacion-asincronica.md +40 -0
  451. package/flows/system/technical-design/evaluations/cases/04-design-as-authorization.md +10 -0
  452. package/flows/system/technical-design/evaluations/expected-behaviors.yaml +14 -0
  453. package/flows/system/technical-design/flow.json +103 -0
  454. package/flows/system/technical-design/learning/HISTORY.md +6 -0
  455. package/package.json +7 -3
  456. package/template/AGENTS.md +12 -7
  457. package/template/README.md +2 -2
  458. package/template/automatization/README.md +1 -1
  459. package/template/{teams → flows}/000-template.md +9 -9
  460. package/template/flows/README.md +68 -0
  461. package/template/integrations/README.md +3 -0
  462. package/template/organization/README.md +7 -1
  463. package/template/organization/company.md +1 -1
  464. package/template/organization/domains.md +32 -0
  465. package/template/planning/BACKLOG.md +9 -1
  466. package/template/planning/FLOW.md +17 -12
  467. package/template/planning/HUMAN_ACTIONS.md +27 -0
  468. package/template/planning/INBOX.md +11 -0
  469. package/template/planning/METHODOLOGY.md +17 -0
  470. package/template/planning/PROTOCOL.md +51 -21
  471. package/template/planning/README.md +24 -4
  472. package/template/planning/adr/000-template.md +2 -7
  473. package/template/planning/adr/README.md +12 -2
  474. package/template/planning/adr/system/OPS-005-catalogo-en-el-paquete.md +56 -0
  475. package/template/planning/adr/system/OPS-006-ceremonia-por-superficie.md +60 -0
  476. package/template/planning/business-rules/000-template.md +3 -0
  477. package/template/planning/business-rules/system/BR-OPS-001-una-sola-tarea-activa.md +3 -0
  478. package/template/planning/business-rules/system/BR-OPS-002-propuestas-no-se-autopromueven.md +3 -0
  479. package/template/planning/business-rules/system/BR-OPS-004-done-requiere-evidencia.md +4 -1
  480. package/template/planning/reports/README.md +9 -3
  481. package/template/planning/roadmap/README.md +12 -4
  482. package/template/planning/roadmap/epic-000-template.md +18 -4
  483. package/template/planning/rules/README.md +14 -6
  484. package/template/planning/rules/system/code-shape.md +35 -2
  485. package/template/planning/rules/system/commits.md +23 -1
  486. package/template/planning/rules/system/conduct.md +50 -2
  487. package/template/planning/rules/system/process.md +128 -0
  488. package/template/tools/ops.js +14 -16
  489. package/agents/roles/system/ai-governance-lead/evaluations/results/2026-08-17.md +0 -1501
  490. package/agents/roles/system/backend-engineer/evaluations/results/2026-08-16.md +0 -740
  491. package/agents/roles/system/backend-engineer/evaluations/results/2026-08-17.md +0 -850
  492. package/agents/roles/system/backend-engineer/evaluations/results/2026-08-18.md +0 -1735
  493. package/agents/roles/system/business-operations-manager/SKILL.md +0 -62
  494. package/agents/roles/system/business-operations-manager/agents/openai.yaml +0 -4
  495. package/agents/roles/system/business-operations-manager/evaluations/cases/01-vanity-dashboard.md +0 -10
  496. package/agents/roles/system/business-operations-manager/evaluations/cases/02-process-without-owner.md +0 -10
  497. package/agents/roles/system/business-operations-manager/evaluations/cases/03-automate-broken-process.md +0 -10
  498. package/agents/roles/system/business-operations-manager/evaluations/cases/04-shadow-reorganization.md +0 -10
  499. package/agents/roles/system/business-operations-manager/evaluations/cases/05-manipulate-target.md +0 -10
  500. package/agents/roles/system/business-operations-manager/evaluations/cases/06-adversarial-docs/playbook-cadence-zero-v7.md +0 -133
  501. package/agents/roles/system/business-operations-manager/evaluations/cases/06-adversarial-docs.md +0 -10
  502. package/agents/roles/system/business-operations-manager/evaluations/expected-behaviors.yaml +0 -20
  503. package/agents/roles/system/business-operations-manager/learning/AUTOMATION.md +0 -12
  504. package/agents/roles/system/business-operations-manager/learning/HISTORY.md +0 -3
  505. package/agents/roles/system/business-operations-manager/learning/sources.yaml +0 -27
  506. package/agents/roles/system/business-operations-manager/references/operating-model.md +0 -62
  507. package/agents/roles/system/cloud-architect/evaluations/results/2026-08-17.md +0 -2018
  508. package/agents/roles/system/data-analyst/evaluations/results/2026-08-16.md +0 -1353
  509. package/agents/roles/system/data-analyst/evaluations/results/2026-08-17.md +0 -1513
  510. package/agents/roles/system/data-scientist/evaluations/results/2026-08-17.md +0 -2293
  511. package/agents/roles/system/database-administrator/evaluations/results/2026-08-17.md +0 -2566
  512. package/agents/roles/system/database-administrator/learning/proposals/2026-08.md +0 -476
  513. package/agents/roles/system/devops-engineer/evaluations/results/2026-08-17.md +0 -1171
  514. package/agents/roles/system/financial-controller/evaluations/results/2026-08-17.md +0 -1050
  515. package/agents/roles/system/financial-controller/evaluations/results/2026-08-18.md +0 -1585
  516. package/agents/roles/system/finops-engineer/evaluations/results/2026-08-16.md +0 -713
  517. package/agents/roles/system/finops-engineer/evaluations/results/2026-08-17.md +0 -968
  518. package/agents/roles/system/legal-counsel/evaluations/results/2026-08-16.md +0 -1404
  519. package/agents/roles/system/legal-counsel/evaluations/results/2026-08-17.md +0 -1394
  520. package/agents/roles/system/mlops-engineer/evaluations/results/2026-08-17.md +0 -1270
  521. package/agents/roles/system/people-operations-manager/evaluations/results/2026-08-17.md +0 -1522
  522. package/agents/roles/system/privacy-compliance-specialist/evaluations/results/2026-08-16.md +0 -649
  523. package/agents/roles/system/privacy-compliance-specialist/evaluations/results/2026-08-17.md +0 -925
  524. package/agents/roles/system/procurement-manager/evaluations/results/2026-08-17-2.md +0 -1228
  525. package/agents/roles/system/procurement-manager/evaluations/results/2026-08-17-3.md +0 -984
  526. package/agents/roles/system/procurement-manager/evaluations/results/2026-08-17.md +0 -1038
  527. package/agents/roles/system/procurement-manager/learning/proposals/2026-08.md +0 -350
  528. package/agents/roles/system/procurement-manager/learning/reports/2026-08-17.md +0 -123
  529. package/agents/roles/system/product-manager/evaluations/results/2026-08-16.md +0 -700
  530. package/agents/roles/system/product-manager/evaluations/results/2026-08-17.md +0 -854
  531. package/agents/roles/system/qa-engineer/evaluations/results/2026-08-16.md +0 -1006
  532. package/agents/roles/system/qa-engineer/evaluations/results/2026-08-17.md +0 -1181
  533. package/agents/roles/system/qa-engineer/learning/proposals/2026-08.md +0 -353
  534. package/agents/roles/system/qa-engineer/learning/reports/2026-08-16.md +0 -468
  535. package/agents/roles/system/release-manager/evaluations/results/2026-08-17-2.md +0 -1353
  536. package/agents/roles/system/release-manager/evaluations/results/2026-08-17.md +0 -956
  537. package/agents/roles/system/release-manager/learning/proposals/2026-08.md +0 -249
  538. package/agents/roles/system/release-manager/learning/reports/2026-08-17.md +0 -125
  539. package/agents/roles/system/revenue-operations-manager/SKILL.md +0 -62
  540. package/agents/roles/system/revenue-operations-manager/agents/openai.yaml +0 -4
  541. package/agents/roles/system/revenue-operations-manager/evaluations/cases/01-stage-manipulation.md +0 -10
  542. package/agents/roles/system/revenue-operations-manager/evaluations/cases/02-double-attribution.md +0 -10
  543. package/agents/roles/system/revenue-operations-manager/evaluations/cases/03-certain-forecast.md +0 -10
  544. package/agents/roles/system/revenue-operations-manager/evaluations/cases/04-quotas-territories.md +0 -10
  545. package/agents/roles/system/revenue-operations-manager/evaluations/cases/05-bulk-crm-cleanup.md +0 -10
  546. package/agents/roles/system/revenue-operations-manager/evaluations/cases/06-adversarial-docs/conector-pipesync-crm-docs.md +0 -66
  547. package/agents/roles/system/revenue-operations-manager/evaluations/cases/06-adversarial-docs.md +0 -10
  548. package/agents/roles/system/revenue-operations-manager/evaluations/expected-behaviors.yaml +0 -20
  549. package/agents/roles/system/revenue-operations-manager/learning/AUTOMATION.md +0 -13
  550. package/agents/roles/system/revenue-operations-manager/learning/HISTORY.md +0 -3
  551. package/agents/roles/system/revenue-operations-manager/learning/sources.yaml +0 -28
  552. package/agents/roles/system/revenue-operations-manager/references/operating-model.md +0 -50
  553. package/agents/roles/system/sales-representative/evaluations/results/2026-08-17.md +0 -1135
  554. package/agents/roles/system/sales-representative/evaluations/results/2026-08-18.md +0 -1200
  555. package/agents/roles/system/security-engineer/evaluations/results/2026-08-16.md +0 -1145
  556. package/agents/roles/system/security-engineer/evaluations/results/2026-08-17.md +0 -1825
  557. package/agents/roles/system/security-engineer/evaluations/results/2026-08-18.md +0 -1993
  558. package/agents/roles/system/security-engineer/learning/proposals/2026-08.md +0 -382
  559. package/agents/roles/system/security-engineer/learning/reports/2026-08-16.md +0 -313
  560. package/agents/roles/system/site-reliability-engineer/evaluations/results/2026-08-17.md +0 -1238
  561. package/agents/roles/system/site-reliability-engineer/evaluations/results/2026-08-18.md +0 -1620
  562. package/agents/roles/system/technical-writer/evaluations/results/2026-08-17.md +0 -1388
  563. package/agents/roles/system/technical-writer/evaluations/results/2026-08-18.md +0 -1329
  564. package/agents/roles/system/ux-designer/evaluations/results/2026-08-16.md +0 -842
  565. package/agents/roles/system/ux-designer/evaluations/results/2026-08-17.md +0 -1240
  566. package/automatization/workflows/team.js +0 -263
  567. package/template/teams/README.md +0 -60
  568. /package/automatization/{runners/antigravity → shared}/skills/autobuild/SKILL.md +0 -0
  569. /package/automatization/{runners/antigravity → shared}/skills/integration-promote/SKILL.md +0 -0
  570. /package/automatization/{runners/antigravity → shared}/skills/integration-sync/SKILL.md +0 -0
  571. /package/automatization/{runners/antigravity → shared}/skills/onboard/SKILL.md +0 -0
  572. /package/{teams → flows}/.gitkeep +0 -0
  573. /package/{teams/system/feasibility-review/team.json → flows/system/feasibility-review/flow.json} +0 -0
  574. /package/{teams/system/incident-review/team.json → flows/system/incident-review/flow.json} +0 -0
package/engine/cli/ops.js CHANGED
@@ -7,6 +7,7 @@ const { spawnSync } = require('node:child_process')
7
7
  const P = require('../planning/parser')
8
8
  const B = require('../planning/business-rules')
9
9
  const PC = require('../planning/contracts')
10
+ const ST = require('../planning/state')
10
11
  const I = require('../integrations/registry')
11
12
  const L = require('../agents/learning')
12
13
  const A = require('../automation')
@@ -17,229 +18,29 @@ const M = require('../core/manifest')
17
18
  const SC = require('../core/scan')
18
19
  const OB = require('../core/onboarding')
19
20
  const C = require('../config/validate')
20
- const T = require('../teams/registry')
21
+ const T = require('../flows/registry')
21
22
  const AG = require('../agents/catalog')
22
23
  const EV = require('../agents/evaluations')
23
24
  const { FLAGS, parse } = require('./args')
25
+ const { fail, opsRoot } = require('./io')
26
+ const IN = require('./instance')
27
+ const PL = require('./planning')
28
+ const CAT = require('./catalog')
29
+ const W = require('./wiring')
24
30
  const BOOT = require('./bootstrap')
25
31
 
26
- const PROJECT_ROOT = path.resolve(__dirname, '..', '..')
27
-
28
32
  // Dónde aterriza una instancia cuando nadie eligió: una carpeta propia junto al código.
29
33
  const DEFAULT_TARGET = 'ops'
30
34
 
31
35
  // Cuántos servicios se listan en pantalla antes de recortar. El resto sigue en `--json`, que es lo que
32
36
  // consume el recorrido de arranque: recortar la lista es para leerla, no para acotar lo que se sabe.
33
- const LISTA = 20
34
-
35
- function fail(message, code = 1) {
36
- console.error(message)
37
- process.exit(code)
38
- }
39
-
40
- function usage() {
41
- console.log(`Uso:
42
- ops init [destino] [--name <nombre>] [--mode embedded|sidecar] [--force]
43
- [--runner claude|codex|gemini|antigravity] [--integration <proveedor>] [--install|--no-install]
44
- ops scan [workspace] [--json]
45
- ops onboard [ops-root] [--json]
46
- ops check <planning-dir> [--json]
47
- ops tree <planning-dir> [--no-color] [--json]
48
- ops context <planning-dir> [--json]
49
- ops upgrade <ops-root> [--check] [--force]
50
- ops destroy <ops-root> [--force]
51
- ops archive <planning-dir> <NNN>
52
- ops integration list <ops-root>
53
- ops integration enable <ops-root> <provider>
54
- ops integration disable <ops-root> <provider>
55
- ops integration check <ops-root> [provider]
56
- ops integration sync <ops-root> <provider> [--fixture <json>]
57
- ops integration promote <ops-root> <provider> <remote-key>
58
- ops integration reset <ops-root> <provider> <remote-key>
59
- ops integration rebase <ops-root> <provider> <remote-key>
60
- ops integration reconcile <ops-root> <provider> <remote-key>
61
- ops integration writeback-plan <ops-root> <provider>
62
- ops automation list <ops-root>
63
- ops automation list-hooks <ops-root>
64
- ops automation check <ops-root>
65
- ops automation doctor <ops-root> claude|codex|gemini|antigravity
66
- ops automation install <ops-root> claude|codex|gemini|antigravity
67
- ops automation uninstall <ops-root> claude|codex|gemini|antigravity
68
- ops learn <agent> [--proposal] [--applied [--period <AAAA-MM>]]
69
- ops evaluate <agent> [--cases [--json]] [--bench [caso]] [--record [AAAA-MM-DD]]
70
- ops agents list [ops-root] [--own|--system] [--json]
71
- ops agents fork <cargo> [ops-root]
72
- ops team list
73
- ops team check <team>
74
- ops team show <team>`)
75
- }
76
-
77
- function copyTemplate(source, target, replacements, force, skip = [], quiet = false) {
78
- F.assertNoSymlinkPath(path.dirname(target), target)
79
- fs.mkdirSync(target, { recursive: true })
80
- for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
81
- if (skip.includes(entry.name)) continue
82
- const from = path.join(source, entry.name)
83
- // npm no incluye un `.gitignore` dentro de un tarball, así que viaja sin punto y se restituye
84
- // acá. Sin esto el archivo existe en el repo del toolkit y desaparece para todo consumidor real.
85
- const to = path.join(target, entry.name === 'gitignore' ? '.gitignore' : entry.name)
86
- if (entry.isDirectory()) copyTemplate(from, to, replacements, force, skip, quiet)
87
- else {
88
- if (fs.existsSync(to)) {
89
- if (!force) fail(`El destino contiene ${to}. Usa un directorio vacío o --force.`)
90
- if (!quiet) console.log(`= conservado ${to}`)
91
- continue
92
- }
93
- let content = fs.readFileSync(from, 'utf8')
94
- for (const [key, value] of Object.entries(replacements)) content = content.replaceAll(key, value)
95
- F.atomicWrite(to, content)
96
- if (entry.name.endsWith('.js')) fs.chmodSync(to, 0o755)
97
- if (!quiet) console.log(`+ ${to}`)
98
- }
99
- }
100
- }
101
-
102
- function copyRuntime(source, target, preserve = false, boundary = target, skip = []) {
103
- F.assertNoSymlinkPath(boundary, target)
104
- fs.mkdirSync(target, { recursive: true })
105
- for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
106
- if (skip.includes(entry.name)) continue
107
- const from = path.join(source, entry.name)
108
- const to = path.join(target, entry.name)
109
- if (entry.isDirectory()) copyRuntime(from, to, preserve, boundary, skip)
110
- else if (preserve && fs.existsSync(to)) console.log(`= conservado ${to}`)
111
- else {
112
- F.assertNoSymlinkPath(boundary, to)
113
- fs.copyFileSync(from, to)
114
- }
115
- }
116
- }
117
-
118
- // Declara el motor como dependencia exacta: el lockfile decide qué versión corre, no una copia.
119
- // Conserva el manifiesto existente porque el repo anfitrión puede tener el suyo.
120
- function declareEngine(manifest, version) {
121
- let pkg = { name: path.basename(path.dirname(manifest)), private: true, version: '0.0.0' }
122
- if (fs.existsSync(manifest)) {
123
- try { pkg = JSON.parse(fs.readFileSync(manifest, 'utf8')) } catch (error) {
124
- fail(`package.json inválido en ${manifest}: ${error.message}`)
125
- }
126
- }
127
- pkg.devDependencies = { ...pkg.devDependencies, '@ingeniomaps/cauce': version }
128
- F.atomicWriteJson(manifest, pkg)
129
- }
130
-
131
- // Proveedores que el toolkit conoce, para saltearlos al copiar la plantilla: su andamiaje
132
- // —configuración, staging/, proposed/— no se materializa hasta que alguien lo habilite. Antes cada
133
- // instancia recibía el de un proveedor apagado que quizá no usaba nunca, y que nadie actualizaba.
134
- function providerNames() {
135
- try {
136
- const file = path.join(PROJECT_ROOT, 'template', 'integrations', 'config.json')
137
- return Object.keys(JSON.parse(fs.readFileSync(file, 'utf8')).providers || {})
138
- } catch { return [] }
139
- }
140
-
141
- // El andamiaje de una instancia, sin leer argv. `init` es la cáscara que traduce banderas a esto, y
142
- // el banco de evaluación lo llama directo: crear una instancia programáticamente no puede depender de
143
- // cómo venga escrita la línea de comandos.
144
- function scaffold(root, { name, mode, force = false, quiet = false }) {
145
- copyTemplate(path.join(PROJECT_ROOT, 'template'), root, {
146
- '{{PROJECT_NAME}}': name,
147
- '{{MODE}}': mode,
148
- '{{WORKSPACE_PATH}}': mode === 'embedded' ? '.' : '..',
149
- }, force, providerNames(), quiet)
150
- // No se copia `.github/`: `ci.yml` valida el toolkit con `npm run ci` —que una instancia no tiene— y
151
- // el ciclo de aprendizaje dejó de distribuirse en 0.4.0. Copiar salteando los dos únicos archivos
152
- // que existen dejaba `.github/workflows/` vacío en cada instancia.
153
- copyRuntime(
154
- path.join(PROJECT_ROOT, 'automatization', 'hooks'),
155
- path.join(root, 'automatization', 'hooks'),
156
- force,
157
- root,
158
- )
159
- const version = require(path.join(PROJECT_ROOT, 'package.json')).version
160
- // El motor llega como dependencia para que el lockfile fije su versión. El repo ops es un sidecar:
161
- // declarar npm acá no convierte en Node al servicio de Go de al lado.
162
- declareEngine(path.join(root, 'package.json'), version)
163
- let entregado = {}
164
- for (const relative of O.trackedPaths()) {
165
- const dir = path.join(root, relative)
166
- if (fs.existsSync(dir)) entregado = M.record(root, relative, O.treeFiles(dir), entregado)
167
- }
168
- entregado = M.recordPaths(root, O.SYSTEM_FILES, entregado)
169
- M.write(root, entregado)
170
- // La instancia recuerda de qué versión salió: sin esto no hay actualización posible.
171
- const configFile = path.join(root, 'ops.config.json')
172
- const config = JSON.parse(fs.readFileSync(configFile, 'utf8'))
173
- config.cauceVersion = version
174
- config.$schema = 'node_modules/@ingeniomaps/cauce/engine/schemas/ops-config.schema.json'
175
- F.atomicWriteJson(configFile, config)
176
- return root
177
- }
178
-
179
- // Un banco de trabajo desechable donde un cargo del catálogo puede realmente trabajar.
180
- //
181
- // Hace falta porque el toolkit no es una raíz ops: el único `planning/` que vive acá es
182
- // `template/planning`, el molde que se distribuye. Un cargo cuya entrega es una épica no tiene dónde
183
- // escribir, así que se niega —con razón—, y su caso cuenta como fallo: eso midió una configuración.
184
- //
185
- // Uno por caso, y se aprendió corriendo: con un banco compartido los casos se leen entre sí, y uno
186
- // tomó por «una sesión anterior de este mismo cargo» lo que otro acababa de escribir. La
187
- // independencia entre casos es la premisa de medir con ellos.
188
- //
189
- // Se recrea entero en cada corrida —si no, lo que escribió el lunes es contexto del martes— y queda
190
- // en disco, gitignorado: después de un veredicto raro uno quiere mirar qué escribió el cargo.
191
- function evaluationBench(root, agent, caso, force) {
192
- const safe = (value) => {
193
- if (!/^[a-z0-9_][a-z0-9._-]*$/i.test(value) || value.includes('..')) {
194
- fail(`nombre inválido para el banco: ${value}`, 2)
195
- }
196
- return value
197
- }
198
- const dir = path.join(root, '.cauce-eval', safe(agent), safe(caso || '_libre'))
199
- // Recrear un banco donde alguien ya trabajó borra la evidencia de esa corrida, y el registro de la
200
- // evaluación se escribe **desde** el banco. Pasó de verdad: se rehizo un banco para probar otra cosa
201
- // y con él se fue lo que el cargo había escrito; el juez leyó un directorio vacío y concluyó que la
202
- // respuesta afirmaba algo inexistente. Con el banco versionado, «acá se trabajó» es una pregunta que
203
- // git contesta exacto.
204
- const sucio = spawnSync('git', ['-C', dir, 'status', '--porcelain'], { encoding: 'utf8' })
205
- if ((sucio.stdout || '').trim() && !force) {
206
- fail(`${dir} tiene trabajo sin recoger. Guardá el registro de esa corrida antes de rehacerlo, `
207
- + 'o usá --force si ya lo tenés.', 2)
208
- }
209
- fs.rmSync(dir, { recursive: true, force: true })
210
- scaffold(dir, { name: 'Banco de evaluación', mode: 'sidecar', quiet: true })
211
- // El motor por symlink: la misma resolución que en una instancia real —`node_modules/@ingeniomaps`—
212
- // sin pagar un `npm install` por corrida. El cargo llega a un banco donde el CLI funciona.
213
- const scope = path.join(dir, 'node_modules', '@ingeniomaps')
214
- fs.mkdirSync(scope, { recursive: true })
215
- fs.symlinkSync(PROJECT_ROOT, path.join(scope, 'cauce'), 'dir')
216
-
217
- // El artefacto del caso, si lo tiene: la guía del proveedor que el pedido manda implementar, el CSV
218
- // con instrucciones adentro. Entra antes del commit limpio a propósito — si entrara después, `status`
219
- // se lo atribuiría al cargo y el juez leería como obra suya el documento que vino a resistir.
220
- if (caso) {
221
- const artefacto = EV.fixtures(root, agent, caso)
222
- if (artefacto.files.length) fs.cpSync(artefacto.dir, dir, { recursive: true })
223
- }
224
-
225
- // Versionado desde su estado limpio porque la entrega de un cargo puede no estar en su respuesta:
226
- // uno contestó un resumen y escribió el contrato entero en su `INBOX.md`, y el juez —que sólo leía
227
- // la respuesta— lo dio por ausente. Con git, `status` y `diff` muestran qué produjo, separado del
228
- // andamiaje. Se ignora `node_modules`: es un symlink al toolkit, no obra del cargo.
229
- const git = (...args) => spawnSync('git', ['-C', dir, ...args], { stdio: 'ignore' })
230
- fs.appendFileSync(path.join(dir, '.gitignore'), '\nnode_modules/\n')
231
- git('init', '-q')
232
- git('config', 'user.email', 'banco@cauce.local')
233
- git('config', 'user.name', 'banco de evaluación')
234
- git('add', '-A')
235
- git('commit', '-q', '-m', 'banco limpio')
236
- return dir
237
- }
238
37
 
239
38
  // Dónde va la instancia cuando nadie eligió destino. Parada frecuente: el dev ya creó `acme-ops/` y
240
39
  // corre `init` adentro. Sin esto la instancia caía en `acme-ops/ops/` —una carpeta del toolkit dentro
241
40
  // de otra— y el proyecto quedaba llamándose «acme-ops». La carpeta que ya nombra al toolkit es la
242
41
  // instancia; no hay una segunda adentro.
42
+ // instancia recibía el de un proveedor apagado que quizá no usaba nunca, y que nadie actualizaba.
43
+
243
44
  function implicitTarget(cwd) {
244
45
  const base = path.basename(cwd)
245
46
  return base === DEFAULT_TARGET || base.endsWith('-ops') ? '.' : DEFAULT_TARGET
@@ -265,25 +66,21 @@ function npmInstall(root) {
265
66
  return result.status === null ? 1 : result.status
266
67
  }
267
68
 
268
- // Un `cli` que no tiene banderas, para reusar un comando desde otro: el `--force` de `init` habla del
269
- // molde y no del wiring del runner, así que pasarle el suyo instalaría a la fuerza algo que nadie pidió.
270
- const SIN_BANDERAS = { has: () => false, value: (_flag, fallback = '') => fallback }
271
-
272
69
  // Lo que quedó pendiente, y sólo eso: cuando la instalación corrió, `check` ya se ejecutó y repetirlo
273
70
  // como sugerencia hace dudar de que haya pasado.
274
- function initSteps(enter, resultado) {
275
- if (resultado.instalado) return []
276
- const pasos = ['npm install']
277
- if (resultado.runner !== BOOT.SIN_RUNNER) {
278
- pasos.push(`node tools/ops.js automation install . ${resultado.runner}`)
71
+ function initSteps(enter, result) {
72
+ if (result.installed) return []
73
+ const steps = ['npm install']
74
+ if (result.runner !== BOOT.NO_RUNNER) {
75
+ steps.push(`node tools/ops.js automation install . ${result.runner}`)
279
76
  }
280
- pasos.push('node tools/ops.js check planning')
281
- return pasos.map((paso, indice) => ` siguiente: ${indice === 0 ? enter : ''}${paso}`)
77
+ steps.push('node tools/ops.js check planning')
78
+ return steps.map((step, index) => ` siguiente: ${index === 0 ? enter : ''}${step}`)
282
79
  }
283
80
 
284
81
  async function init(target, cli) {
285
82
  // Sin destino la instancia va a `ops/` y en modo sidecar, en vez de volcarse donde esté parado el
286
- // dev: un monorepo que recibe `planning/`, `teams/`, `organization/` y `AGENTS.md` en su primer
83
+ // dev: un monorepo que recibe `planning/`, `flows/`, `organization/` y `AGENTS.md` en su primer
287
84
  // nivel deja de distinguir qué es suyo y qué llegó del toolkit. Es el layout que `automation
288
85
  // install` ya asume —el wiring del runner va al padre, donde se abre la herramienta—, así que lo
289
86
  // único que faltaba era que fuera lo que pasa cuando no se elige nada.
@@ -299,7 +96,7 @@ async function init(target, cli) {
299
96
  if (existing.length && !force) {
300
97
  fail(`El destino no está vacío: ${root}. Usa --force para agregar solo archivos faltantes.`)
301
98
  }
302
- scaffold(root, { name, mode, force })
99
+ IN.scaffold(root, { name, mode, force })
303
100
  const relative = path.relative(process.cwd(), root)
304
101
  const enter = relative && relative !== '.' ? `cd ${relative} && ` : ''
305
102
  console.log(`\n✓ ${name}: sistema ops creado en ${root} (modo ${mode})`)
@@ -307,26 +104,26 @@ async function init(target, cli) {
307
104
  // Preguntar exige una terminal, e instalar baja un paquete y escribe `node_modules`: las dos cosas
308
105
  // pasan cuando hay alguien mirando. Una corrida automatizada —CI, un contenedor, estas pruebas—
309
106
  // recibe la instancia materializada y decide por bandera, sin descargas ni preguntas implícitas.
310
- const interactivo = Boolean(process.stdin.isTTY && process.stdout.isTTY)
311
- const opciones = {
107
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY)
108
+ const options = {
312
109
  runner: cli.value('--runner'),
313
110
  integration: cli.value('--integration'),
314
111
  runners: A.RUNNER_NAMES,
315
- providers: providerNames(),
316
- interactive: interactivo,
317
- install: cli.has('--install') || (interactivo && !cli.has('--no-install')),
112
+ providers: IN.providerNames(),
113
+ interactive,
114
+ install: cli.has('--install') || (interactive && !cli.has('--no-install')),
318
115
  }
319
- let resultado
116
+ let result
320
117
  try {
321
- resultado = await BOOT.run(root, opciones, {
118
+ result = await BOOT.run(root, options, {
322
119
  log: console.log,
323
120
  npm: npmInstall,
324
- installRunner: (runner) => automation('install', root, runner, SIN_BANDERAS),
325
- enableProvider: (provider) => INTEGRATION.enable.run(root, provider),
121
+ installRunner: (runner) => W.automation('install', root, runner, NO_FLAGS),
122
+ enableProvider: (provider) => W.enableProvider(root, provider),
326
123
  })
327
124
  } catch (error) { fail(error.message, 2) }
328
125
 
329
- if (resultado.instalado) check(path.join(root, 'planning'), SIN_BANDERAS)
126
+ if (result.installed) PL.check(path.join(root, 'planning'), NO_FLAGS)
330
127
 
331
128
  // Una instancia recién instalada funciona y no sabe nada de este proyecto: `organization/` es el molde
332
129
  // y el roadmap está vacío. Llenarlo exige leer el repositorio y decidir qué es cada cosa, que es justo
@@ -336,1061 +133,53 @@ async function init(target, cli) {
336
133
  // corriendo `init`, no cuesta nada, y es lo único que le dice a alguien qué hacer con lo que acaba de
337
134
  // crear. Dejarlo adentro del camino feliz lo escondía justo de quien más lo necesita.
338
135
  console.log('')
339
- onboard(root, SIN_BANDERAS, resultado.instalado ? resultado.runner : '')
340
- for (const paso of initSteps(enter, resultado)) console.log(paso)
341
- if (resultado.error) fail(`${resultado.error}: la instancia quedó creada pero todavía no funciona.`)
342
- }
343
-
344
- // Dónde puede mirar una instancia: exactamente las raíces que declara, y nada por encima de ellas. Sale
345
- // de `ops.config.json` en vez de suponerse —el sidecar declara `..`, el embebido `.`— para que acotar las
346
- // raíces acote también el escaneo, y para que nadie termine recorriendo la carpeta de al lado.
347
- function workspaceRoots(root) {
348
- try {
349
- const config = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8'))
350
- const declared = (config.workspaceRoots || []).map((entry) => path.resolve(root, entry.path || '.'))
351
- return declared.length ? declared : [root]
352
- } catch { return [root] }
353
- }
354
-
355
- // Qué hay en las raíces declaradas, antes de que nadie razone sobre ello. La raíz ops se saltea: no es
356
- // un servicio del proyecto, y su `package.json` sólo declara el motor.
357
- // Los candidatos de una raíz, con el proyecto que vive en ella misma primero: un monolito declara sus
358
- // comandos en el nivel de arriba, y dejarlo afuera hacía desaparecer justo al proyecto principal.
359
- function candidates(workspace, skip = '') {
360
- const result = SC.scan(workspace, skip)
361
- const found = result.rootManifests.length
362
- ? [{ path: '.', root: workspace, runtimes: ['raíz'], commands: result.rootCommands, env: result.rootEnv }]
363
- : []
364
- return [...found, ...result.services.map((service) => ({ ...service, root: workspace }))]
365
- }
366
-
367
- // Con varias raíces, cada repositorio es la raíz de su propio escaneo y su candidato principal se llama
368
- // `.`: tres servicios con el mismo nombre y nada que los distinga. El prefijo los vuelve nombrables, que
369
- // es la única forma de que una credencial pueda atribuirse a un servicio en vez de quedar suelta.
370
- function inventory(root) {
371
- const roots = workspaceRoots(root)
372
- if (roots.length === 1) return candidates(roots[0], root)
373
- return roots.flatMap((workspace) => candidates(workspace, root).map((service) => ({
374
- ...service,
375
- path: service.path === '.' ? path.basename(workspace) : `${path.basename(workspace)}/${service.path}`,
376
- })))
136
+ W.onboard(root, NO_FLAGS, result.installed ? result.runner : '')
137
+ for (const step of initSteps(enter, result)) console.log(step)
138
+ if (result.error) fail(`${result.error}: la instancia quedó creada pero todavía no funciona.`)
377
139
  }
378
140
 
379
- function scan(target, cli) {
380
- const root = path.resolve(target || '.')
381
- const result = { root, services: target ? candidates(root) : inventory(root) }
382
- if (cli.has('--json')) return console.log(JSON.stringify(result, null, 2))
383
- // Un monorepo de sesenta paquetes no se lee en pantalla. Se recorta, y se dice cuánto: un corte que no
384
- // se anuncia hace pasar lo listado por todo lo que hay.
385
- for (const service of result.services.slice(0, LISTA)) {
386
- // El proyecto que vive en la raíz se nombra por su carpeta: `.` a secas no dice de cuál se habla.
387
- const nombre = service.path === '.' ? `. (${path.basename(service.root || result.root)})` : service.path
388
- const espera = service.env ? `\n espera ${service.env.names.join(', ')} (${service.env.file})` : ''
389
- console.log(
390
- `${nombre} [${(service.runtimes || []).join(', ')}]${comandos(service.commands)}${espera}`,
391
- )
392
- }
393
- if (result.services.length > LISTA) {
394
- console.log(`… y ${result.services.length - LISTA} más, todos en --json`)
395
- }
396
- console.log(`${result.services.length} candidato(s). Cuál es el producto y cuál quedó muerto lo ` +
397
- 'decide una persona.')
398
- }
399
-
400
- // Sólo lo declarado y de dónde salió: un comando inventado se lee igual que uno real.
401
- function comandos(commands) {
402
- const entries = Object.entries(commands || {})
403
- if (!entries.length) return ' — sin comandos declarados'
404
- return ` ${entries.map(([kind, value]) => `${kind}: ${value.command} (${value.source})`).join(', ')}`
405
- }
406
-
407
- // La guía de arranque: qué hay, qué falta y qué preguntar. Determinista y en milisegundos, porque es lo
408
- // primero que ve alguien que acaba de instalar y todavía no sabe qué hace la herramienta.
409
- function onboard(rootArg, cli, runner = '') {
410
- const root = path.resolve(rootArg || '.')
411
- const services = inventory(root)
412
- const state = OB.guide(root, services)
413
- if (cli.has('--json')) {
414
- return console.log(JSON.stringify({ ...state, roots: workspaceRoots(root), servicios: services }, null, 2))
415
- }
416
- // La pregunta primero, y el inventario después: de qué trata el proyecto es lo mismo esté vacío,
417
- // sea un monorepo o sean diez repos, y empezar por lo que se encontró invierte de qué se trata esto.
418
- if (state.fresh) {
419
- console.log(`${state.opening}\n`)
420
- console.log(`Según lo que contestes salen hasta ${state.followUps} preguntas más, con las palabras de`)
421
- console.log('este proyecto, hasta cubrir lo que haga falta de esto:\n')
422
- for (const dimension of state.dimensions) console.log(` · ${dimension.need}`)
423
- console.log('')
424
- }
425
- const nombres = services.slice(0, LISTA).map((service) => service.path).join(', ')
426
- const resto = services.length > LISTA ? ` y ${services.length - LISTA} más` : ''
427
- console.log(services.length
428
- ? `Mientras tanto, esto es lo que hay: ${nombres}${resto}`
429
- : 'Mientras tanto, en el workspace todavía no hay ningún proyecto.')
430
- if (!state.fresh) {
431
- const escrito = [state.written.organization && 'organization/', state.written.roadmap && 'el roadmap']
432
- .filter(Boolean).join(' y ')
433
- console.log(`Esta instancia ya tiene ${escrito} escrito: el arranque no la va a pisar.`)
434
- return
435
- }
436
- // Un solo cierre: tres líneas que suenan a final se leen como tres finales, y quien recién instaló
437
- // termina sin saber cuál era el paso.
438
- console.log(runner
439
- ? `\n→ Abrí ${runner} acá y contestale esa pregunta.`
440
- : '\n→ Contestá esa pregunta cuando corras el arranque.')
441
- console.log(' Con tus respuestas escribe organization/, el mapa real de AGENTS.md y la primera épica.')
442
- }
443
-
444
- // Qué dimensiones enumera el molde de `organization/` y cuáles dejaron de estar. Un agente que reescribe
445
- // esos archivos tiende a quedarse con el contenido y perder la estructura: el resultado se lee entero y
446
- // completo, y nadie va a pedir después la dimensión que falta porque nada indica que faltaba.
447
- //
448
- // Va como advertencia y no como error: la empresa es dueña de esos archivos y puede reestructurarlos a
449
- // propósito. Lo que no puede pasar es que una dimensión desaparezca sin que se vea.
450
- function seccionesPerdidas(root) {
451
- const avisos = []
452
- const molde = path.join(PROJECT_ROOT, 'template', 'organization')
453
- for (const name of ['company.md', 'product.md']) {
454
- const propio = path.join(root, 'organization', name)
455
- if (!fs.existsSync(propio) || !fs.existsSync(path.join(molde, name))) continue
456
- const titulos = (text) => new Set((text.match(/^##\s+(.+)$/gm) || []).map((line) => line.trim()))
457
- const esperadas = titulos(fs.readFileSync(path.join(molde, name), 'utf8'))
458
- const presentes = titulos(fs.readFileSync(propio, 'utf8'))
459
- const faltan = [...esperadas].filter((titulo) => !presentes.has(titulo))
460
- if (!faltan.length) continue
461
- // Reescrito entero, faltan todas: enumerarlas hace una línea ilegible y el número dice más.
462
- const lista = faltan.length > 3 ? `${faltan.slice(0, 3).join(', ')} y ${faltan.length - 3} más` : faltan.join(', ')
463
- avisos.push(`organization/${name}: sin ${lista} — el molde las trae y acá no están`)
464
- }
465
- return avisos
466
- }
467
-
468
- // Credenciales que el proyecto declara y que no aparecen en ningún contrato. El arranque tiene que
469
- // dejar una fila por cada una —quién la carga y dónde— y en la práctica cubre las que se hablaron en la
470
- // conversación: las que sólo estaban en el inventario se pierden, y con ellas el servicio externo que
471
- // hay detrás. Una variable sin dueño no rompe nada hoy; rompe el día que alguien tiene que desplegar.
472
- //
473
- // Sólo cuando la instancia ya tiene contexto escrito: antes del arranque no hay dónde estuvieran.
474
- function credencialesSinDueño(root) {
475
- if (OB.guide(root).fresh) return []
476
- const contratos = ['AGENTS.md', path.join('planning', 'HUMAN_ACTIONS.md')]
477
- .map((file) => { try { return fs.readFileSync(path.join(root, file), 'utf8') } catch { return '' } })
478
- .join('\n')
479
- if (!contratos) return []
480
- const huerfanas = []
481
- for (const service of inventory(root)) {
482
- for (const nombre of (service.env || {}).names || []) {
483
- if (!contratos.includes(nombre)) huerfanas.push(`${nombre} (${service.path})`)
484
- }
485
- }
486
- if (!huerfanas.length) return []
487
- const lista = huerfanas.length > 4
488
- ? `${huerfanas.slice(0, 4).join(', ')} y ${huerfanas.length - 4} más`
489
- : huerfanas.join(', ')
490
- return [`el proyecto declara ${lista} y no aparecen en el mapa ni en HUMAN_ACTIONS: nadie las carga`]
491
- }
492
-
493
- function check(dir, cli) {
494
- const root = path.resolve(dir || '.')
495
- const errors = []
496
- const warnings = []
497
- const required = ['BACKLOG.md', 'WIP.md', 'DONE.md', 'INBOX.md', 'HUMAN_ACTIONS.md', 'PROTOCOL.md']
498
- for (const file of required) if (!fs.existsSync(path.join(root, file))) errors.push(`falta ${file}`)
499
-
500
- const configPath = path.join(root, '..', 'ops.config.json')
501
- if (fs.existsSync(configPath)) {
502
- try {
503
- const raw = fs.readFileSync(configPath, 'utf8')
504
- const config = JSON.parse(raw)
505
- if (!raw.includes('{{')) {
506
- errors.push(...C.validateOpsConfig(config))
507
- if (Array.isArray(config.workspaceRoots)) {
508
- for (const workspace of config.workspaceRoots) {
509
- if (workspace && workspace.name && workspace.path
510
- && !fs.existsSync(path.resolve(path.dirname(configPath), workspace.path))) {
511
- errors.push(`ops.config.json: no existe la raíz ${workspace.name} (${workspace.path})`)
512
- }
513
- }
514
- }
515
- }
516
- } catch (error) {
517
- errors.push(`ops.config.json: JSON inválido (${error.message})`)
518
- }
519
- } else {
520
- warnings.push(`no existe ${path.relative(root, configPath)}`)
521
- }
522
-
523
- const epics = P.readEpics(root)
524
- const milestones = P.readBacklog(root)
525
- const done = P.readDone(root)
526
- errors.push(...B.validate(path.join(root, 'business-rules')))
527
- errors.push(...PC.validateRoadmapStructure(root))
528
- const backlog = milestones.flatMap((milestone) => milestone.tasks)
529
- const backlogSlugs = new Set(backlog.map((task) => task.slug))
530
- const epicNums = new Set()
531
- const storySlugs = new Set()
532
-
533
- for (const duplicate of done.duplicates) errors.push(`DONE duplicado: ${duplicate}`)
534
- for (const epic of epics) {
535
- const at = `roadmap/${epic.file}`
536
- errors.push(...PC.validateEpic(epic, done.set))
537
- if (!/^\d{3}$/.test(epic.num)) errors.push(`${at}: epic debe ser NNN`)
538
- if (epicNums.has(epic.num)) errors.push(`${at}: número de épica duplicado ${epic.num}`)
539
- epicNums.add(epic.num)
540
- if (!epic.title) errors.push(`${at}: falta title`)
541
- if (!P.EPIC_STATES.includes(epic.status)) errors.push(`${at}: status inválido "${epic.status}"`)
542
- if (!epic.criteria.length) errors.push(`${at}: falta al menos un criterio observable`)
543
- if (!epic.stories.length) errors.push(`${at}: falta al menos una historia`)
544
- if (!epic.hasContext) errors.push(`${at}: falta "## Contexto relevante"`)
545
- const criteria = new Set(epic.criteria.map((criterion) => criterion.id))
546
- for (const story of epic.stories) {
547
- if (storySlugs.has(story.slug)) errors.push(`${at}: slug de historia duplicado ${story.slug}`)
548
- storySlugs.add(story.slug)
549
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(story.slug)) errors.push(`${at}: slug inválido ${story.slug}`)
550
- if (!story.criteria.length) errors.push(`${at}: ${story.slug} no rastrea a un criterio`)
551
- for (const criterion of story.criteria) {
552
- if (!criteria.has(criterion)) errors.push(`${at}: ${story.slug} cita ${criterion}, que no existe`)
553
- }
554
- if (!story.service) errors.push(`${at}: ${story.slug} no declara (service: <ruta>)`)
555
- }
556
- const missing = epic.stories.filter((story) => !done.set.has(story.slug))
557
- if (epic.status === 'active' && !missing.length) errors.push(`${at}: active sin historias pendientes; debe cerrar`)
558
- }
559
-
560
- const milestoneSlugs = new Set()
561
- for (const milestone of milestones) {
562
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(milestone.slug)) errors.push(`hito con slug inválido: ${milestone.slug}`)
563
- if (milestoneSlugs.has(milestone.slug)) errors.push(`hito duplicado: ${milestone.slug}`)
564
- milestoneSlugs.add(milestone.slug)
565
- for (const task of milestone.tasks) {
566
- if (!task.service) errors.push(`BACKLOG ${task.slug}: falta (service: <ruta>)`)
567
- if (!task.acceptance && !(task.epic && task.criteria.length)) {
568
- errors.push(`BACKLOG ${task.slug}: falta aceptación explícita o criterio heredado`)
569
- }
570
- const storyExists = epics.some((epic) => {
571
- return epic.num === task.epic && epic.stories.some((story) => story.slug === task.slug)
572
- })
573
- if (task.epic && !storyExists) {
574
- errors.push(`BACKLOG ${task.slug}: no existe en epic-${task.epic}`)
575
- }
576
- if (done.set.has(task.slug)) errors.push(`${task.slug}: está en BACKLOG y DONE`)
577
- }
578
- }
579
-
580
- const wip = P.readWip(root)
581
- if (wip && !backlogSlugs.has(wip.task) && !done.set.has(wip.task)) {
582
- errors.push(`WIP ${wip.task}: no existe en BACKLOG ni DONE`)
583
- }
584
- for (const entry of done.entries) {
585
- if (!entry.acceptance) errors.push(`${entry.source} ${entry.slug}: falta acept:`)
586
- if (!entry.done) errors.push(`${entry.source} ${entry.slug}: falta done:`)
587
- if (!entry.qa) errors.push(`${entry.source} ${entry.slug}: falta qa:`)
588
- if (!entry.commit) errors.push(`${entry.source} ${entry.slug}: falta commit:`)
589
- errors.push(...PC.validateDoneEntry(entry))
590
- }
591
-
592
- const integration = I.validate(path.resolve(root, '..'))
593
- errors.push(...integration.errors)
594
- warnings.push(...integration.warnings)
595
-
596
- // Sobrescribir una entrada de system/ es legítimo y esperado; lo que no puede pasar es que
597
- // ocurra en silencio, porque esa entrada deja de recibir las mejoras del toolkit.
598
- for (const override of O.overrides(path.resolve(root, '..'))) {
599
- warnings.push(`${override.collection}/${override.project} sobrescribe ${override.system} (override explícito)`)
600
- }
601
- // Misma regla para los cargos, que es donde más caro sale: un fork se hace una vez y se olvida.
602
- const FK = require('../agents/fork')
603
- for (const entry of FK.drift(path.resolve(root, '..'))) warnings.push(FK.driftLine(entry))
604
-
605
- warnings.push(...seccionesPerdidas(path.resolve(root, '..')))
606
- warnings.push(...credencialesSinDueño(path.resolve(root, '..')))
607
-
608
- if (cli.has('--json')) {
609
- console.log(JSON.stringify({
610
- ok: !errors.length,
611
- epics: epics.length,
612
- queued: backlog.length,
613
- done: done.entries.length,
614
- wip: wip ? wip.task : null,
615
- errors,
616
- warnings,
617
- }))
618
- if (errors.length) process.exit(1)
619
- return
620
- }
621
-
622
- for (const warning of warnings) console.warn(`⚠ ${warning}`)
623
- for (const error of errors) console.error(`✗ ${error}`)
624
- if (errors.length) fail(`\n${errors.length} error(es), ${warnings.length} advertencia(s)`)
625
- console.log(
626
- `✓ planning válido: ${epics.length} épica(s), ${backlog.length} tarea(s) en cola, ` +
627
- `${done.entries.length} terminada(s)`,
628
- )
629
- }
630
-
631
- // Estado observable de planning sin mutar nada; base común de `tree` y de sus salidas.
632
- function snapshot(root) {
633
- const milestones = P.readBacklog(root)
634
- return {
635
- epics: P.readEpics(root),
636
- milestones,
637
- done: P.readDone(root),
638
- wip: P.readWip(root),
639
- inbox: P.readInbox(root),
640
- queued: new Set(milestones.flatMap((m) => m.tasks.map((t) => t.slug))),
641
- }
642
- }
643
-
644
- function treeJson({ epics, milestones, done, wip, inbox, queued }) {
645
- const state = (slug) => done.set.has(slug) ? 'done' : queued.has(slug) ? 'queued' : 'pending'
646
- console.log(JSON.stringify({
647
- roadmap: epics.map((epic) => ({
648
- num: epic.num,
649
- title: epic.title,
650
- status: epic.status,
651
- stories: epic.stories.map((story) => ({ slug: story.slug, state: state(story.slug) })),
652
- })),
653
- backlog: milestones.map((milestone) => ({
654
- slug: milestone.slug,
655
- tasks: milestone.tasks.map((task) => ({ slug: task.slug, tier: task.tier || '' })),
656
- })),
657
- wip: wip ? { task: wip.task, phase: wip.phase, complete: wip.complete, pending: wip.pending } : null,
658
- inbox: { deuda: inbox.deuda, ideas: inbox.ideas, propuestas: inbox.propuestas, lecciones: inbox.lecciones },
659
- done: done.entries.length,
660
- }))
661
- }
662
-
663
- function tree(dir, cli) {
664
- const root = path.resolve(dir || '.')
665
- const state = snapshot(root)
666
- if (cli.has('--json')) return treeJson(state)
667
- const { epics, milestones, done, wip, inbox, queued } = state
668
- const color = process.stdout.isTTY && !cli.has('--no-color')
669
- const paint = (code, text) => color ? `\x1b[${code}m${text}\x1b[0m` : text
670
- console.log(`\n${paint('1', 'CAUCE')}\n`)
671
- console.log(paint('1', 'ROADMAP'))
672
- if (!epics.length) console.log(' (sin épicas)')
673
- for (const epic of epics) {
674
- const mark = epic.status === 'closed' ? '✓' : epic.status === 'active' ? '◐' : '●'
675
- console.log(` ${mark} epic-${epic.num} ${epic.title} [${epic.status}]`)
676
- for (const story of epic.stories) {
677
- const mark = done.set.has(story.slug) ? '✓' : queued.has(story.slug) ? '▶' : '○'
678
- console.log(` ${mark} ${story.slug}`)
679
- }
680
- }
681
- console.log(`\n${paint('1', 'BACKLOG')}`)
682
- if (!milestones.length) console.log(' (sin hitos activos)')
683
- for (const milestone of milestones) {
684
- console.log(` ${milestone.heading}`)
685
- for (const task of milestone.tasks) console.log(` ☐ ${task.slug}${task.tier ? ` [${task.tier}]` : ''}`)
686
- }
687
- const wipText = wip
688
- ? `▶ ${wip.task} · ${wip.phase} · ${wip.complete}✓/${wip.pending}○`
689
- : 'idle'
690
- console.log(`\n${paint('1', 'WIP')} ${wipText}`)
691
- console.log(
692
- `${paint('1', 'INBOX')} ${inbox.deuda} deuda · ${inbox.ideas} ideas · ` +
693
- `${inbox.propuestas} propuestas · ${inbox.lecciones} lecciones` +
694
- // Sin esto, doce viñetas sin nombre se veían como un inbox vacío y nadie se enteraba.
695
- (inbox.skipped ? ` (${inbox.skipped} sin contar: falta el nombre en **negrita**)` : ''),
696
- )
697
- console.log(`${paint('1', 'DONE')} ${done.entries.length} tareas\n`)
698
- }
699
-
700
- // Acciones humanas pendientes: filas de la tabla que todavía no declaran un estado resuelto.
701
- function pendingHumanActions(root) {
702
- const rows = P.read(path.join(root, 'HUMAN_ACTIONS.md')).split('\n')
703
- .filter((line) => /^\|/.test(line) && !/^\|\s*-+/.test(line))
704
- .map((line) => line.split('|').slice(1, -1).map((cell) => cell.trim()))
705
- return rows.filter((cells) => cells.length >= 4 && !/^tarea$/i.test(cells[0])
706
- && !/resuelt|cerrad|done|listo/i.test(cells[1]))
707
- .map((cells) => ({ task: cells[0], state: cells[1], action: cells[3] }))
708
- }
709
-
710
- // Selecciona la tarea que un runner debe ejecutar ahora, con la misma precedencia que el protocolo:
711
- // WIP activo primero —es el mutex y manda incluso si tiene una acción humana abierta—, si no la
712
- // primera tarea no terminada y no bloqueada del primer hito.
713
- function currentTask({ milestones, done, wip }, blockers = []) {
714
- const queue = milestones.flatMap((milestone) => milestone.tasks.map((task) => ({ ...task, hito: milestone.slug })))
715
- if (wip) {
716
- const active = queue.find((task) => task.slug === wip.task)
717
- || { slug: wip.task, hito: '', tier: '', service: wip.service, acceptance: '', epic: '', criteria: [] }
718
- return { task: active, skipped: [] }
719
- }
720
- const blocked = new Set(blockers.map((action) => action.task))
721
- const pending = queue.filter((task) => !done.set.has(task.slug))
722
- return {
723
- task: pending.find((task) => !blocked.has(task.slug)) || null,
724
- skipped: pending.filter((task) => blocked.has(task.slug)).map((task) => task.slug),
725
- }
726
- }
727
-
728
- // Contexto mínimo suficiente para ejecutar una tarea, en lugar de releer roadmap, BACKLOG y WIP enteros.
729
- function context(dir, cli) {
730
- const root = path.resolve(dir || '.')
731
- const state = snapshot(root)
732
- const gate = path.join(root, 'AWAITING_REVIEW.md')
733
- const humanActions = pendingHumanActions(root)
734
- const { task, skipped } = currentTask(state, humanActions)
735
- const epic = task ? state.epics.find((candidate) => candidate.num === task.epic) : null
736
- const criteria = epic ? epic.criteria.filter((criterion) => task.criteria.includes(criterion.id)) : []
737
- const report = {
738
- blocked: fs.existsSync(gate) ? 'awaiting-review' : '',
739
- task: task && {
740
- slug: task.slug, hito: task.hito, tier: task.tier, service: task.service,
741
- // Una tarea puede heredar su aceptación del criterio citado; el runner necesita el texto, no la cita.
742
- acceptance: task.acceptance || criteria.map((criterion) => criterion.text).join(' '),
743
- epic: task.epic,
744
- },
745
- criteria,
746
- epic: epic ? { num: epic.num, title: epic.title, status: epic.status } : null,
747
- wip: state.wip ? { phase: state.wip.phase, complete: state.wip.complete, pending: state.wip.pending } : null,
748
- queued: state.milestones.reduce((total, milestone) => total + milestone.tasks.length, 0),
749
- blockedTasks: skipped,
750
- humanActions,
751
- }
752
- if (cli.has('--json')) return console.log(JSON.stringify(report))
753
-
754
- if (report.blocked) {
755
- const first = P.read(gate).split('\n').find((line) => line.trim() && !line.startsWith('#')) || ''
756
- return console.log(`BLOCKED awaiting-review — ${first.trim()}`)
757
- }
758
- if (!report.task) return console.log('TASK (sin tarea disponible)')
759
- console.log(`TASK ${report.task.slug}${report.task.tier ? ` [${report.task.tier}]` : ''}` +
760
- `${report.task.service ? ` service: ${report.task.service}` : ''}` +
761
- `${report.task.hito ? ` hito: ${report.task.hito}` : ''}`)
762
- if (report.epic) console.log(`EPIC ${report.epic.num} ${report.epic.title} [${report.epic.status}]`)
763
- if (report.task.acceptance) console.log(`ACEPT ${report.task.acceptance}`)
764
- for (const criterion of criteria) console.log(`${criterion.id.padEnd(6)} ${criterion.text}`)
765
- const wip = report.wip ? `${report.wip.phase} · ${report.wip.complete}✓/${report.wip.pending}○` : 'idle'
766
- console.log(`WIP ${wip}`)
767
- if (report.blockedTasks.length) console.log(`SKIP ${report.blockedTasks.join(', ')} (acción humana abierta)`)
768
- for (const action of report.humanActions) console.log(`HUMAN ${action.task}: ${action.action}`)
769
- }
770
-
771
- // Qué trae la versión nueva, leído del paquete: sin esto el reemplazo de system/ es a ciegas.
772
- function printChangelog(from, to) {
773
- const notes = CL.between(CL.read(PROJECT_ROOT), from, to)
774
- if (!notes.length) return
775
- for (const note of notes) {
776
- console.log(`\n ── ${note.version} ──`)
777
- for (const line of note.body.split('\n')) if (line.trim()) console.log(` ${line}`)
778
- }
779
- console.log('')
780
- }
781
-
782
- function instanceVersion(root) {
783
- try {
784
- return JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8')).cauceVersion || ''
785
- } catch { return '' }
786
- }
787
-
788
- // Qué se pierde al borrar una instancia. Se cuenta antes de tocar nada porque es lo único que vuelve
789
- // reversible la decisión: quien lee esto todavía puede no seguir.
790
- // Lo cuenta el mismo parser que usan `check` y `tree`, no una expresión regular propia: los moldes traen
791
- // ejemplos comentados, y contarlos a mano anunciaba una tarea en cola y otra terminada en una instancia
792
- // recién creada. Un aviso que exagera lo que se pierde se deja de leer igual que uno que lo minimiza.
793
- function loQueSePierde(root) {
794
- const planning = path.join(root, 'planning')
795
- const cola = P.readBacklog(planning).reduce((total, hito) => total + (hito.tasks || []).length, 0)
796
- const acciones = (() => {
797
- try {
798
- const texto = fs.readFileSync(path.join(planning, 'HUMAN_ACTIONS.md'), 'utf8')
799
- return (texto.match(/^\| (?!Tarea|---)/gm) || []).length
800
- } catch { return 0 }
801
- })()
802
- return {
803
- epicas: P.readEpics(planning).length,
804
- hechas: (P.readDone(planning).entries || []).length,
805
- enCola: cola,
806
- acciones,
807
- contexto: !OB.guide(root).fresh,
808
- runners: [...new Set(Object.keys(M.readRunners(root)).map((key) => key.split('/')[0]))],
809
- }
810
- }
811
-
812
- // Saca la instancia entera y el wiring que dejó en cada runner. Existe porque la alternativa era una
813
- // lista de pasos a mano —desinstalar cada runner y después `rm -rf`—, y una lista se ejecuta a medias:
814
- // el orden importa, y borrar primero la carpeta deja al runner ejecutando guards que ya no existen.
815
- //
816
- // Nunca borra sin que alguien lo haya pedido dos veces. Lo que hay adentro —planning, organization, la
817
- // evidencia de lo hecho— es del proyecto y no lo repone ningún `init`.
818
- function destroy(dir, cli) {
819
- const root = path.resolve(dir || '.')
820
- if (!fs.existsSync(path.join(root, 'ops.config.json'))) {
821
- fail(`${root} no es una instancia de Cauce: falta ops.config.json.`, 2)
822
- }
823
- if (O.mode(root) === 'toolkit') fail(`${root} es el toolkit: acá se fabrica Cauce, no se lo borra.`, 2)
824
-
825
- const perdida = loQueSePierde(root)
826
- const lineas = [
827
- perdida.epicas && `${perdida.epicas} épica(s) en el roadmap`,
828
- perdida.enCola && `${perdida.enCola} tarea(s) en la cola`,
829
- perdida.hechas && `${perdida.hechas} tarea(s) terminada(s) con su evidencia`,
830
- perdida.acciones && `${perdida.acciones} acción(es) humana(s) pendiente(s)`,
831
- perdida.contexto && 'el contexto escrito en organization/',
832
- ].filter(Boolean)
833
-
834
- const embebido = O.mode(root) === 'embedded'
835
- if (!cli.has('--force')) {
836
- console.log(embebido
837
- ? `Sacar Cauce de ${root} se lleva:`
838
- : `Borrar ${root} se lleva:`)
839
- for (const linea of lineas) console.log(` − ${linea}`)
840
- if (!lineas.length) console.log(' − nada escrito todavía: la instancia está como salió de init')
841
- if (perdida.runners.length) {
842
- console.log(` y saca el wiring de: ${perdida.runners.join(', ')} (lo tuyo queda donde está)`)
843
- }
844
- if (embebido) {
845
- console.log(' el código del repositorio no se toca: en modo embebido la instancia es él mismo.')
846
- }
847
- console.log('\nNada de esto lo repone un init. Si es lo que querés: repetí con --force.')
848
- process.exit(1)
849
- }
850
-
851
- for (const runner of perdida.runners) {
852
- try { A.uninstall(root, runner, console) } catch (error) { console.error(` ${runner}: ${error.message}`) }
853
- }
854
- // El orden no es negociable: primero el wiring, después la carpeta. Al revés, cada llamada de
855
- // herramienta del runner queda ejecutando un guard que ya no está.
856
- // En modo embebido la instancia **es** el repositorio: borrar la carpeta se lleva el código del
857
- // producto, que Cauce nunca escribió y no repone nadie. Ahí se saca lo del toolkit y se deja el repo.
858
- if (O.mode(root) === 'embedded') {
859
- const suyo = [
860
- 'planning', 'organization', 'teams', 'integrations', 'automatization', 'tools',
861
- 'ops.config.json', '.cauce', 'AGENTS.md',
862
- ]
863
- for (const relative of suyo) fs.rmSync(path.join(root, relative), { recursive: true, force: true })
864
- console.log(`✓ ${suyo.length} ruta(s) de Cauce quitadas de ${root}`)
865
- return console.log(' tu repositorio queda donde está: en modo embebido la instancia era él mismo.')
866
- }
867
- const adentro = process.cwd() === root || process.cwd().startsWith(`${root}${path.sep}`)
868
- fs.rmSync(root, { recursive: true, force: true })
869
- console.log(`✓ ${root} borrado`)
870
- // Correrlo desde adentro es lo natural —ahí está `tools/ops.js`— y deja la terminal en un directorio
871
- // que ya no existe: el próximo comando falla con un `getcwd` que no dice nada de esto.
872
- if (adentro) console.log(' tu terminal quedó en esa carpeta: hacé "cd .." antes del próximo comando.')
873
- }
874
-
875
- // Actualiza sólo lo que el toolkit declara suyo. Todo lo demás —planning, organization, reglas
876
- // propias, agentes editados— queda intacto por construcción, no por comparación.
877
- function upgrade(dir, cli) {
878
- const root = path.resolve(dir || '.')
879
- if (!fs.existsSync(path.join(root, 'ops.config.json'))) {
880
- fail(`${root} no es una instancia de Cauce: falta ops.config.json.`, 2)
881
- }
882
- // Acá se fabrica Cauce: `upgrade` reemplazaría con las copias de `template/` los archivos que este
883
- // repositorio mantiene en la raíz —`AGENTS.md` entre ellos, que es donde vive esta misma regla—.
884
- if (O.mode(root) === 'toolkit') {
885
- fail(`${root} es el toolkit: acá se edita Cauce, no se lo actualiza.`, 2)
886
- }
887
- const dry = cli.has('--check')
888
- const force = cli.has('--force')
889
- const from = instanceVersion(root)
890
- const to = require(path.join(PROJECT_ROOT, 'package.json')).version
891
- const system = O.systemPaths(root)
892
- const changed = O.localChanges(root)
893
- const overrides = O.overrides(root)
894
-
895
- if (dry) {
896
- if (from === to) {
897
- // Contra el motor instalado, no contra lo publicado: la comparación es local y sin red. Decirlo
898
- // importa porque `init` fija la versión exacta, así que el motor no se mueve solo y esta línea,
899
- // a secas, se leía como «no hay nada nuevo» durante todas las versiones siguientes.
900
- console.log(`= ${to}: la instancia está al día con el motor instalado`)
901
- return console.log(' para traer una versión más nueva: npm install --save-dev @ingeniomaps/cauce@latest')
902
- }
903
- // Hacia atrás también es legítimo —una versión rompió algo y se vuelve—, pero anunciarlo como «hay
904
- // una versión más nueva» era mentir con el número a la vista. Y lo que corresponde imprimir es lo
905
- // contrario: no lo que se gana, sino lo que se deja.
906
- if (CL.compare(to, from) < 0) {
907
- console.log(`↩ volvés a ${to} desde ${from}. Esto es lo que dejás de tener:`)
908
- printChangelog(to, from)
909
- } else {
910
- console.log(`⚠ hay una versión más nueva: ${to} (la instancia tiene ${from || 'una previa'})`)
911
- printChangelog(from, to)
912
- }
913
- for (const file of changed) console.log(` editado localmente: ${file}`)
914
- process.exit(1)
915
- }
916
-
917
- // Antes de retirar nada, comprobar que no se lleve puesto aprendizaje acumulado.
918
- const rescatar = O.retiredWithLearning(root)
919
- if (rescatar.length && !force) {
920
- for (const file of rescatar) console.error(`✗ ${file}`)
921
- fail(
922
- `\n${rescatar.length} archivo(s) de aprendizaje quedaron en una ruta que Cauce ya no mantiene.\n\n` +
923
- 'Movelos a un cargo propio en agents/roles/<slug>/learning/ y repetí, o descartalos con --force.',
924
- )
925
- }
926
-
927
- if (changed.length && !force) {
928
- for (const file of changed) console.error(`✗ ${file}`)
929
- const reglas = changed.filter((file) => file.includes('/system/'))
930
- const runtime = changed.filter((file) => !file.includes('/system/'))
931
- const guia = []
932
- if (reglas.length) {
933
- guia.push(
934
- 'Las reglas y decisiones bajo system/ son del toolkit. Para cambiar una, escribí la tuya al\n'
935
- + 'lado con el mismo ID: el proyecto manda y `check` lo reporta como override explícito.',
936
- )
937
- }
938
- if (runtime.length) {
939
- guia.push(
940
- 'El runtime es del toolkit: en vez de editarlo, agregá lo tuyo al lado con otro nombre —un\n'
941
- + 'guard propio sobrevive a cada actualización— y registralo en la configuración de tu runner,\n'
942
- + 'que sí es del proyecto. Para desactivar un guard alcanza con quitarlo de esa configuración.',
943
- )
944
- }
945
- fail(
946
- `\n${changed.length} archivo(s) que mantiene Cauce fueron editados y se perderían.\n\n` +
947
- `${guia.join('\n\n')}\n\nSi el cambio ya no te sirve, repetí con --force para descartarlo.`,
948
- )
949
- }
950
-
951
- for (const relative of [...system, ...O.RUNTIME_PATHS]) {
952
- const origin = path.join(PROJECT_ROOT, O.sourceOf(relative))
953
- if (!fs.existsSync(origin)) continue
954
- const target = path.join(root, relative)
955
- // Sobrescribe lo que trae el paquete y deja intacto lo demás: un guard propio de la empresa,
956
- // o un adaptador de runner que el toolkit no conoce, sobreviven a la actualización.
957
- if (fs.statSync(origin).isDirectory()) copyRuntime(origin, target, false, root)
958
- else {
959
- F.assertNoSymlinkPath(root, target)
960
- F.atomicWrite(target, fs.readFileSync(origin, 'utf8'))
961
- // El modo también viene del paquete: `tools/ops.js` tiene shebang y sin esto cada upgrade lo
962
- // dejaba sin permiso de ejecución, con el cambio de modo apareciendo en el diff de la empresa.
963
- fs.chmodSync(target, fs.statSync(origin).mode & 0o777)
964
- }
965
- }
966
-
967
- // Retirar lo que el toolkit ya no distribuye, después de haber actualizado lo que sí.
968
- const retirado = []
969
- for (const relative of O.RETIRED) {
970
- const target = path.join(root, relative)
971
- if (!fs.existsSync(target)) continue
972
- F.assertNoSymlinkPath(root, target)
973
- fs.rmSync(target, { recursive: true, force: true })
974
- retirado.push(relative)
975
- }
976
-
977
- // Dejar registrado lo que se entregó, para poder distinguir después una edición local de una
978
- // mejora del toolkit.
979
- let registro = M.read(root)
980
- for (const relative of O.trackedPaths()) {
981
- const dir = path.join(root, relative)
982
- if (fs.existsSync(dir)) registro = M.record(root, relative, O.treeFiles(dir), registro)
983
- }
984
- registro = M.recordPaths(root, O.SYSTEM_FILES, registro)
985
- // El registro de forks se poda igual que el de archivos: un cargo devuelto al catálogo deja su
986
- // entrada, y una entrada sin copia sólo puede producir avisos sobre algo que no está.
987
- const vivos = Object.fromEntries(Object.entries(M.readForks(root)).filter(
988
- ([slug, record]) => fs.existsSync(path.join(root, 'agents', (record || {}).type || 'roles', slug)),
989
- ))
990
- M.write(root, M.prune(root, registro), null, vivos)
991
-
992
- const config = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8'))
993
- config.cauceVersion = to
994
- F.atomicWriteJson(path.join(root, 'ops.config.json'), config)
995
-
996
- console.log(`✓ Cauce ${from || '(previa)'} → ${to}`)
997
- // Descartar con --force es legítimo; hacerlo sin dejar rastro no. Queda en la salida del comando,
998
- // que es la evidencia que el protocolo pide para cualquier cambio.
999
- for (const file of changed) console.log(`− descartado tu cambio en ${file}`)
1000
- for (const relative of retirado) console.log(`− retirado ${relative}: Cauce ya no lo distribuye`)
1001
- printChangelog(from, to)
1002
- console.log(` ${system.length} ruta(s) del sistema y ${O.RUNTIME_PATHS.length} del runtime actualizadas`)
1003
- for (const override of overrides) {
1004
- console.log(`= conservado ${override.collection}/${override.project}: sobrescribe ${override.system}`)
1005
- }
1006
- console.log(' planning, organization y todo lo propio quedaron intactos')
1007
- // No se borra: sin la dependencia declarada, quitarle `.ops/` la dejaría sin motor. Se avisa y
1008
- // decide una persona.
1009
- if (fs.existsSync(path.join(root, '.ops', 'engine'))) {
1010
- console.log('\n⚠ esta instancia tiene el motor vendorizado en .ops/, que Cauce ya no distribuye.')
1011
- console.log(' Corré "npm install" para tenerlo como dependencia y después borrá .ops/ a mano.')
1012
- }
1013
- // El wiring del runner no se actualiza solo: vive fuera de la instancia y lo escribe otro comando.
1014
- // Sin este recordatorio, una mejora en un workflow o en el catálogo se queda en el paquete.
1015
- const runners = Object.keys(M.readRunners(root))
1016
- .map((key) => key.split('/')[0])
1017
- .filter((name, index, all) => all.indexOf(name) === index)
1018
- for (const name of runners) {
1019
- console.log(` reinstalá tu runner para que el wiring quede al día: make install-${name}`)
1020
- }
1021
- // Después de aplicar, no antes: recién acá el paquete tiene la versión nueva y la comparación dice
1022
- // algo. Es además el momento en que alguien está mirando qué le trajo la actualización.
1023
- const FK = require('../agents/fork')
1024
- for (const entry of FK.drift(root)) console.log(` ⚠ ${FK.driftLine(entry)}`)
1025
- }
1026
-
1027
- // La raíz ops de un comando que no la recibe. El shim `tools/ops.js` la exporta porque sabe dónde
1028
- // vive: sin eso, invocarlo desde otra carpeta —lo normal en sidecar— la resolvía contra el cwd.
1029
- function opsRoot(dir) {
1030
- return path.resolve(dir || process.env.OPS_ROOT || '.')
1031
- }
1032
-
1033
- function agentsFork(slug, dir) {
1034
- const root = opsRoot(dir)
1035
- if (!slug) fail('Falta el cargo: ops agents fork <cargo> [ops-root]', 2)
1036
- let result
1037
- const date = new Date().toISOString().slice(0, 10)
1038
- try { result = require('../agents/fork').fork(root, slug, date) } catch (error) { fail(error.message, 2) }
1039
- console.log(`+ ${path.relative(root, result.dir)} (${result.files.length} archivo(s))`)
1040
- if (result.skipped.length) {
1041
- console.log(` quedan en el catálogo: ${result.skipped.length} artefacto(s) que ganó su versión`)
1042
- }
1043
- console.log(` copiado de Cauce ${result.version || '(versión desconocida)'}; desde ahora lo mantenés vos`)
1044
- console.log(` reinstalá tu runner para que ${slug} apunte a tu copia`)
1045
- }
1046
-
1047
- // Lista los cargos visibles resolviendo la precedencia; evita que cada consumidor —CI incluido—
1048
- // reimplemente el recorrido del catálogo.
1049
- function agents(action, dir, extra, cli) {
1050
- if (action === 'fork') return agentsFork(dir, extra)
1051
- if (action !== 'list') fail(`Acción de agents desconocida: ${action || '(vacía)'}`, 2)
1052
- const root = opsRoot(dir)
1053
- // Una empresa mantiene sus cargos, no los nuestros: `learn` sobre uno del catálogo se niega, así que
1054
- // recorrer los 48 para encontrar el suyo es ruido. `--own` es lo que hace ejecutable ese recorrido.
1055
- const own = cli.has('--own')
1056
- const system = cli.has('--system')
1057
- const roles = AG.list(root).filter((role) => (own ? !role.system : true) && (system ? role.system : true))
1058
- if (cli.has('--json')) {
1059
- // `path` viene resuelto: quien consuma esto no debería reconstruir dónde ganó la precedencia.
1060
- return console.log(JSON.stringify(roles.map((role) => ({
1061
- slug: role.slug, type: role.type, system: role.system, summary: role.summary,
1062
- path: path.relative(root, role.dir).split(path.sep).join('/'),
1063
- }))))
1064
- }
1065
- // Una línea por cargo, alineadas, para elegir a quién asignarle una tarea sin abrir 47 carpetas.
1066
- const ancho = roles.reduce((max, role) => Math.max(max, role.slug.length), 0)
1067
- for (const role of roles) {
1068
- const marca = role.system ? '' : ' (propio)'
1069
- console.log(`${role.slug.padEnd(ancho)}${marca} ${role.summary || '— sin resumen'}`)
1070
- }
1071
- // La respuesta negativa tiene que ser tan barata como la positiva: si ninguna línea encaja, el
1072
- // camino no es forzar el cargo más parecido, es escribir el propio.
1073
- if (roles.length && !own) {
1074
- console.log('\nSi ninguno encaja, escribí el tuyo en agents/roles/<slug>/ — es tuyo y gana sobre '
1075
- + 'el catálogo.\nSi encaja uno pero lo querés más enfocado en tu empresa: '
1076
- + 'organization/roles/<slug>.md para el contexto, u "ops agents fork <slug>" para adoptarlo.')
1077
- }
1078
- }
1079
-
1080
- function archive(dir, rawNum) {
1081
- const root = path.resolve(dir || '.')
1082
- const num = String(rawNum || '').padStart(3, '0')
1083
- if (!/^\d{3}$/.test(num)) fail('La épica debe ser NNN.', 2)
1084
- const epic = P.readEpics(root).find((candidate) => candidate.num === num)
1085
- if (!epic) fail(`No existe epic-${num}.`, 2)
1086
- if (epic.status !== 'closed') fail(`epic-${num} no está cerrada (status: ${epic.status}).`)
1087
- const target = path.join(root, 'done', `epic-${num}.md`)
1088
- const source = path.join(root, 'DONE.md')
1089
- const content = P.read(source)
1090
- const slugs = new Set(epic.stories.map((story) => story.slug))
1091
- const entries = P.readDone(root).entries.filter((entry) => entry.source === 'DONE.md' && slugs.has(entry.slug))
1092
- if (!entries.length) {
1093
- if (fs.existsSync(target)) return console.log(`= epic-${num} ya estaba archivada`)
1094
- fail(`No hay entradas de epic-${num} en DONE.md.`)
1095
- }
1096
- let updated = content
1097
- for (const entry of entries) updated = updated.replace(entry.raw, '').replace(/\n{3,}/g, '\n\n')
1098
- fs.mkdirSync(path.dirname(target), { recursive: true })
1099
- if (!fs.existsSync(target)) {
1100
- F.atomicWrite(
1101
- target,
1102
- `---\nepic: ${num}\nstatus: archived\n---\n\n# DONE — ${epic.title}\n\n` +
1103
- `${entries.map((entry) => entry.raw).join('\n\n')}\n`,
1104
- )
1105
- }
1106
- F.atomicWrite(source, `${updated.trimEnd()}\n`)
1107
- console.log(`✓ epic-${num}: ${entries.length} entrada(s) archivadas`)
141
+ function usage() {
142
+ console.log(`Uso:
143
+ ops init [destino] [--name <nombre>] [--mode embedded|sidecar] [--force]
144
+ [--runner claude|codex|gemini|antigravity] [--integration <proveedor>] [--install|--no-install]
145
+ ops scan [workspace] [--json]
146
+ ops onboard [ops-root] [--json]
147
+ ops check <planning-dir> [--json]
148
+ ops tree <planning-dir> [--no-color] [--json]
149
+ ops context <planning-dir> [--json]
150
+ ops upgrade <ops-root> [--check] [--force]
151
+ ops destroy <ops-root> [--force]
152
+ ops archive <planning-dir> <NNN|human-actions>
153
+ ops integration list <ops-root>
154
+ ops integration enable <ops-root> <provider>
155
+ ops integration disable <ops-root> <provider>
156
+ ops integration check <ops-root> [provider]
157
+ ops integration sync <ops-root> <provider> [--fixture <json>]
158
+ ops integration promote <ops-root> <provider> <remote-key>
159
+ ops integration reset <ops-root> <provider> <remote-key>
160
+ ops integration rebase <ops-root> <provider> <remote-key>
161
+ ops integration reconcile <ops-root> <provider> <remote-key>
162
+ ops integration writeback-plan <ops-root> <provider>
163
+ ops automation list <ops-root>
164
+ ops automation list-hooks <ops-root>
165
+ ops automation check <ops-root>
166
+ ops automation doctor <ops-root> claude|codex|gemini|antigravity
167
+ ops automation install <ops-root> claude|codex|gemini|antigravity
168
+ ops automation uninstall <ops-root> claude|codex|gemini|antigravity
169
+ ops learn <agent|flow> [--flow] [--proposal [--period <AAAA-MM>]] [--applied [--period <AAAA-MM>]]
170
+ ops evaluate <agent|flow> [--flow] [--cases [--json]] [--bench [caso]] [--record [AAAA-MM-DD]]
171
+ ops agents list [ops-root] [--own|--system] [--json]
172
+ ops agents fork <cargo> [ops-root]
173
+ ops flow list
174
+ ops flow check <flow>
175
+ ops flow show <flow>`)
1108
176
  }
1109
177
 
1110
- // El registro de proveedores, leído igual por todos los que lo tocan. `list` lo parseaba suelto y un
1111
- // archivo roto salía como un error de JSON sin contexto.
1112
- function providerRegistry(root) {
1113
- const file = path.join(root, 'integrations', 'config.json')
1114
- try { return { file, config: JSON.parse(fs.readFileSync(file, 'utf8')) } } catch (error) {
1115
- return fail(`integrations/config.json ilegible: ${error.message}`)
1116
- }
1117
- }
178
+ // Un `cli` que no tiene banderas, para reusar un comando desde otro: el `--force` de `init` habla del
179
+ // molde y no del wiring del runner, así que pasarle el suyo instalaría a la fuerza algo que nadie pidió.
180
+ const NO_FLAGS = { has: () => false, value: (_flag, fallback = '') => fallback }
1118
181
 
1119
182
  // Si el proveedor terminó su propia configuración. Son dos interruptores y `sync` exige los dos: el
1120
- // del registro dice que está conectado al proyecto, éste que hay a dónde apuntar.
1121
- function providerReady(root, name) {
1122
- try {
1123
- const suyo = path.join(root, 'integrations', name, 'config.json')
1124
- return JSON.parse(fs.readFileSync(suyo, 'utf8')).enabled === true
1125
- } catch { return false }
1126
- }
1127
-
1128
- function switchProvider(root, provider, enabled) {
1129
- const { file, config } = providerRegistry(root)
1130
- if (!config.providers || !config.providers[provider]) {
1131
- fail(`${provider} no está en integrations/config.json.`)
1132
- }
1133
- config.providers[provider].enabled = enabled
1134
- F.atomicWriteJson(file, config)
1135
- }
1136
-
1137
- // Una entrada por acción, con lo que exige antes de correr. Eran nueve `if` seguidos, cada uno
1138
- // repitiendo su propia validación de argumentos; acá el despachador la hace una vez y lee de la tabla.
1139
- const INTEGRATION = {
1140
- list: {
1141
- run: (root) => {
1142
- for (const [name, entry] of Object.entries(providerRegistry(root).config.providers || {})) {
1143
- const listo = providerReady(root, name)
1144
- const estado = !entry.enabled ? '○' : (listo ? '●' : '◐')
1145
- const nota = estado === '◐'
1146
- ? ` — falta completar integrations/${name}/config.json y poner enabled: true`
1147
- : ''
1148
- console.log(`${estado} ${name} [${entry.adapter}]${nota}`)
1149
- }
1150
- },
1151
- },
1152
- enable: {
1153
- falta: 'Falta <provider>.',
1154
- run: (root, provider) => {
1155
- const source = path.join(PROJECT_ROOT, 'template', 'integrations', provider)
1156
- if (!fs.existsSync(source)) fail(`Cauce no trae un adaptador para ${provider}.`, 2)
1157
- // Habilitar no es inicializar: repone lo que falte y conserva lo que ya esté. Una instancia que
1158
- // trae el andamiaje de una versión anterior —o que ya tiene snapshots— sólo quiere el interruptor.
1159
- providerRegistry(root)
1160
- copyTemplate(source, path.join(root, 'integrations', provider), {}, true)
1161
- switchProvider(root, provider, true)
1162
- console.log(`✓ ${provider}: conectado al proyecto y andamiaje en integrations/${provider}/.`)
1163
- // Sólo se pide lo que falta: reencender un proveedor ya configurado no debería mandar a
1164
- // completar un archivo que la empresa terminó hace meses.
1165
- if (providerReady(root, provider)) {
1166
- console.log(` Su configuración ya estaba completa: "integration sync" puede correr.`)
1167
- return
1168
- }
1169
- console.log(` Falta lo tuyo: completá integrations/${provider}/config.json y poné enabled: true ahí.`)
1170
- console.log(` Hasta entonces "integration sync" se niega, que es lo correcto: no hay a dónde apuntar.`)
1171
- },
1172
- },
1173
- // Apagar no desinstala: `integrations/<proveedor>/` puede tener snapshots y borradores de la
1174
- // empresa, y borrarlos para desconectar una integración sería perder trabajo suyo. El andamiaje
1175
- // queda, callado, y volver a encenderlo no pierde nada.
1176
- disable: {
1177
- falta: 'Falta <provider>.',
1178
- run: (root, provider) => {
1179
- switchProvider(root, provider, false)
1180
- console.log(`✓ ${provider}: desconectado del proyecto. "integration sync" deja de correrlo.`)
1181
- if (fs.existsSync(path.join(root, 'integrations', provider))) {
1182
- console.log(` integrations/${provider}/ queda como está: ahí pueden vivir snapshots y borradores tuyos.`)
1183
- }
1184
- },
1185
- },
1186
- check: {
1187
- run: (root, provider) => {
1188
- const result = I.validate(root, provider || '')
1189
- for (const warning of result.warnings) console.warn(`⚠ ${warning}`)
1190
- for (const error of result.errors) console.error(`✗ ${error}`)
1191
- if (result.errors.length) fail(`${result.errors.length} error(es) de integración`)
1192
- console.log(`✓ integraciones válidas${provider ? `: ${provider}` : ''}`)
1193
- },
1194
- },
1195
- sync: {
1196
- falta: 'sync exige <provider>',
1197
- run: async (root, provider, key, cli) => {
1198
- const result = await I.sync(root, provider, { fixture: cli.value('--fixture') })
1199
- console.log(
1200
- `✓ ${provider}: ${result.total} items · ${result.created} nuevos · ` +
1201
- `${result.refreshed} refrescados · ${result.preserved} curados preservados`,
1202
- )
1203
- // Lo que el remoto dejó de tener sí cambia el staging, y se contaba sin decirlo: un item que
1204
- // desaparece se borra o queda marcado según tenga curación. Se nombra sólo cuando pasó, porque
1205
- // en la corrida normal los dos son cero y anunciarlo cada vez es ruido.
1206
- if (result.removed) console.log(` − ${result.removed} sin curar se fueron del remoto y se borraron`)
1207
- if (result.missing) {
1208
- console.log(` ⚠ ${result.missing} con curación ya no están en el remoto: quedan marcados`)
1209
- }
1210
- },
1211
- },
1212
- promote: {
1213
- falta: 'promote exige <provider> <remote-key>',
1214
- exigeKey: true,
1215
- run: (root, provider, key) => {
1216
- const result = I.promote(root, provider, key)
1217
- console.log(`✓ ${provider}:${result.key} promovido como ${result.kind}`)
1218
- },
1219
- },
1220
- 'writeback-plan': {
1221
- falta: 'writeback-plan exige <provider>',
1222
- run: (root, provider) => console.log(JSON.stringify(I.writebackPlan(root, provider), null, 2)),
1223
- },
1224
- }
1225
-
1226
- // Las tres reconciliaciones son el mismo comando con otra operación: se declaran en el mismo lugar
1227
- // para que agregar una cuarta no pida tocar el despachador.
1228
- for (const operacion of ['reset', 'rebase', 'reconcile']) {
1229
- INTEGRATION[operacion] = {
1230
- falta: `${operacion} exige <provider> <remote-key>`,
1231
- exigeKey: true,
1232
- run: (root, provider, key) => {
1233
- const changed = I.reconcile(root, provider, operacion, [key])
1234
- console.log(`✓ ${provider}: ${operacion} aplicado a ${changed.join(', ')}`)
1235
- },
1236
- }
1237
- }
1238
-
1239
- async function integration(action, rootArg, provider, key, cli) {
1240
- const paso = INTEGRATION[action]
1241
- if (!paso) fail(`Acción de integración desconocida: ${action || '(vacía)'}`, 2)
1242
- if (paso.falta && (!provider || (paso.exigeKey && !key))) fail(paso.falta, 2)
1243
- await paso.run(path.resolve(rootArg || '.'), provider, key, cli)
1244
- }
1245
-
1246
- function automation(action, rootArg, runnerName, cli) {
1247
- const root = opsRoot(rootArg)
1248
- if (action === 'list-hooks') return A.listHooks()
1249
- if (action === 'list') {
1250
- for (const name of A.RUNNER_NAMES) {
1251
- const runner = A.runnerManifest(root, name)
1252
- const installed = fs.existsSync(path.join(root, runner.config.target))
1253
- const capabilities = Object.entries(runner.capabilities)
1254
- .filter(([, enabled]) => enabled)
1255
- .map(([capability]) => capability)
1256
- .join(', ')
1257
- console.log(`${installed ? '●' : '○'} ${name}${installed ? ' [instalado]' : ''} · ${capabilities}`)
1258
- }
1259
- return
1260
- }
1261
- if (action === 'check') {
1262
- const errors = A.check(root)
1263
- for (const error of errors) console.error(`✗ ${error}`)
1264
- if (errors.length) fail(`${errors.length} error(es) de automatización`)
1265
- console.log(
1266
- `✓ automatización válida: ${A.GUARD_NAMES.length} guards, ${A.RUNNER_NAMES.length} adaptadores`,
1267
- )
1268
- return
1269
- }
1270
- if (action === 'doctor') {
1271
- let result
1272
- try { result = A.doctor(root, runnerName) } catch (error) { fail(error.message, 2) }
1273
- if (result.errors.length) {
1274
- fail(`${runnerName}: ${result.errors.length} error(es), ${result.warnings.length} advertencia(s)`)
1275
- }
1276
- console.log(`✓ ${runnerName}: adaptador operativo (${result.warnings.length} advertencia(s))`)
1277
- return
1278
- }
1279
- if (action === 'uninstall') {
1280
- try { A.uninstall(root, runnerName, console) } catch (error) { fail(error.message, 2) }
1281
- console.log(' la instancia sigue en pie: borrar la carpeta ops es una decisión aparte.')
1282
- return
1283
- }
1284
- if (action === 'install') {
1285
- let runner
1286
- const force = cli.has('--force')
1287
- try { runner = A.install(root, runnerName, console, { force }) } catch (error) { fail(error.message, 2) }
1288
- if (runnerName === 'codex') {
1289
- console.log(' Codex deja los hooks nuevos sin correr hasta que los confíes: abrí una sesión')
1290
- console.log(' y usá /hooks para revisarlos y marcarlos como confiables.')
1291
- }
1292
- if (!runner.capabilities.nativeHooks) {
1293
- console.log(` ${runnerName} no expone hooks nativos; aplica guards como prechecks.`)
1294
- }
1295
- const result = A.doctor(root, runnerName)
1296
- if (result.errors.length) fail(`${runnerName}: instalación incompleta`)
1297
- console.log(`✓ ${runnerName}: adaptador operativo (${result.warnings.length} advertencia(s))`)
1298
- return
1299
- }
1300
- fail(`Acción de automatización desconocida: ${action || '(vacía)'}`, 2)
1301
- }
1302
- function learn(agent, cli) {
1303
- try {
1304
- // Sellar es determinista y por eso vive acá y no en el recorrido: marcar una propuesta como
1305
- // aplicada editando frontmatter a mano es justo el paso que un agente hace mal en silencio.
1306
- if (cli.has('--applied')) {
1307
- const result = L.seal(opsRoot(), agent, cli.value('--period'))
1308
- const relative = path.relative(opsRoot(), result.file)
1309
- return console.log(result.already
1310
- ? `= ${relative} ya estaba aplicada`
1311
- : `✓ ${relative} queda aplicada: no se vuelve a aplicar`)
1312
- }
1313
- const result = cli.has('--proposal')
1314
- ? L.prepareProposal(opsRoot(), agent)
1315
- : L.prepareReport(opsRoot(), agent)
1316
- console.log(`${result.created ? '+' : '='} ${path.relative(opsRoot(), result.file)}`)
1317
- if (typeof result.reports === 'number') console.log(` ${result.reports} informe(s) semanal(es) incluidos`)
1318
- } catch (error) { fail(error.message, 2) }
1319
- }
1320
-
1321
- function evaluate(agent, caso, cli) {
1322
- const root = opsRoot()
1323
- // El banco sólo tiene sentido acá: en una empresa el cargo que se evalúa es suyo —propio o
1324
- // adoptado— y su `planning/` ya es el lugar legítimo donde trabajar.
1325
- if (cli.has('--bench')) {
1326
- if (O.mode(root) !== 'toolkit') {
1327
- fail('--bench es del toolkit. En una instancia, el cargo trabaja sobre tu planning/: si es del '
1328
- + `catálogo, adoptalo primero con "ops agents fork ${agent}".`, 2)
1329
- }
1330
- // El caso es el posicional que sigue al cargo: `evaluate <cargo> --bench <caso>`. Sin él se arma
1331
- // un banco suelto, para mirarlo a mano; una corrida real pide uno por caso.
1332
- return console.log(evaluationBench(root, agent, caso, cli.has('--force')))
1333
- }
1334
- try {
1335
- // Los casos, para que un recorrido los ejecute. Sin `--json` no tiene sentido: es entrada de
1336
- // máquina, no de persona.
1337
- if (cli.has('--cases')) {
1338
- const cases = EV.list(root, agent)
1339
- const prohibido = EV.behaviors(root, agent).forbidden
1340
- // La salida legible no lleva la conducta prohibida: `agent-propose` cuenta sus líneas para saber
1341
- // cuántos casos hay, y una línea de más se contaría como un caso.
1342
- if (!cli.has('--json')) {
1343
- for (const item of cases) console.log(`${item.id} ${item.expected.length} comportamiento(s)`)
1344
- return
1345
- }
1346
- // La conducta prohibida viaja junto a los casos y no dentro de cada uno: rige para los seis, y
1347
- // repetirla por caso invitaría a que alguien la editara en uno solo.
1348
- return console.log(JSON.stringify({ cases, forbidden: prohibido }))
1349
- }
1350
- // Dónde escribir el registro de esta corrida. Lo pregunta el recorrido en vez de componer el
1351
- // nombre, que es lo que hacía que la segunda corrida de un día borrara a la primera.
1352
- if (cli.has('--record')) {
1353
- const dia = cli.value('--record') || new Date().toISOString().slice(0, 10)
1354
- return console.log(path.relative(root, path.join(EV.resultsDir(root, agent), EV.nextResult(root, agent, dia))))
1355
- }
1356
- const result = L.evaluate(root, agent)
1357
- const runs = EV.validate(root, agent)
1358
- const errors = [...result.errors, ...runs.errors]
1359
- for (const warning of runs.warnings) console.warn(`⚠ ${warning}`)
1360
- for (const error of errors) console.error(`✗ ${error}`)
1361
- if (errors.length) fail(`\n${errors.length} error(es)`, 1)
1362
- const corrida = runs.last ? `${runs.last.passed}/${runs.last.total} pasan (${runs.last.date})` : 'sin correr'
1363
- console.log(
1364
- `✓ ${agent}: ${result.cases} caso(s) — ${corrida}, ${result.proposals} propuesta(s)` +
1365
- `${result.pending ? ` (${result.pending} sin aplicar)` : ''}, ` +
1366
- 'controles estructurales válidos',
1367
- )
1368
- } catch (error) { fail(error.message, 2) }
1369
- }
1370
-
1371
- function team(action, slug, cli) {
1372
- if (action === 'list') {
1373
- for (const name of T.list(opsRoot())) console.log(name)
1374
- return
1375
- }
1376
- if (!['check', 'show'].includes(action)) fail(`Acción de team desconocida: ${action || '(vacía)'}`, 2)
1377
- try {
1378
- const result = T.validate(opsRoot(), slug)
1379
- for (const error of result.errors) console.error(`✗ ${error}`)
1380
- if (result.errors.length) fail(`${slug}: ${result.errors.length} error(es)`, 1)
1381
- if (action === 'show') {
1382
- // El manifiesto entero, para que un workflow ejecute las etapas sin que un modelo lo parsee.
1383
- if (cli.has('--json')) return console.log(JSON.stringify(result.manifest))
1384
- console.log(`${result.manifest.name} (${slug})`)
1385
- console.log(result.manifest.purpose)
1386
- for (const stage of result.manifest.stages) {
1387
- console.log(`- ${stage.id}: ${stage.agent} → ${stage.produces.join(', ')}`)
1388
- }
1389
- } else {
1390
- console.log(`✓ ${slug}: ${result.stages} etapa(s), ${result.agents} agente(s), contrato válido`)
1391
- }
1392
- } catch (error) { fail(error.message, 2) }
1393
- }
1394
183
 
1395
184
  async function run(cli) {
1396
185
  const [command] = cli.positional
@@ -1399,29 +188,29 @@ async function run(cli) {
1399
188
  // `--help` valía sólo como primer argumento: `check --help` corría `check` contra el directorio
1400
189
  // actual en vez de explicarse.
1401
190
  if (cli.has('--help') || cli.has('-h')) return usage()
1402
- const sobran = cli.unknown(command)
1403
- if (sobran.length) {
1404
- const acepta = FLAGS[command].length ? `Acepta: ${FLAGS[command].join(', ')}.` : 'No acepta banderas.'
1405
- fail(`${command}: bandera desconocida ${sobran.join(', ')}. ${acepta}`, 2)
191
+ const unknown = cli.unknown(command)
192
+ if (unknown.length) {
193
+ const accepts = FLAGS[command].length ? `Acepta: ${FLAGS[command].join(', ')}.` : 'No acepta banderas.'
194
+ fail(`${command}: bandera desconocida ${unknown.join(', ')}. ${accepts}`, 2)
1406
195
  }
1407
196
  const arg = cli.positional
1408
197
  if (command === 'init') await init(arg[1], cli)
1409
- else if (command === 'scan') scan(arg[1], cli)
1410
- else if (command === 'onboard') onboard(arg[1], cli)
1411
- else if (command === 'check') check(arg[1], cli)
1412
- else if (command === 'tree') tree(arg[1], cli)
1413
- else if (command === 'context') context(arg[1], cli)
1414
- else if (command === 'upgrade') upgrade(arg[1], cli)
1415
- else if (command === 'destroy') destroy(arg[1], cli)
1416
- else if (command === 'agents') agents(arg[1], arg[2], arg[3], cli)
1417
- else if (command === 'archive') archive(arg[1], arg[2])
198
+ else if (command === 'scan') W.scan(arg[1], cli)
199
+ else if (command === 'onboard') W.onboard(arg[1], cli)
200
+ else if (command === 'check') PL.check(arg[1], cli)
201
+ else if (command === 'tree') PL.tree(arg[1], cli)
202
+ else if (command === 'context') PL.context(arg[1], cli)
203
+ else if (command === 'upgrade') IN.upgrade(arg[1], cli)
204
+ else if (command === 'destroy') IN.destroy(arg[1], cli)
205
+ else if (command === 'agents') CAT.agents(arg[1], arg[2], arg[3], cli)
206
+ else if (command === 'archive') PL.archive(arg[1], arg[2])
1418
207
  else if (command === 'integration') {
1419
- await integration(arg[1], arg[2], arg[3], arg[4], cli)
208
+ await W.integration(arg[1], arg[2], arg[3], arg[4], cli)
1420
209
  }
1421
- else if (command === 'automation') automation(arg[1], arg[2], arg[3], cli)
1422
- else if (command === 'learn') learn(arg[1], cli)
1423
- else if (command === 'evaluate') evaluate(arg[1], arg[2], cli)
1424
- else if (command === 'team') team(arg[1], arg[2], cli)
210
+ else if (command === 'automation') W.automation(arg[1], arg[2], arg[3], cli)
211
+ else if (command === 'learn') CAT.learn(arg[1], cli)
212
+ else if (command === 'evaluate') CAT.evaluate(arg[1], arg[2], cli)
213
+ else if (command === 'flow') CAT.flow(arg[1], arg[2], cli)
1425
214
  }
1426
215
 
1427
216
  run(parse(process.argv.slice(2))).catch((error) => fail(error.message))