@ingeniomaps/cauce 0.2.0 → 0.4.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 (145) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +25 -3
  3. package/agents/roles/system/ai-governance-lead/SKILL.md +2 -0
  4. package/agents/roles/system/ai-governance-lead/learning/sources.yaml +2 -4
  5. package/agents/roles/system/ai-product-manager/SKILL.md +2 -0
  6. package/agents/roles/system/ai-product-manager/learning/sources.yaml +2 -2
  7. package/agents/roles/system/analytics-engineer/SKILL.md +2 -0
  8. package/agents/roles/system/analytics-engineer/learning/sources.yaml +2 -1
  9. package/agents/roles/system/backend-engineer/SKILL.md +2 -0
  10. package/agents/roles/system/backend-engineer/learning/sources.yaml +2 -4
  11. package/agents/roles/system/business-operations-manager/SKILL.md +2 -0
  12. package/agents/roles/system/business-operations-manager/learning/sources.yaml +2 -4
  13. package/agents/roles/system/business-strategist/SKILL.md +2 -0
  14. package/agents/roles/system/business-strategist/learning/sources.yaml +2 -8
  15. package/agents/roles/system/cloud-architect/SKILL.md +2 -0
  16. package/agents/roles/system/cloud-architect/learning/sources.yaml +2 -2
  17. package/agents/roles/system/community-manager/SKILL.md +2 -0
  18. package/agents/roles/system/community-manager/learning/sources.yaml +2 -4
  19. package/agents/roles/system/content-specialist/SKILL.md +2 -0
  20. package/agents/roles/system/content-specialist/learning/sources.yaml +2 -8
  21. package/agents/roles/system/customer-success-manager/SKILL.md +2 -0
  22. package/agents/roles/system/customer-success-manager/learning/sources.yaml +2 -8
  23. package/agents/roles/system/customer-support-specialist/SKILL.md +2 -0
  24. package/agents/roles/system/customer-support-specialist/learning/sources.yaml +2 -8
  25. package/agents/roles/system/data-analyst/SKILL.md +2 -0
  26. package/agents/roles/system/data-analyst/learning/sources.yaml +2 -8
  27. package/agents/roles/system/data-engineer/SKILL.md +2 -0
  28. package/agents/roles/system/data-engineer/learning/sources.yaml +2 -4
  29. package/agents/roles/system/data-scientist/SKILL.md +2 -0
  30. package/agents/roles/system/data-scientist/learning/sources.yaml +2 -4
  31. package/agents/roles/system/database-administrator/SKILL.md +2 -0
  32. package/agents/roles/system/database-administrator/learning/sources.yaml +2 -2
  33. package/agents/roles/system/developer-relations-engineer/SKILL.md +2 -0
  34. package/agents/roles/system/developer-relations-engineer/learning/sources.yaml +2 -4
  35. package/agents/roles/system/devops-engineer/SKILL.md +2 -0
  36. package/agents/roles/system/devops-engineer/learning/sources.yaml +2 -4
  37. package/agents/roles/system/engineering-manager/SKILL.md +2 -0
  38. package/agents/roles/system/engineering-manager/learning/sources.yaml +2 -4
  39. package/agents/roles/system/financial-controller/SKILL.md +2 -0
  40. package/agents/roles/system/financial-controller/learning/sources.yaml +2 -8
  41. package/agents/roles/system/finops-engineer/SKILL.md +83 -0
  42. package/agents/roles/system/finops-engineer/agents/openai.yaml +4 -0
  43. package/agents/roles/system/finops-engineer/evaluations/cases/01-apagar-en-produccion.md +10 -0
  44. package/agents/roles/system/finops-engineer/evaluations/cases/02-ahorro-estimado-como-realizado.md +10 -0
  45. package/agents/roles/system/finops-engineer/evaluations/cases/03-salto-por-regresion.md +10 -0
  46. package/agents/roles/system/finops-engineer/evaluations/cases/04-optimizar-contra-la-fiabilidad.md +10 -0
  47. package/agents/roles/system/finops-engineer/evaluations/cases/05-costos-por-cliente.md +10 -0
  48. package/agents/roles/system/finops-engineer/evaluations/cases/06-adversarial-calculadora-del-proveedor.md +11 -0
  49. package/agents/roles/system/finops-engineer/evaluations/expected-behaviors.yaml +21 -0
  50. package/agents/roles/system/finops-engineer/learning/CODEX_AUTOMATION.md +18 -0
  51. package/agents/roles/system/finops-engineer/learning/HISTORY.md +4 -0
  52. package/agents/roles/system/finops-engineer/learning/proposals/_template.md +15 -0
  53. package/agents/roles/system/finops-engineer/learning/reports/_template.md +14 -0
  54. package/agents/roles/system/finops-engineer/learning/sources.yaml +27 -0
  55. package/agents/roles/system/finops-engineer/references/operating-model.md +70 -0
  56. package/agents/roles/system/frontend-engineer/SKILL.md +2 -0
  57. package/agents/roles/system/frontend-engineer/learning/sources.yaml +2 -4
  58. package/agents/roles/system/growth-marketer/SKILL.md +79 -0
  59. package/agents/roles/system/growth-marketer/agents/openai.yaml +4 -0
  60. package/agents/roles/system/growth-marketer/evaluations/cases/01-gastar-sin-baseline.md +10 -0
  61. package/agents/roles/system/growth-marketer/evaluations/cases/02-metrica-de-plataforma.md +10 -0
  62. package/agents/roles/system/growth-marketer/evaluations/cases/03-cortar-experimento.md +10 -0
  63. package/agents/roles/system/growth-marketer/evaluations/cases/04-promesa-que-el-producto-no-sostiene.md +10 -0
  64. package/agents/roles/system/growth-marketer/evaluations/cases/05-audiencia-sin-base-legal.md +10 -0
  65. package/agents/roles/system/growth-marketer/evaluations/cases/06-adversarial-caso-de-exito.md +11 -0
  66. package/agents/roles/system/growth-marketer/evaluations/expected-behaviors.yaml +21 -0
  67. package/agents/roles/system/growth-marketer/learning/CODEX_AUTOMATION.md +18 -0
  68. package/agents/roles/system/growth-marketer/learning/HISTORY.md +4 -0
  69. package/agents/roles/system/growth-marketer/learning/proposals/_template.md +15 -0
  70. package/agents/roles/system/growth-marketer/learning/reports/_template.md +14 -0
  71. package/agents/roles/system/growth-marketer/learning/sources.yaml +27 -0
  72. package/agents/roles/system/growth-marketer/references/operating-model.md +76 -0
  73. package/agents/roles/system/implementation-manager/SKILL.md +2 -0
  74. package/agents/roles/system/implementation-manager/learning/sources.yaml +2 -4
  75. package/agents/roles/system/legal-counsel/SKILL.md +2 -0
  76. package/agents/roles/system/legal-counsel/learning/sources.yaml +2 -8
  77. package/agents/roles/system/machine-learning-engineer/SKILL.md +2 -0
  78. package/agents/roles/system/machine-learning-engineer/learning/sources.yaml +2 -4
  79. package/agents/roles/system/mlops-engineer/SKILL.md +2 -0
  80. package/agents/roles/system/mlops-engineer/learning/sources.yaml +2 -2
  81. package/agents/roles/system/mobile-engineer/SKILL.md +2 -0
  82. package/agents/roles/system/mobile-engineer/learning/sources.yaml +2 -4
  83. package/agents/roles/system/partnerships-manager/SKILL.md +2 -0
  84. package/agents/roles/system/partnerships-manager/learning/sources.yaml +2 -4
  85. package/agents/roles/system/people-operations-manager/SKILL.md +2 -0
  86. package/agents/roles/system/people-operations-manager/learning/sources.yaml +2 -4
  87. package/agents/roles/system/privacy-compliance-specialist/SKILL.md +2 -0
  88. package/agents/roles/system/privacy-compliance-specialist/learning/sources.yaml +2 -8
  89. package/agents/roles/system/procurement-manager/SKILL.md +2 -0
  90. package/agents/roles/system/procurement-manager/learning/sources.yaml +2 -4
  91. package/agents/roles/system/product-manager/SKILL.md +2 -0
  92. package/agents/roles/system/product-manager/learning/sources.yaml +2 -0
  93. package/agents/roles/system/product-marketing-manager/SKILL.md +2 -0
  94. package/agents/roles/system/product-marketing-manager/learning/sources.yaml +2 -8
  95. package/agents/roles/system/project-manager/SKILL.md +2 -0
  96. package/agents/roles/system/project-manager/learning/sources.yaml +2 -4
  97. package/agents/roles/system/qa-engineer/SKILL.md +2 -0
  98. package/agents/roles/system/qa-engineer/learning/sources.yaml +2 -4
  99. package/agents/roles/system/release-manager/SKILL.md +2 -0
  100. package/agents/roles/system/release-manager/learning/sources.yaml +2 -4
  101. package/agents/roles/system/revenue-operations-manager/SKILL.md +2 -0
  102. package/agents/roles/system/revenue-operations-manager/learning/sources.yaml +2 -4
  103. package/agents/roles/system/sales-representative/SKILL.md +2 -0
  104. package/agents/roles/system/sales-representative/learning/sources.yaml +2 -8
  105. package/agents/roles/system/security-engineer/SKILL.md +2 -0
  106. package/agents/roles/system/security-engineer/learning/sources.yaml +2 -4
  107. package/agents/roles/system/site-reliability-engineer/SKILL.md +2 -0
  108. package/agents/roles/system/site-reliability-engineer/learning/sources.yaml +2 -4
  109. package/agents/roles/system/software-architect/SKILL.md +2 -0
  110. package/agents/roles/system/software-architect/learning/sources.yaml +2 -4
  111. package/agents/roles/system/solutions-engineer/SKILL.md +2 -0
  112. package/agents/roles/system/solutions-engineer/learning/sources.yaml +2 -4
  113. package/agents/roles/system/technical-program-manager/SKILL.md +2 -0
  114. package/agents/roles/system/technical-program-manager/learning/sources.yaml +2 -1
  115. package/agents/roles/system/technical-writer/SKILL.md +2 -0
  116. package/agents/roles/system/technical-writer/learning/sources.yaml +2 -4
  117. package/agents/roles/system/ui-designer/SKILL.md +2 -0
  118. package/agents/roles/system/ui-designer/learning/sources.yaml +2 -0
  119. package/agents/roles/system/user-researcher/SKILL.md +2 -0
  120. package/agents/roles/system/user-researcher/learning/sources.yaml +2 -0
  121. package/agents/roles/system/ux-designer/SKILL.md +2 -0
  122. package/agents/roles/system/ux-designer/learning/sources.yaml +2 -0
  123. package/automatization/workflows/autobuild.js +73 -11
  124. package/automatization/workflows/team.js +59 -6
  125. package/engine/agents/catalog.js +45 -20
  126. package/engine/agents/learning.js +19 -2
  127. package/engine/cli/ops.js +72 -18
  128. package/engine/core/manifest.js +54 -0
  129. package/engine/core/ownership.js +68 -21
  130. package/engine/teams/registry.js +23 -3
  131. package/package.json +1 -1
  132. package/teams/000-template.md +110 -0
  133. package/teams/README.md +60 -0
  134. package/teams/system/feasibility-review/WORKFLOW.md +60 -0
  135. package/teams/system/feasibility-review/team.json +73 -0
  136. package/teams/system/incident-review/WORKFLOW.md +65 -0
  137. package/teams/system/incident-review/team.json +72 -0
  138. package/teams/system/product-development/team.json +71 -17
  139. package/template/AGENTS.md +11 -0
  140. package/template/README.md +1 -1
  141. package/template/organization/roles/README.md +37 -0
  142. package/template/planning/reports/README.md +11 -0
  143. package/agents/coordinators/.gitkeep +0 -1
  144. package/agents/specialists/.gitkeep +0 -1
  145. package/agents/workflows/.gitkeep +0 -1
@@ -10,6 +10,8 @@ Crear claridad y flujo entre equipos para obtener un outcome técnico que los co
10
10
  ## Construir contexto
11
11
 
12
12
  1. Leer `AGENTS.md`, `ops.config.json`, `organization/`, estrategia, roadmap, arquitectura, contratos, decisiones, SLO, políticas y programas previos.
13
+ Leer también `organization/roles/technical-program-manager.md` si existe: son las restricciones reales de
14
+ esta empresa para este cargo.
13
15
  2. Definir outcome, beneficios, sponsor, owners, usuarios, alcance/no alcance, restricciones, autoridad, horizonte y criterios de cierre.
14
16
  3. Mapear workstreams, sistemas, interfaces, dependencias, secuencia, ambientes, proveedores, equipos, skills y capacidad disponible.
15
17
  4. Obtener evidencia de baseline, progreso, calidad, riesgos, incidentes, costos y adopción; distinguir hechos, forecast, supuesto y decisión.
@@ -6,8 +6,9 @@ rules:
6
6
  require_corroboration_for_major_change: true
7
7
  reject_unsourced_claims: true
8
8
  automatic_apply: false
9
+ # El contexto de la empresa no es una fuente de la profesión: vive en
10
+ # organization/roles/technical-program-manager.md dentro de cada instalación.
9
11
  sources:
10
- - {name: Company strategy programs architecture roadmaps decisions policies and approved metrics, url: "local://technical-program-context", tier: company-primary, topics: [strategy, programs, architecture, decisions, metrics]}
11
12
  - {name: ISO 21503 programme management, url: "https://www.iso.org/cms/%20render/live/en/sites/isoorg/contents/data/standard/08/28/82868.html", tier: standards-primary, topics: [programme, roles, responsibilities, practices]}
12
13
  - {name: ISO 21502 project management, url: "https://committee.iso.org/sites/tc258/home/projects/published/iso-21502.html", tier: standards-primary, topics: [project, adaptive, predictive, hybrid]}
13
14
  - {name: ISO 31000 risk management, url: "https://www.iso.org/standard/65694.html", tier: standards-primary, topics: [risk, principles, process]}
@@ -10,6 +10,8 @@ Ayudar a una audiencia concreta a aprender, completar una tarea, consultar una i
10
10
  ## Construir contexto
11
11
 
12
12
  1. Leer `AGENTS.md`, `ops.config.json`, guías editoriales, glosario, repositorios y documentación existente.
13
+ Leer también `organization/roles/technical-writer.md` si existe: son las restricciones reales de
14
+ esta empresa para este cargo.
13
15
  2. Identificar audiencia, objetivo, conocimiento previo, entorno, versión, idioma, canal, owner, sensibilidad y fecha de revisión.
14
16
  3. Ubicar fuentes autorizadas: interfaz ejecutable, especificación, tests, configuración, ADR, telemetría, SME y release aprobado.
15
17
  4. Clasificar la necesidad como tutorial, guía práctica, referencia o explicación; separar tipos cuando persigan necesidades distintas.
@@ -6,11 +6,9 @@ rules:
6
6
  require_corroboration_for_major_change: true
7
7
  reject_unsourced_claims: true
8
8
  automatic_apply: false
9
+ # El contexto de la empresa no es una fuente de la profesión: vive en
10
+ # organization/roles/technical-writer.md dentro de cada instalación.
9
11
  sources:
10
- - name: Company style glossary product code specs tests and documentation analytics
11
- url: local://technical-writing-context
12
- tier: company-primary
13
- topics: [style, terminology, product, api, operations, docs, feedback]
14
12
  - name: Google developer documentation style guide
15
13
  url: https://developers.google.com/style
16
14
  tier: professional-primary
@@ -11,6 +11,8 @@ Actuar como responsable del lenguaje visual de la interfaz y su aplicación sist
11
11
 
12
12
  1. Localizar la raíz operativa del proyecto.
13
13
  2. Leer `AGENTS.md`, `ops.config.json` y `organization/README.md` si existen.
14
+ Leer también `organization/roles/ui-designer.md` si existe: son las restricciones reales de
15
+ esta empresa para este cargo.
14
16
  3. Leer `organization/company.md`, `organization/product.md` y cualquier guía de marca o design system autorizado.
15
17
  4. Leer el flujo y criterios entregados por UX Designer, además de evidencia relevante y restricciones confirmadas.
16
18
  5. Inspeccionar componentes, tokens y convenciones existentes antes de crear otros. Separar hechos, restricciones y supuestos. No inventar marca, usuarios, métricas ni evidencia observable.
@@ -5,6 +5,8 @@ rules:
5
5
  reject_unsourced_claims: true
6
6
  automatic_apply: false
7
7
 
8
+ # El contexto de la empresa no es una fuente de la profesión: vive en
9
+ # organization/roles/ui-designer.md dentro de cada instalación.
8
10
  sources:
9
11
  - name: W3C Web Accessibility Initiative
10
12
  url: https://www.w3.org/WAI/
@@ -11,6 +11,8 @@ Actuar como responsable de producir evidencia confiable sobre usuarios. Explicar
11
11
 
12
12
  1. Localizar la raíz operativa del proyecto.
13
13
  2. Leer `AGENTS.md`, `ops.config.json` y `organization/README.md` si existen.
14
+ Leer también `organization/roles/user-researcher.md` si existe: son las restricciones reales de
15
+ esta empresa para este cargo.
14
16
  3. Leer `organization/company.md` y `organization/product.md`. Consultar políticas de privacidad, seguridad, accesibilidad y salvaguarda cuando apliquen.
15
17
  4. Leer sólo el estado de `planning/` necesario para comprender la decisión que la investigación debe informar.
16
18
  5. Clasificar cada entrada como dato original, observación, interpretación, inferencia, supuesto o pregunta.
@@ -5,6 +5,8 @@ rules:
5
5
  reject_unsourced_claims: true
6
6
  automatic_apply: false
7
7
 
8
+ # El contexto de la empresa no es una fuente de la profesión: vive en
9
+ # organization/roles/user-researcher.md dentro de cada instalación.
8
10
  sources:
9
11
  - name: GOV.UK User Research
10
12
  url: https://www.gov.uk/service-manual/user-research
@@ -11,6 +11,8 @@ Actuar como responsable de la estructura y comportamiento de la experiencia. Red
11
11
 
12
12
  1. Localizar la raíz operativa del proyecto.
13
13
  2. Leer `AGENTS.md`, `ops.config.json` y `organization/README.md` si existen.
14
+ Leer también `organization/roles/ux-designer.md` si existe: son las restricciones reales de
15
+ esta empresa para este cargo.
14
16
  3. Leer `organization/company.md`, `organization/product.md` y las reglas de diseño o accesibilidad disponibles.
15
17
  4. Consultar evidencia del User Researcher y decisiones aprobadas del Product Manager. Leer el estado de `planning/` necesario.
16
18
  5. Separar hechos, evidencia, restricciones, supuestos y decisiones pendientes. No inventar investigación, usuarios, métricas ni evidencia observable.
@@ -5,6 +5,8 @@ rules:
5
5
  reject_unsourced_claims: true
6
6
  automatic_apply: false
7
7
 
8
+ # El contexto de la empresa no es una fuente de la profesión: vive en
9
+ # organization/roles/ux-designer.md dentro de cada instalación.
8
10
  sources:
9
11
  - name: W3C Web Accessibility Initiative
10
12
  url: https://www.w3.org/WAI/
@@ -2,10 +2,10 @@
2
2
  // Descubre proyecto, servicios y límites desde ops.config.json; no codifica rutas ni proveedores.
3
3
  export const meta = {
4
4
  name: 'autobuild',
5
- description: 'Triage → Pick → Ready → Plan → Build → Review → Verify → QA → Commit → Done',
5
+ description: 'Triage → Pick → Cast → Ready → Plan → Build → Review → Verify → QA → Commit → Done',
6
6
  whenToUse: 'Ejecutar un hito aprobado con recuperación por WIP y checkpoint humano entre hitos.',
7
7
  phases: [
8
- 'Triage', 'Pick', 'Ready', 'Decompose', 'Plan', 'Critique', 'Build', 'Review',
8
+ 'Triage', 'Pick', 'Cast', 'Ready', 'Decompose', 'Plan', 'Critique', 'Build', 'Review',
9
9
  'Verify', 'QA', 'Commit', 'Done', 'Closing',
10
10
  ].map((title) => ({ title, detail: `Fase ${title} del protocolo agnóstico` })),
11
11
  }
@@ -81,6 +81,31 @@ const COMMIT = {
81
81
  branch: { type: 'string' }, leftovers: { type: 'array', items: { type: 'string' } }, reason: { type: 'string' },
82
82
  },
83
83
  }
84
+ // Dueño por defecto de cada fase. Es determinista: no hace falta preguntarle a un modelo quién
85
+ // revisa la arquitectura o quién decide si la evidencia de calidad alcanza.
86
+ const OWNERS = {
87
+ ready: 'product-manager',
88
+ plan: 'software-architect',
89
+ review: 'software-architect',
90
+ verify: 'qa-engineer',
91
+ qa: 'qa-engineer',
92
+ commit: 'release-manager',
93
+ }
94
+
95
+ const CAST_RULES = 'Elegís quién trabaja, no qué se hace. No inventes slugs: usá sólo los que ' +
96
+ 'devuelve el CLI. Un cargo se suma por riesgo, plataforma o alcance, nunca por rutina.'
97
+
98
+ const CAST = {
99
+ type: 'object', additionalProperties: false, required: ['build'],
100
+ properties: {
101
+ build: { type: 'string' },
102
+ review: { type: 'array', items: { type: 'string' } },
103
+ verify: { type: 'array', items: { type: 'string' } },
104
+ qa: { type: 'array', items: { type: 'string' } },
105
+ reason: { type: 'string' },
106
+ },
107
+ }
108
+
84
109
  const CONTRACT = {
85
110
  type: 'object', additionalProperties: false,
86
111
  required: ['project', 'workspaceRoots', 'maxTaskHours', 'commitPerTask', 'humanCheckpoint', 'contracts'],
@@ -164,11 +189,41 @@ while (safety++ < 50) {
164
189
  const direct = planning.lane === 'directo'
165
190
  const lite = planning.lane === 'lite'
166
191
 
192
+ // Quién ejecuta cada fase. Los dueños por defecto son fijos y no gastan una llamada; los
193
+ // condicionales entran por riesgo, plataforma y alcance —nunca por rutina—, que es la misma
194
+ // regla que ya usa el team. En `directo` no hay condicionales: el lane baja ceremonia.
195
+ const cast = { ...OWNERS, build: '' }
196
+ if (!direct) {
197
+ phase('Cast')
198
+ const chosen = await read(
199
+ `${CAST_RULES}\n\nTarea ${task.id} en ${task.service}. Aceptación: ${task.acceptance}.\n` +
200
+ `Run "node tools/ops.js agents list ${ROOT} --json" and choose only from the slugs it lists.\n` +
201
+ `Elegí el cargo que implementa según la plataforma del servicio${lite ? '.' : ', y los cargos ' +
202
+ 'condicionales que la superficie realmente justifica: seguridad si toca autenticación, permisos, ' +
203
+ 'criptografía o datos sensibles; privacidad si toca datos personales; sre si toca disponibilidad, ' +
204
+ 'límites o despliegue; ux si cambia una superficie que usa una persona; y el que corresponda a ' +
205
+ 'datos o modelos si los toca. Dejá vacío lo que no aplique: sumar un cargo que no aporta es ruido ' +
206
+ 'que diluye la revisión.'}`,
207
+ { schema: CAST, label: 'cast' },
208
+ )
209
+ if (chosen) {
210
+ cast.build = chosen.build || ''
211
+ for (const key of ['review', 'verify', 'qa']) {
212
+ cast[key] = [OWNERS[key], ...(lite ? [] : chosen[key] || [])].filter(Boolean).join(', ')
213
+ }
214
+ log(`Cargos: build=${cast.build || '(sin asignar)'} · review=${cast.review} · qa=${cast.qa}`)
215
+ }
216
+ }
217
+ const asRole = (slugs) => (slugs
218
+ ? `Actuá como ${slugs}, respetando el contrato de cada uno en su SKILL.md bajo agents/ y sus ` +
219
+ `límites: un cargo que no puede decidir solo, no decide solo.\n\n`
220
+ : '')
221
+
167
222
  if (!planning.wipActive) {
168
223
  phase('Ready')
169
224
  const ready = await read(
170
- `As product manager, check concrete acceptance, dependencies and unresolved decisions for ${task.id}: ${task.acceptance}. ` +
171
- `Clarify wording only; never expand scope.`, { schema: READY },
225
+ `${asRole(OWNERS.ready)}Check concrete acceptance, dependencies and unresolved decisions for ${task.id}: ` +
226
+ `${task.acceptance}. Clarify wording only; never expand scope.`, { schema: READY },
172
227
  )
173
228
  if (!ready.ready) {
174
229
  await write(`Register ${task.id} in ${HUMAN} with reason and an exact human action: ${ready.reason}.`)
@@ -192,7 +247,8 @@ while (safety++ < 50) {
192
247
 
193
248
  phase('Plan')
194
249
  let plan = await run(
195
- `Inspect real code, repository instructions, neighbouring conventions, epic context and git status for ${task.id}. ` +
250
+ `${asRole(OWNERS.plan)}Inspect real code, repository instructions, neighbouring conventions, epic context ` +
251
+ `and git status for ${task.id}. ` +
196
252
  `Produce the smallest plan satisfying ${task.acceptance}. Planning files cannot be implementation files.`, { schema: PLAN },
197
253
  )
198
254
  if (!direct && !lite) {
@@ -209,13 +265,16 @@ while (safety++ < 50) {
209
265
  }
210
266
  await write(
211
267
  `Persist active WIP before code: task=${task.id}, hito=${JSON.stringify(task.hito)}, phase=Build, service=${task.service}, ` +
212
- `acceptance=${JSON.stringify(task.acceptance)}, unchecked steps=${JSON.stringify(plan.steps)}. Follow the WIP contract exactly.`,
268
+ `acceptance=${JSON.stringify(task.acceptance)}, unchecked steps=${JSON.stringify(plan.steps)}. ` +
269
+ `Registrá además el reparto de cargos ${JSON.stringify(cast)} en las decisiones del WIP, para que ` +
270
+ `después se pueda auditar quién revisó qué. Follow the WIP contract exactly.`,
213
271
  )
214
272
  }
215
273
 
216
274
  phase('Build')
217
275
  const build = await run(
218
- `Implement only ${task.id} inside ${task.service}. Resume at the first pending WIP step; verify completed steps on disk ` +
276
+ `${asRole(cast.build)}Implement only ${task.id} inside ${task.service}. Resume at the first pending WIP ` +
277
+ `step; verify completed steps on disk ` +
219
278
  `and tick each successful step. Use RED/GREEN for behavior. Acceptance: ${task.acceptance}.`, {
220
279
  schema: { type: 'object', required: ['completed', 'summary'], properties: {
221
280
  completed: { type: 'boolean' }, summary: { type: 'string' }, blockers: { type: 'array', items: { type: 'string' } },
@@ -227,7 +286,8 @@ while (safety++ < 50) {
227
286
  if (!direct) {
228
287
  phase('Review')
229
288
  let review = await run(
230
- `Review the actual diff for acceptance, regressions, security, architecture, generated code, migrations and accidental scope.`,
289
+ `${asRole(cast.review)}Review the actual diff for acceptance, regressions, security, architecture, ` +
290
+ `generated code, migrations and accidental scope. Cada cargo revisa su dominio, no el ajeno.`,
231
291
  { schema: DECISION },
232
292
  )
233
293
  if (!review.approved) {
@@ -239,14 +299,15 @@ while (safety++ < 50) {
239
299
 
240
300
  phase('Verify')
241
301
  const verified = await run(
242
- `Discover and run the real gates for ${task.service}: repository instructions first, then applicable test, lint, ` +
302
+ `${asRole(cast.verify)}Discover and run the real gates for ${task.service}: repository instructions first, ` +
303
+ `then applicable test, lint, ` +
243
304
  `typecheck and build. Read actual exit codes. passed=true needs commands and no task-caused regression.`, { schema: VERIFY },
244
305
  )
245
306
  if (!verified.passed || !verified.commands.length) return stop('verify-failed', verified.details)
246
307
 
247
308
  phase('QA')
248
309
  const qa = await run(
249
- `${direct || lite ? 'Perform the cheapest real acceptance check' : 'Exercise real consumer-visible behavior'} for ` +
310
+ `${asRole(cast.qa)}${direct || lite ? 'Perform the cheapest real acceptance check' : 'Exercise real consumer-visible behavior'} for ` +
250
311
  `${task.id}. Unit tests alone are not QA. Start only minimum runtime and tear it down. Acceptance: ${task.acceptance}.`,
251
312
  { schema: QA },
252
313
  )
@@ -254,7 +315,8 @@ while (safety++ < 50) {
254
315
 
255
316
  phase('Commit')
256
317
  const commit = contract.commitPerTask ? await run(
257
- `Find the git repo owning ${task.service}, inspect status/diff, stage explicit task files, create one Conventional ` +
318
+ `${asRole(OWNERS.commit)}Find the git repo owning ${task.service}, inspect status/diff, stage explicit task ` +
319
+ `files, create one Conventional ` +
258
320
  `Commit with footer "Task: ${task.id}", then verify log/status. Never amend or push; report unrelated leftovers.`,
259
321
  { schema: COMMIT },
260
322
  ) : { committed: true, reason: 'runner.commitPerTask is disabled' }
@@ -20,14 +20,27 @@ const P = `${ROOT}/planning`
20
20
  const ROADMAP = `${P}/roadmap`
21
21
  const HUMAN = `${P}/HUMAN_ACTIONS.md`
22
22
  const INBOX = `${P}/INBOX.md`
23
+ const REPORTS = `${P}/reports`
23
24
 
24
- const TEAM = String(args && args.team || process.env.OPS_TEAM || 'product-development')
25
- const INTENT = String(args && args.intent || process.env.OPS_INTENT || '').trim()
25
+ // Tres formas de invocarlo, porque escribir JSON en un slash command no es razonable:
26
+ //
27
+ // /team quiero cobrar con tarjeta equipo por defecto
28
+ // /team feasibility-review: quiero cobrar con tarjeta equipo elegido por prefijo
29
+ // /team {"team": "acme-soporte", "intent": "..."} argumentos estructurados
30
+ //
31
+ // El prefijo se toma como candidato y se confirma más abajo contra los equipos que existen: si no es
32
+ // uno, el texto completo era la intención y nadie tuvo que aprenderse una sintaxis.
33
+ const input = typeof args === 'string' ? { intent: args } : (args || {})
34
+ const raw = String(input.intent || process.env.OPS_INTENT || '').trim()
35
+ const prefix = raw.match(/^([a-z][a-z0-9-]*)\s*:\s*(.+)$/s)
36
+ const CANDIDATE = String(input.team || process.env.OPS_TEAM || (prefix ? prefix[1] : '') || 'product-development')
37
+ const INTENT = (input.team || !prefix ? raw : prefix[2]).trim()
26
38
 
27
39
  const MANIFEST = {
28
40
  type: 'object', additionalProperties: false, required: ['name', 'purpose', 'stages', 'guardrails'],
29
41
  properties: {
30
42
  name: { type: 'string' }, purpose: { type: 'string' },
43
+ outcome: { type: 'string', enum: ['epic', 'report'] },
31
44
  entryAgent: { type: 'string' }, facilitator: { type: 'string' },
32
45
  guardrails: { type: 'array', items: { type: 'string' } },
33
46
  owners: { type: 'array', items: { type: 'object', additionalProperties: false, properties: {
@@ -35,6 +48,7 @@ const MANIFEST = {
35
48
  } } },
36
49
  stages: { type: 'array', items: { type: 'object', additionalProperties: false, properties: {
37
50
  id: { type: 'string' }, agent: { type: 'string' }, exitGate: { type: 'string' },
51
+ phase: { type: 'string', enum: ['discovery', 'delivery'] },
38
52
  produces: { type: 'array', items: { type: 'string' } },
39
53
  } } },
40
54
  },
@@ -75,10 +89,27 @@ const BASE = `Nunca inventes clientes, métricas, restricciones ni decisiones. N
75
89
  `haría falta averiguar. No promuevas trabajo al BACKLOG, no escribas en sistemas externos y no ` +
76
90
  `declares validado nada sin evidencia observable.`
77
91
 
92
+ const resolved = await agent(
93
+ `${BASE}\n\nRun "node tools/ops.js team list" from ${ROOT}. Report whether "${CANDIDATE}" is one of ` +
94
+ `the slugs it printed, and the full list. Do not guess: report only what the command printed.`,
95
+ { schema: { type: 'object', required: ['exists', 'teams'], properties: {
96
+ exists: { type: 'boolean' }, teams: { type: 'array', items: { type: 'string' } },
97
+ } }, label: 'team-resolve' },
98
+ )
99
+ if (!resolved) return stop('teams-unavailable', 'no se pudo listar los equipos')
100
+ if (!resolved.exists && (input.team || process.env.OPS_TEAM)) {
101
+ return stop('equipo-inexistente', `${CANDIDATE} no existe. Disponibles: ${resolved.teams.join(', ')}`)
102
+ }
103
+ // El prefijo era parte de la intención, no un equipo: se recompone y sigue con el equipo por defecto.
104
+ const TEAM = resolved.exists ? CANDIDATE : 'product-development'
105
+ const GOAL = resolved.exists ? INTENT : raw
106
+ if (!resolved.exists && prefix) log(`"${CANDIDATE}" no es un equipo: se toma el texto completo como intención.`)
107
+
78
108
  const contract = await agent(
79
109
  `${BASE}\n\nRun "node tools/ops.js team show ${TEAM} --json" from ${ROOT} and report only what it ` +
80
- `printed: name, purpose, entryAgent, facilitator, guardrails, the stages with id, agent, produces ` +
81
- `and exitGate, and decisionOwners flattened into owners as domain/agent pairs. Then read ` +
110
+ `printed: name, purpose, outcome, entryAgent, facilitator, guardrails, the stages with id, phase, ` +
111
+ `agent, ` +
112
+ `produces and exitGate, and decisionOwners flattened into owners as domain/agent pairs. Then read ` +
82
113
  `${ROOT}/organization/ and report nothing from it: it is context for later stages, not output.`,
83
114
  { schema: MANIFEST, label: 'team-contract' },
84
115
  )
@@ -88,13 +119,17 @@ const owners = (contract.owners || []).map((owner) => `${owner.domain}=${owner.a
88
119
  const RULES = `${BASE}\n\nEquipo ${contract.name}: ${contract.purpose}\n` +
89
120
  `Guardrails: ${contract.guardrails.join(' ')}\n` +
90
121
  `${owners ? `Dueños de decisión: ${owners}. Ningún otro cargo resuelve en su dominio.\n` : ''}` +
91
- `Contexto de la empresa en ${ROOT}/organization/. Intención a evaluar: ${INTENT}`
122
+ `Contexto de la empresa en ${ROOT}/organization/. Intención a evaluar: ${GOAL}`
92
123
 
93
124
  phase('Stages')
94
125
 
95
126
  const handoffs = []
96
127
  const blocked = []
97
- for (const stage of contract.stages) {
128
+ // Sólo descubrimiento: este recorrido propone trabajo, no lo ejecuta. Las etapas de entrega las
129
+ // corre `autobuild`, y sólo después de que una persona promueva la épica al BACKLOG.
130
+ const discovery = contract.stages.filter((stage) => stage.phase === 'discovery')
131
+ if (!discovery.length) return stop('sin-descubrimiento', `${TEAM} no declara etapas de discovery`)
132
+ for (const stage of discovery) {
98
133
  const previous = handoffs.length
99
134
  ? `Handoffs previos:\n${handoffs.map((entry) => `- ${entry.id}: ${entry.findings}`).join('\n')}`
100
135
  : 'Sos la primera etapa: no hay handoff previo.'
@@ -129,6 +164,24 @@ if (blocked.length) {
129
164
 
130
165
  phase('Draft')
131
166
 
167
+ // Un recorrido que registra lo aprendido no propone trabajo: deja el informe y las tareas de
168
+ // seguimiento en el INBOX, donde una persona decide si alguna merece convertirse en épica.
169
+ if (contract.outcome === 'report') {
170
+ const report = await agent(
171
+ `${RULES}\n\nHandoffs completos:\n${JSON.stringify(handoffs)}\n\nEscribí el informe en ${REPORTS} como ` +
172
+ `<AAAA-MM-DD>-<slug>.md: qué pasó, qué se sabe con evidencia, qué se supone, qué se decidió y qué ` +
173
+ `queda abierto. Separá causa de síntoma y no atribuyas responsabilidad a personas. Registrá cada ` +
174
+ `seguimiento en la sección Lecciones de ${INBOX}, sin promoverlo, y toda acción que requiera una ` +
175
+ `persona en ${HUMAN}.`,
176
+ { schema: { type: 'object', required: ['file', 'followUps'], properties: {
177
+ file: { type: 'string' }, followUps: { type: 'integer' }, summary: { type: 'string' },
178
+ } }, label: 'report-write' },
179
+ )
180
+ if (!report) return stop('report-unavailable', 'el informe no devolvió resultado')
181
+ log(`Informe en ${report.file}. ${report.followUps} seguimiento(s) en el INBOX, sin promover.`)
182
+ return finish({ team: TEAM, stages: handoffs.length, report: report.file, promoted: false })
183
+ }
184
+
132
185
  const epic = await agent(
133
186
  `${RULES}\n\nHandoffs completos:\n${JSON.stringify(handoffs)}\n\nComo product-manager, decidí si la ` +
134
187
  `intención es viable con la evidencia reunida. Si lo es, redactá la épica: título, slug en ` +
@@ -1,15 +1,18 @@
1
1
  'use strict'
2
2
 
3
3
  // Resolución única del catálogo de cargos. La usan el ciclo de aprendizaje, la validación de
4
- // equipos y la instalación en runners: tres lugares que antes resolvían por su cuenta.
4
+ // equipos y la instalación en runners.
5
5
  //
6
- // Cada tipo separa lo del toolkit de lo del proyecto igual que el resto de las colecciones:
6
+ // El catálogo se reparte en dos lugares según de quién es cada cosa:
7
7
  //
8
- // agents/<tipo>/system/<slug>/SKILL.md viene con Cauce y se reemplaza al actualizar
9
- // agents/<tipo>/<slug>/SKILL.md es del proyecto y manda sobre el anterior
8
+ // <paquete>/agents/<tipo>/system/<slug>/ viene con Cauce, se actualiza con la dependencia
9
+ // <proyecto>/agents/<tipo>/<slug>/ es de la empresa y manda sobre el anterior
10
10
  //
11
- // Un slug repetido entre tipos distintos sigue siendo ambiguo: ahí no hay ninguna regla que
12
- // diga cuál gana, y elegir en silencio sería peor que fallar.
11
+ // Los cargos del sistema no se copian al proyecto: evolucionan como profesión y esa evolución es
12
+ // la misma para todos. Lo que es de cada empresa —su contexto— vive en `organization/roles/`.
13
+ //
14
+ // Un slug repetido entre tipos distintos sigue siendo ambiguo: ahí no hay ninguna regla que diga
15
+ // cuál gana, y elegir en silencio sería peor que fallar.
13
16
 
14
17
  const fs = require('node:fs')
15
18
  const path = require('node:path')
@@ -24,44 +27,66 @@ function directories(dir) {
24
27
  } catch { return [] }
25
28
  }
26
29
 
30
+ // Dónde está el catálogo que trae Cauce. Se reconoce por tener `roles/system/`, que es el espacio
31
+ // del sistema y no algo que un proyecto deba crear.
32
+ function systemCatalog(root) {
33
+ return require('../core/ownership').packageDir(root, 'agents')
34
+ }
35
+
36
+ function projectCatalog(root) {
37
+ return path.join(root, 'agents')
38
+ }
39
+
27
40
  function types(root) {
28
- return directories(path.join(root, 'agents'))
41
+ const own = directories(projectCatalog(root))
42
+ const system = directories(systemCatalog(root) || path.join(root, '\0'))
43
+ return [...new Set([...own, ...system])]
29
44
  }
30
45
 
31
- // Todos los cargos visibles: los del proyecto ocultan a los del sistema con el mismo slug.
46
+ // Todos los cargos visibles: los de la empresa ocultan a los del sistema con el mismo slug.
32
47
  function list(root) {
33
- const catalog = path.join(root, 'agents')
48
+ const system = systemCatalog(root)
34
49
  const found = new Map()
35
50
  for (const type of types(root)) {
36
- const base = path.join(catalog, type)
37
- // El sistema primero para que el proyecto lo sobrescriba al pasar por encima.
38
- for (const [source, system] of [[path.join(base, 'system'), true], [base, false]]) {
51
+ // El sistema primero, para que el proyecto lo sobrescriba al pasar por encima.
52
+ const sources = [
53
+ system ? [path.join(system, type, 'system'), true] : null,
54
+ [path.join(projectCatalog(root), type), false],
55
+ ].filter(Boolean)
56
+ for (const [source, fromSystem] of sources) {
39
57
  for (const slug of directories(source)) {
40
58
  if (slug === 'system' || !SLUG.test(slug)) continue
41
59
  const dir = path.join(source, slug)
42
60
  if (!fs.existsSync(path.join(dir, 'SKILL.md'))) continue
43
- found.set(slug, { slug, type, dir, system })
61
+ found.set(slug, { slug, type, dir, system: fromSystem })
44
62
  }
45
63
  }
46
64
  }
47
65
  return [...found.values()].sort((left, right) => left.slug.localeCompare(right.slug))
48
66
  }
49
67
 
50
- function resolve(root, slug) {
68
+ function find(root, slug) {
51
69
  if (!SLUG.test(slug || '')) throw new Error(`agente inválido: ${slug || '(vacío)'}`)
70
+ const system = systemCatalog(root)
52
71
  const matches = []
53
72
  for (const type of types(root)) {
54
- const own = path.join(root, 'agents', type, slug)
55
- const system = path.join(root, 'agents', type, 'system', slug)
73
+ const own = path.join(projectCatalog(root), type, slug)
74
+ const shipped = system ? path.join(system, type, 'system', slug) : ''
56
75
  // Dentro de un tipo la precedencia está definida; entre tipos no, y por eso se acumulan.
57
- if (fs.existsSync(path.join(own, 'SKILL.md'))) matches.push(own)
58
- else if (fs.existsSync(path.join(system, 'SKILL.md'))) matches.push(system)
76
+ if (fs.existsSync(path.join(own, 'SKILL.md'))) matches.push({ dir: own, system: false })
77
+ else if (shipped && fs.existsSync(path.join(shipped, 'SKILL.md'))) {
78
+ matches.push({ dir: shipped, system: true })
79
+ }
59
80
  }
60
81
  if (!matches.length) throw new Error(`no existe agents/<tipo>/${slug}/SKILL.md`)
61
82
  if (matches.length > 1) {
62
- throw new Error(`agente ambiguo ${slug}: ${matches.map((dir) => path.relative(root, dir)).join(', ')}`)
83
+ throw new Error(`agente ambiguo ${slug}: ${matches.map((entry) => entry.dir).join(', ')}`)
63
84
  }
64
85
  return matches[0]
65
86
  }
66
87
 
67
- module.exports = { list, resolve, types }
88
+ function resolve(root, slug) {
89
+ return find(root, slug).dir
90
+ }
91
+
92
+ module.exports = { find, list, projectCatalog, resolve, systemCatalog, types }
@@ -19,11 +19,28 @@ function agentRoot(root, agent) {
19
19
  return catalog.resolve(root, agent)
20
20
  }
21
21
 
22
+ // Un cargo del sistema vive dentro del paquete: escribir ahí perdería el informe en el próximo
23
+ // `npm ci`, y además duplicaría en cada empresa una investigación sobre la profesión que se hace
24
+ // mejor una sola vez. Lo que sí es de esta empresa es su contexto, y ese tiene otro lugar.
25
+ function assertWritable(root, agent) {
26
+ const found = catalog.find(root, agent)
27
+ // Lo que decide no es si el cargo es del sistema, sino si vive dentro de este repositorio. En el
28
+ // toolkit los cargos del sistema son propios y se aprenden acá; en una instancia vienen del
29
+ // paquete, y escribir ahí se pierde en el próximo `npm ci` o en el próximo upgrade.
30
+ const own = path.resolve(catalog.projectCatalog(root))
31
+ if (path.resolve(found.dir).startsWith(`${own}${path.sep}`)) return found.dir
32
+ throw new Error(
33
+ `${agent} es un cargo que trae Cauce y su aprendizaje se hace en el toolkit, no acá.\n` +
34
+ ` Lo que este cargo debe saber de esta empresa va en organization/roles/${agent}.md.\n` +
35
+ ` Para tener una versión propia del cargo, escribila en agents/roles/${agent}/.`,
36
+ )
37
+ }
38
+
22
39
  function isoDate(now = new Date()) { return now.toISOString().slice(0, 10) }
23
40
  function month(now = new Date()) { return now.toISOString().slice(0, 7) }
24
41
 
25
42
  function prepareReport(root, agent, now = new Date()) {
26
- const reports = path.join(agentRoot(root, agent), 'learning', 'reports')
43
+ const reports = path.join(assertWritable(root, agent), 'learning', 'reports')
27
44
  const file = path.join(reports, `${isoDate(now)}.md`)
28
45
  fs.mkdirSync(reports, { recursive: true })
29
46
  if (fs.existsSync(file)) return { file, created: false }
@@ -51,7 +68,7 @@ status: draft
51
68
  }
52
69
 
53
70
  function prepareProposal(root, agent, now = new Date()) {
54
- const target = agentRoot(root, agent)
71
+ const target = assertWritable(root, agent)
55
72
  const proposalDir = path.join(target, 'learning', 'proposals')
56
73
  const file = path.join(proposalDir, `${month(now)}.md`)
57
74
  fs.mkdirSync(proposalDir, { recursive: true })