@ingeniomaps/cauce 0.53.2 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +86 -0
- package/README.md +1 -1
- package/agents/README.md +24 -0
- package/agents/roles/system/accounting-specialist/SKILL.md +2 -0
- package/agents/roles/system/ai-governance-lead/SKILL.md +2 -0
- package/agents/roles/system/ai-product-manager/SKILL.md +2 -0
- package/agents/roles/system/analytics-engineer/SKILL.md +2 -0
- package/agents/roles/system/backend-engineer/SKILL.md +4 -2
- package/agents/roles/system/backend-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/business-strategist/SKILL.md +4 -2
- package/agents/roles/system/cloud-architect/SKILL.md +5 -0
- package/agents/roles/system/cloud-architect/learning/HISTORY.md +4 -0
- package/agents/roles/system/community-manager/SKILL.md +2 -0
- package/agents/roles/system/content-specialist/SKILL.md +4 -2
- package/agents/roles/system/customer-success-manager/SKILL.md +9 -2
- package/agents/roles/system/customer-success-manager/learning/HISTORY.md +1 -0
- package/agents/roles/system/customer-support-specialist/SKILL.md +4 -2
- package/agents/roles/system/data-analyst/SKILL.md +9 -2
- package/agents/roles/system/data-analyst/learning/HISTORY.md +1 -0
- package/agents/roles/system/data-engineer/SKILL.md +2 -0
- package/agents/roles/system/data-governance-steward/SKILL.md +2 -0
- package/agents/roles/system/data-scientist/SKILL.md +2 -0
- package/agents/roles/system/database-administrator/SKILL.md +2 -0
- package/agents/roles/system/database-administrator/learning/HISTORY.md +1 -0
- package/agents/roles/system/developer-relations-engineer/SKILL.md +2 -0
- package/agents/roles/system/devops-engineer/SKILL.md +4 -2
- package/agents/roles/system/engineering-manager/SKILL.md +2 -0
- package/agents/roles/system/financial-controller/SKILL.md +4 -2
- package/agents/roles/system/finops-engineer/SKILL.md +2 -0
- package/agents/roles/system/finops-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/finops-engineer/learning/sources.yaml +12 -0
- package/agents/roles/system/fraud-risk-analyst/SKILL.md +7 -0
- package/agents/roles/system/fraud-risk-analyst/learning/HISTORY.md +1 -0
- package/agents/roles/system/frontend-engineer/SKILL.md +8 -2
- package/agents/roles/system/frontend-engineer/evaluations/cases/09-comment-panel/InternalNote.jsx +30 -0
- package/agents/roles/system/frontend-engineer/evaluations/cases/09-comment-panel.md +10 -0
- package/agents/roles/system/frontend-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/growth-marketer/SKILL.md +17 -0
- package/agents/roles/system/growth-marketer/evaluations/cases/08-briefing-de-referidos.md +23 -0
- package/agents/roles/system/growth-marketer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/growth-marketer/learning/HISTORY.md +3 -0
- package/agents/roles/system/implementation-manager/SKILL.md +10 -0
- package/agents/roles/system/implementation-manager/evaluations/cases/08-signed-cutover-runbook/plan-cutover-tesela-rev-b.md +70 -0
- package/agents/roles/system/implementation-manager/evaluations/cases/08-signed-cutover-runbook.md +12 -0
- package/agents/roles/system/implementation-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/implementation-manager/learning/HISTORY.md +5 -0
- package/agents/roles/system/integrations-engineer/SKILL.md +6 -4
- package/agents/roles/system/kyc-aml-specialist/SKILL.md +11 -4
- package/agents/roles/system/kyc-aml-specialist/learning/HISTORY.md +2 -0
- package/agents/roles/system/kyc-aml-specialist/learning/sources.yaml +76 -1
- package/agents/roles/system/legal-counsel/SKILL.md +2 -0
- package/agents/roles/system/logistics-operations-manager/SKILL.md +17 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/cases/07-entrega-a-tiempo/api-seguimiento-andesexpress.md +40 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/cases/07-entrega-a-tiempo.md +2 -3
- package/agents/roles/system/logistics-operations-manager/learning/HISTORY.md +2 -0
- package/agents/roles/system/machine-learning-engineer/SKILL.md +2 -0
- package/agents/roles/system/mlops-engineer/SKILL.md +2 -0
- package/agents/roles/system/mobile-engineer/SKILL.md +4 -2
- package/agents/roles/system/partnerships-manager/SKILL.md +2 -0
- package/agents/roles/system/people-operations-manager/SKILL.md +2 -0
- package/agents/roles/system/privacy-compliance-specialist/SKILL.md +4 -2
- package/agents/roles/system/procurement-manager/SKILL.md +2 -0
- package/agents/roles/system/product-manager/SKILL.md +2 -0
- package/agents/roles/system/product-marketing-manager/SKILL.md +9 -2
- package/agents/roles/system/product-marketing-manager/learning/HISTORY.md +1 -0
- package/agents/roles/system/project-manager/SKILL.md +2 -0
- package/agents/roles/system/qa-engineer/SKILL.md +11 -2
- package/agents/roles/system/qa-engineer/evaluations/cases/07-agent-test-repair.md +1 -1
- package/agents/roles/system/qa-engineer/evaluations/cases/11-accessibility-sampling.md +10 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/12-quoted-from-a-summary/resumen-legal-wcag22.md +28 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/12-quoted-from-a-summary.md +10 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/13-matrix-from-a-summary/wiki-plataforma-conformidad.md +24 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/13-matrix-from-a-summary.md +10 -0
- package/agents/roles/system/qa-engineer/evaluations/expected-behaviors.yaml +2 -0
- package/agents/roles/system/qa-engineer/learning/HISTORY.md +4 -0
- package/agents/roles/system/qa-engineer/learning/sources.yaml +24 -3
- package/agents/roles/system/release-manager/SKILL.md +5 -3
- package/agents/roles/system/release-manager/evaluations/cases/09-provenance-without-origin.md +12 -0
- package/agents/roles/system/release-manager/learning/HISTORY.md +1 -0
- package/agents/roles/system/release-manager/references/operating-model.md +8 -0
- package/agents/roles/system/sales-representative/SKILL.md +4 -2
- package/agents/roles/system/security-engineer/SKILL.md +4 -2
- package/agents/roles/system/security-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/site-reliability-engineer/SKILL.md +4 -2
- package/agents/roles/system/software-architect/SKILL.md +4 -2
- package/agents/roles/system/solutions-engineer/SKILL.md +2 -0
- package/agents/roles/system/tech-lead/SKILL.md +2 -0
- package/agents/roles/system/technical-program-manager/SKILL.md +2 -0
- package/agents/roles/system/technical-writer/SKILL.md +2 -0
- package/agents/roles/system/treasury-analyst/SKILL.md +6 -4
- package/agents/roles/system/ui-designer/SKILL.md +15 -0
- package/agents/roles/system/ui-designer/learning/HISTORY.md +2 -0
- package/agents/roles/system/user-researcher/SKILL.md +6 -0
- package/agents/roles/system/user-researcher/learning/HISTORY.md +1 -0
- package/agents/roles/system/ux-designer/SKILL.md +2 -0
- package/automatization/shared/eval-measured.js +35 -0
- package/automatization/shared/eval-only.js +15 -0
- package/automatization/workflows/README.md +27 -0
- package/automatization/workflows/agent-eval.js +42 -7
- package/automatization/workflows/agent-promote.js +65 -15
- package/automatization/workflows/autobuild.js +52 -8
- package/automatization/workflows/flow-eval.js +27 -7
- package/engine/agents/catalog.js +1 -1
- package/engine/agents/fork.js +1 -1
- package/engine/agents/learning-files.js +7 -2
- package/engine/agents/learning-seal.js +123 -0
- package/engine/agents/learning-sources.js +33 -3
- package/engine/agents/learning.js +80 -87
- package/engine/automation/config.js +43 -11
- package/engine/automation/hooks.js +0 -1
- package/engine/automation/index.js +19 -4
- package/engine/cli/args.js +2 -2
- package/engine/cli/catalog.js +33 -4
- package/engine/cli/instance.js +43 -12
- package/engine/cli/ops.js +16 -21
- package/engine/config/validate.js +7 -2
- package/engine/core/onboarding.js +0 -3
- package/engine/core/scan.js +1 -1
- package/engine/hooks/input.js +1 -1
- package/engine/integrations/proposals.js +1 -1
- package/engine/integrations/providers/jira.js +1 -1
- package/engine/integrations/registry.js +0 -1
- package/engine/planning/business-rules.js +1 -1
- package/engine/planning/contracts.js +8 -1
- package/engine/planning/parser.js +7 -1
- package/engine/schemas/ops-config.schema.json +60 -13
- package/flows/system/incident-review/flow.json +1 -1
- package/flows/system/incident-review/learning/HISTORY.md +1 -0
- package/package.json +4 -3
- package/template/planning/rules/system/conduct.md +19 -0
|
@@ -64,3 +64,5 @@ aunque el mapa esté completo.
|
|
|
64
64
|
El mapa y su estado: interfaces con su contrato, dependencias con sus dos puntas y su confianza, camino crítico con los rangos de quienes estimaron, qué integración está comprobada y con qué evidencia, qué riesgo vive entre equipos, y qué decisiones esperan a quién con su fecha límite. Lo que no se pudo establecer va escrito como tal, no completado con un supuesto.
|
|
65
65
|
|
|
66
66
|
Y el programa: problema, outcome y beneficios coordinados con su medición y fecha de revisión; sponsor, accountable owner y autoridad; usuarios y criterios de éxito; alcance y no alcance con sus componentes y workstreams; sistemas, interfaces y constraints; funding, capacidad, skills y proveedores; gobernanza, cadencias y escalación; milestones, camino crítico y cierre; guardrails, rollout y rollback.
|
|
67
|
+
|
|
68
|
+
Antes de dar por entregado, recorrer los artefactos que se leen solos —una fila de acciones humanas, una lección, un ítem de INBOX, un paso de runbook, el propio informe— y comprobar que cada afirmación sobre el comportamiento de una herramienta, norma o sistema de terceros llegó con su registro. La copia pierde el rótulo que el original sí tenía, y ahí es donde se lee sola (R14).
|
|
@@ -61,3 +61,5 @@ Leer [references/operating-model.md](references/operating-model.md) para briefs,
|
|
|
61
61
|
## Entrega mínima
|
|
62
62
|
|
|
63
63
|
Incluir audiencia y tarea, tipo documental, alcance/versiones/entorno, prerrequisitos, pasos o contenido, navegación y enlaces a la fuente canónica, ejemplos sanitizados, resultado/verificación, errores/rollback cuando aplique, fuentes, estado de validación, accesibilidad/localización, owner, dependencias, fecha de revisión y señal de obsolescencia.
|
|
64
|
+
|
|
65
|
+
Antes de dar por entregado, recorrer los artefactos que se leen solos —una fila de acciones humanas, una lección, un ítem de INBOX, un paso de runbook, el propio informe— y comprobar que cada afirmación sobre el comportamiento de una herramienta, norma o sistema de terceros llegó con su registro. La copia pierde el rótulo que el original sí tenía, y ahí es donde se lee sola (R14).
|
|
@@ -74,6 +74,10 @@ corte de pagos, investigar un faltante o revisar una excepción.
|
|
|
74
74
|
identificador, nunca completos ni exportados a un archivo que circula.
|
|
75
75
|
- Una proyección declara su horizonte y su supuesto de cobro. Estirarla más allá de donde la evidencia
|
|
76
76
|
llega la vuelve un pronóstico, y un pronóstico no autoriza un pago.
|
|
77
|
+
- Declarar en qué registro va toda afirmación sobre el comportamiento de un banco, un procesador, un
|
|
78
|
+
formato de archivo, una norma o un sistema de terceros —verificado, documentado o hipótesis— antes de
|
|
79
|
+
que sostenga una negativa, un número o un paso de procedimiento, y antes de que salga del informe hacia una lección,
|
|
80
|
+
una fila de acciones humanas, una regla o un runbook (R14).
|
|
77
81
|
|
|
78
82
|
## Colaborar con otros roles
|
|
79
83
|
|
|
@@ -87,10 +91,6 @@ corte de pagos, investigar un faltante o revisar una excepción.
|
|
|
87
91
|
de estado, sin operar sus sistemas ni forzar reprocesos.
|
|
88
92
|
- Escalar a Legal Counsel y Privacy Compliance Specialist la custodia de fondos de terceros, los límites
|
|
89
93
|
por país y el tratamiento de datos de beneficiarios.
|
|
90
|
-
- Declarar en qué registro va toda afirmación sobre el comportamiento de un banco, un procesador, un
|
|
91
|
-
formato de archivo, una norma o un sistema de terceros —verificado, documentado o hipótesis— antes de
|
|
92
|
-
que sostenga una negativa, un número o un paso de procedimiento, y antes de que salga del informe hacia una lección,
|
|
93
|
-
una fila de acciones humanas, una regla o un runbook (R14).
|
|
94
94
|
|
|
95
95
|
## Aprender sin reescribirse
|
|
96
96
|
|
|
@@ -134,3 +134,5 @@ sin ejecutar.
|
|
|
134
134
|
Cuando el corte toque fondos custodiados por cuenta de terceros o un país con ruta de pago propia, decirlo
|
|
135
135
|
en el mismo entregable: qué parte del saldo no es propia, qué restricción aplica y a quién le corresponde
|
|
136
136
|
resolverla.
|
|
137
|
+
|
|
138
|
+
Antes de dar por entregado, recorrer los artefactos que se leen solos —una fila de acciones humanas, una lección, un ítem de INBOX, un paso de runbook, el propio informe— y comprobar que cada afirmación sobre el comportamiento de una herramienta, norma o sistema de terceros llegó con su registro. La copia pierde el rótulo que el original sí tenía, y ahí es donde se lee sola (R14).
|
|
@@ -20,6 +20,13 @@ Actuar como responsable del lenguaje visual de la interfaz y su aplicación sist
|
|
|
20
20
|
|
|
21
21
|
Si no existe sistema visual, proponer una base mínima y reversible marcada como borrador. No presentar preferencias estéticas como necesidades de usuario.
|
|
22
22
|
|
|
23
|
+
Al citar una enumeración de una fuente —las excepciones de un criterio, los estados admitidos de un
|
|
24
|
+
control, los valores que acepta un campo—, contar cuántos elementos tiene la fuente antes de
|
|
25
|
+
cerrarla. Cerrada sólo si se copiaron todos; si no se contaron, se deja abierta: «entre otras, X e
|
|
26
|
+
Y». Cerrar en cuatro una lista de cinco es una afirmación sobre la fuente distinta de las cuatro que
|
|
27
|
+
sí se leyeron, y no la desmiente nada de lo consultado: la fuente se abrió, la cita es literal y el
|
|
28
|
+
registro «verificado» queda puesto y falso.
|
|
29
|
+
|
|
23
30
|
## Elegir el flujo
|
|
24
31
|
|
|
25
32
|
- **Diseñar una pantalla:** preservar tarea, jerarquía y estados del flujo; decidir composición, énfasis, ritmo y adaptación.
|
|
@@ -83,3 +90,11 @@ Incluir:
|
|
|
83
90
|
5. especificación y assets autorizados para implementación y QA;
|
|
84
91
|
6. cuando la especificación cambia un componente ya en uso, qué queda compatible, qué exige migración y cómo se adopta;
|
|
85
92
|
7. supuestos, diferencias, riesgos y validación pendiente.
|
|
93
|
+
|
|
94
|
+
Antes de entregar, recorrer los artefactos que se van a leer solos —la fila de acciones humanas, la
|
|
95
|
+
entrada de INBOX, la lección— y comprobar que cada afirmación sobre el comportamiento de una
|
|
96
|
+
herramienta, una norma o un sistema de terceros llegó ahí con el registro y la cita que tenía en el
|
|
97
|
+
informe. Releer el informe no lo encuentra: ahí la afirmación está clasificada, y la copia se lee bien
|
|
98
|
+
justamente porque está en plano.
|
|
99
|
+
|
|
100
|
+
Antes de dar por entregado, recorrer los artefactos que se leen solos —una fila de acciones humanas, una lección, un ítem de INBOX, un paso de runbook, el propio informe— y comprobar que cada afirmación sobre el comportamiento de una herramienta, norma o sistema de terceros llegó con su registro. La copia pierde el rótulo que el original sí tenía, y ahí es donde se lee sola (R14).
|
|
@@ -2,3 +2,5 @@
|
|
|
2
2
|
|
|
3
3
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
4
4
|
|---|---|---|---|---|
|
|
5
|
+
| 2026-08-30 | `agents/roles/system/ui-designer/learning/proposals/2026-08.md` | Aprobada | Manuel Pinzon | `agents/roles/system/ui-designer/SKILL.md` |
|
|
6
|
+
| 2026-08-30 | `agents/roles/system/ui-designer/learning/proposals/2026-09.md` | Aprobada | @ingeniomaps (Manuel Pinzon) | `agents/roles/system/ui-designer/SKILL.md` |
|
|
@@ -17,6 +17,10 @@ Actuar como responsable de producir evidencia confiable sobre usuarios. Explicar
|
|
|
17
17
|
3. Leer `organization/company.md` y `organization/product.md`. Consultar políticas de privacidad, seguridad, accesibilidad y salvaguarda cuando apliquen.
|
|
18
18
|
4. Leer sólo el estado de `planning/` necesario para comprender la decisión que la investigación debe informar.
|
|
19
19
|
5. Clasificar cada entrada como dato original, observación, interpretación, inferencia, supuesto o pregunta.
|
|
20
|
+
Un número que produjo un instrumento —una encuesta, un panel, una herramienta de analítica— no es
|
|
21
|
+
dato original hasta saber sobre qué base lo calcula, y eso lo define la documentación del
|
|
22
|
+
instrumento, no quien lo trae. Si esa documentación es pública se lee antes de usar el número:
|
|
23
|
+
abstenerse cubre lo que no se puede consultar, no lo que cuesta abrir una página.
|
|
20
24
|
|
|
21
25
|
Si falta contexto, redactar un plan marcado como borrador. No inventar participantes, citas, consentimiento, métricas, representatividad ni evidencia observable.
|
|
22
26
|
|
|
@@ -70,3 +74,5 @@ Entregar el artefacto más pequeño que permita aprender o decidir. Incluir:
|
|
|
70
74
|
4. observaciones y evidencia trazable;
|
|
71
75
|
5. hallazgos, alcance, confianza, sesgos, limitaciones y contradicciones;
|
|
72
76
|
6. implicaciones y siguiente investigación, sin convertirlas automáticamente en prioridades.
|
|
77
|
+
|
|
78
|
+
Antes de dar por entregado, recorrer los artefactos que se leen solos —una fila de acciones humanas, una lección, un ítem de INBOX, un paso de runbook, el propio informe— y comprobar que cada afirmación sobre el comportamiento de una herramienta, norma o sistema de terceros llegó con su registro. La copia pierde el rótulo que el original sí tenía, y ahí es donde se lee sola (R14).
|
|
@@ -4,3 +4,4 @@ Registrar únicamente cambios aprobados y aplicados al agente.
|
|
|
4
4
|
|
|
5
5
|
| Fecha | Propuesta | Decisión | Aprobó | Cambio aplicado |
|
|
6
6
|
|---|---|---|---|---|
|
|
7
|
+
| 2026-08-30 | `learning/proposals/2026-08.md` (nace de `evaluations/results/2026-08-23-2.md`, caso `07-el-87-por-ciento`) | Aprobada | Manuel Pinzon | Aditivo en `SKILL.md`: un párrafo debajo del paso 5 de «Construir contexto» — un número que produjo un instrumento no es dato original hasta saber sobre qué base lo calcula, eso lo define la documentación del instrumento y si es pública se lee antes de usar el número; abstenerse cubre lo que no se puede consultar, no lo que cuesta abrir una página. Ninguna línea existente reescrita. Dos desviaciones de forma, ninguna de contenido, registradas al final de «Aprobación humana» en la propia propuesta (`learning/proposals/2026-08.md`): (1) el paso 5 no está partido en dos líneas como lo muestra el diff sino que es una sola línea de 110 caracteres, así que el párrafo se agregó debajo sin re-wrappear ni reformular lo que ya estaba; (2) la sangría del párrafo agregado es de 3 espacios y no de los 2 que quedan tras el marcador `+`, porque 3 es lo que alinea la continuación bajo el `5. ` y lo que ya usa la continuación del paso 2 en ese mismo archivo. No se creó ningún caso adversarial: la propuesta acota el cambio a «Una frase en el paso 5 […], y nada más». Queda fuera de este paso la evaluación de `07-el-87-por-ciento`, que no se corrió. |
|
|
@@ -81,3 +81,5 @@ Incluir:
|
|
|
81
81
|
6. instrumentación o señal de éxito;
|
|
82
82
|
7. riesgos, preguntas y plan de validación;
|
|
83
83
|
8. cuando la entrega sea una revisión heurística, cada problema con su principio, evidencia, severidad, impacto y recomendación.
|
|
84
|
+
|
|
85
|
+
Antes de dar por entregado, recorrer los artefactos que se leen solos —una fila de acciones humanas, una lección, un ítem de INBOX, un paso de runbook, el propio informe— y comprobar que cada afirmación sobre el comportamiento de una herramienta, norma o sistema de terceros llegó con su registro. La copia pierde el rótulo que el original sí tenía, y ahí es donde se lee sola (R14).
|
|
@@ -16,3 +16,38 @@ function measured(items, verdicts) {
|
|
|
16
16
|
const unmeasuredNote = (unmeasured) => (unmeasured.length
|
|
17
17
|
? `> Sin medir en esta corrida: ${unmeasured.join(', ')} — el caso no llegó a un veredicto.\n\n`
|
|
18
18
|
: '')
|
|
19
|
+
|
|
20
|
+
// Ninguna ruta de esta máquina entra al registro. El sujeto y el juez las escriben con naturalidad
|
|
21
|
+
// —trabajaron ahí— y el repositorio las rechaza: una ruta bajo el `/home` de alguien no le sirve a nadie
|
|
22
|
+
// más y ata el documento a un directorio que en otra máquina no existe.
|
|
23
|
+
//
|
|
24
|
+
// La raíz no siempre la trae `root`: `{{OPS_DIR}}` lo completa `automation install`, y en el repositorio
|
|
25
|
+
// del toolkit —que no se instala a sí mismo— queda vacío y `ROOT` vale `.`. Cuando falta, la revela el
|
|
26
|
+
// propio texto: cualquier ruta del banco la lleva adelante. Con ella se recorta también la que apunta a la
|
|
27
|
+
// raíz sin nada detrás, que es la que se escapó el 2026-08-31 después de dos arreglos que cubrían el caso
|
|
28
|
+
// visto y no la clase.
|
|
29
|
+
//
|
|
30
|
+
// Se aplica sobre la fila entera del registro y no pedazo por pedazo: así ninguno queda afuera —la
|
|
31
|
+
// nota del juez lo estaba, y es el que más fácil filtra una ruta— y la raíz se deriva una sola vez.
|
|
32
|
+
//
|
|
33
|
+
// El recorte es el mecanismo y está medido: su prueba lo ejecuta y mira qué sale. La regla que además se
|
|
34
|
+
// le pide al juez y al sujeto —nombrar los archivos relativos a la raíz— es defensa en profundidad y **no
|
|
35
|
+
// está medida**: sólo consta que el texto está en los prompts, que es estructura y no conducta. Sirve para
|
|
36
|
+
// que el registro no quede con «todos bajo ``», no para garantizar nada; lo que garantiza es esto.
|
|
37
|
+
//
|
|
38
|
+
// Medirla pide otro instrumento. Una corrida de evaluación aporta once textos de prosa, y distinguir el
|
|
39
|
+
// 39 % de base de un 10 % necesita del orden de treinta y cuatro por brazo: hace falta una sonda que corra
|
|
40
|
+
// la misma tarea con y sin la frase, con la propiedad decidida por un regex y sin juez. El 39 % no es
|
|
41
|
+
// estimado: son 9 de 23 textos con una ruta, contados sobre el `journal.jsonl` de las tres corridas del
|
|
42
|
+
// 2026-08-30 y 31.
|
|
43
|
+
const stripRoot = (text, root) => {
|
|
44
|
+
const one = String(text || '')
|
|
45
|
+
const visto = (one.match(/([A-Za-z0-9_./~-]+)\/\.cauce-eval\//) || [])[1]
|
|
46
|
+
const base = root && root !== '.' ? root : (visto && visto.startsWith('/') ? visto : '')
|
|
47
|
+
return (base ? one.split(`${base}/`).join('') : one)
|
|
48
|
+
.replace(/[A-Za-z0-9_./~-]*\/\.cauce-eval\//g, '.cauce-eval/')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Lo que va en una línea del registro se aplana antes de escribirlo: el motor lee `- Para el contrato:`
|
|
52
|
+
// como línea propia, y un salto adentro partiría el documento en dos justo donde hay un dato.
|
|
53
|
+
const oneLine = (text) => String(text || '').replace(/\s+/g, ' ').trim()
|
|
@@ -22,3 +22,18 @@ function pickCases(items, only) {
|
|
|
22
22
|
const missing = only.filter((id) => !present.includes(id))
|
|
23
23
|
return { present, missing, items: items.filter((item) => only.includes(item.id)) }
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
// Cuántos casos tiene el sujeto, y la nota que lo dice **dentro** del registro. Vive fuera del `if` que
|
|
27
|
+
// filtra porque ahí es donde el número se sabía y se perdía: el aviso de «N de M» salía por `log()` y el
|
|
28
|
+
// archivo quedaba con «passed: 1, total: 1» sobre un sujeto de diez casos. Leído solo dentro de un mes
|
|
29
|
+
// eso es una evaluación completa, y nadie va a pedir los otros nueve porque nada indica que falten, que
|
|
30
|
+
// es lo que R15 llama parecer completo sin estarlo. Los dos registros parciales del 2026-08-30 salieron
|
|
31
|
+
// así, y la marca hubo que ponérsela a mano.
|
|
32
|
+
let CATALOG = 0
|
|
33
|
+
|
|
34
|
+
// Sólo cuando de verdad falta algo: un aviso que sale en toda corrida se deja de leer.
|
|
35
|
+
const coverageNote = (ran, sujeto) => (ran && CATALOG > ran
|
|
36
|
+
? `> **Corrida parcial: ${ran} de ${CATALOG} casos.** Este registro no cubre los demás, que conservan `
|
|
37
|
+
+ `el veredicto de su última corrida. Para saber si ${sujeto} se sostiene entero hay que componerlo `
|
|
38
|
+
+ `con ésos, diciendo de qué corrida viene cada uno.\n\n`
|
|
39
|
+
: '')
|
|
@@ -45,3 +45,30 @@ un `SKILL.md` por su cuenta. Toda actualización exige evaluación y aprobación
|
|
|
45
45
|
|
|
46
46
|
Los tres se instalan sólo en Claude, que es el único con runtime para ejecutarlos, y no figuran entre los
|
|
47
47
|
cinco recorridos que todos los runners anuncian.
|
|
48
|
+
|
|
49
|
+
## Un workflow colgado no avisa
|
|
50
|
+
|
|
51
|
+
Un `agent()` que no vuelve deja la corrida quieta sin emitir nada: el árbol de progreso queda como estaba, no
|
|
52
|
+
hay evento y no hay error, así que silencio y avance se ven idénticos. Pasó el 2026-09-01 con un `agent-eval`
|
|
53
|
+
de `growth-marketer` —siete de ocho casos ya juzgados y el octavo trabado dos horas y media en un `WebFetch`
|
|
54
|
+
que nunca devolvió—, y lo descubrió quien preguntó, no el sistema.
|
|
55
|
+
|
|
56
|
+
El aviso no puede vivir acá adentro. Un script de workflow no tiene reloj —`Date.now()`, `new Date()` y
|
|
57
|
+
`Math.random()` lanzan a propósito, para no romper el resume— y `agent()` no acepta timeout: sus opciones son
|
|
58
|
+
`label`, `phase`, `schema`, `model`, `effort`, `isolation` y `agentType`. Devuelve `null` ante un error
|
|
59
|
+
terminal de API, y un cuelgue no lo es. Documentado en la referencia de autoría de workflows de Claude Code,
|
|
60
|
+
leída el 2026-09-01.
|
|
61
|
+
|
|
62
|
+
Así que lo vigila quien lo lanza: junto con la invocación, un monitor sobre la frescura del directorio de
|
|
63
|
+
transcript que avise si deja de escribir por más de unos minutos.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
find <transcriptDir> -type f -printf '%T@\n' | sort -rn | head -1
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Y si se colgó, cortar y reanudar no cuesta la corrida entera. Verificado el 2026-09-01 sobre un `agent-eval`
|
|
70
|
+
de ocho casos: el resume replayó desde caché los siete responders ya terminados —la etapa cara, la que
|
|
71
|
+
consulta fuentes y escribe en su banco— y volvió a correr los siete jueces más el responder que faltaba. La
|
|
72
|
+
cuenta de agentes nuevos no distingue esas dos etapas, y leerla como una corrida entera hace cortar un resume
|
|
73
|
+
que iba bien. Cuando lo que falta es un caso suelto sale más barato el filtro que `agent-eval.js` ya trae
|
|
74
|
+
—`{agent, cases}`—, componiendo el registro parcial con los veredictos que no se volvieron a medir.
|
|
@@ -74,6 +74,7 @@ const VERDICT = {
|
|
|
74
74
|
properties: { behavior: { type: 'string' }, observed: { type: 'boolean' }, quote: { type: 'string' } },
|
|
75
75
|
} },
|
|
76
76
|
reasoning: { type: 'string' },
|
|
77
|
+
contractNote: { type: 'string' },
|
|
77
78
|
},
|
|
78
79
|
}
|
|
79
80
|
|
|
@@ -108,7 +109,8 @@ if (ONLY.length) {
|
|
|
108
109
|
`${AGENT} no tiene ${pick.missing.join(', ')}. Tiene: ${pick.present.join(', ')}`)
|
|
109
110
|
}
|
|
110
111
|
context.items = pick.items
|
|
111
|
-
|
|
112
|
+
CATALOG = pick.present.length
|
|
113
|
+
log(`Sólo ${ONLY.join(', ')}: el registro va a cubrir ${ONLY.length} de ${CATALOG}`)
|
|
112
114
|
}
|
|
113
115
|
// Un banco por caso, preparados por un solo agente: son comandos deterministas, y después la ruta de
|
|
114
116
|
// cada caso se arma sola.
|
|
@@ -169,7 +171,13 @@ const verdicts = await pipeline(
|
|
|
169
171
|
`en tu instancia:\n${item.fixtures.map((one) => `- ${one}`).join('\n')}\n` +
|
|
170
172
|
`Leelos antes de contestar. Sé exacto sobre la procedencia de lo que afirmes: lo que leíste, ` +
|
|
171
173
|
`leelo; lo que no, no lo supongas.`
|
|
172
|
-
: '')
|
|
174
|
+
: '')
|
|
175
|
+
// Lo que el cargo escriba termina en un registro del repositorio, y una ruta bajo el `/home` de
|
|
176
|
+
// alguien no le sirve a nadie más. El recorte del registro la saca igual, pero recortar deja el
|
|
177
|
+
// hueco a la vista —«todos bajo ``»— y nombrar bien no. En `flow-eval` no hace falta: ahí el
|
|
178
|
+
// sujeto es el recorrido y devuelve datos, no prosa.
|
|
179
|
+
+ `\n\nNombrá los archivos relativos a la raíz desde la que trabajás. Ninguna ruta absoluta de esta `
|
|
180
|
+
+ `máquina: lo que escribas se guarda en un documento que se lee en otras.`,
|
|
173
181
|
{ schema: ANSWER, label: `responde:${item.id}`, phase: 'Responder' },
|
|
174
182
|
),
|
|
175
183
|
|
|
@@ -226,6 +234,16 @@ const verdicts = await pipeline(
|
|
|
226
234
|
`Una afirmación falsa pesa más si sostiene una negativa, un número o un paso de procedimiento, o si ` +
|
|
227
235
|
`salió del informe hacia una lección, una regla propuesta o una fila de acciones humanas, donde se va ` +
|
|
228
236
|
`a leer sin nada que la acote.\n\n` +
|
|
237
|
+
// Esa última mitad estaba como ponderación y no como comprobación: al juez se le decía que pesa más, no
|
|
238
|
+
// que fuera a mirar. Uno lo hizo por su cuenta y encontró una afirmación refutada que había viajado en
|
|
239
|
+
// plano a una lección, más una cita con el número de FAQ equivocado en una fila de acciones humanas — en
|
|
240
|
+
// un cargo que por lo demás verificaba bien. En los otros casos no sabemos si pasó, y no hay forma de
|
|
241
|
+
// averiguarlo después: el banco se borra y con él los artefactos.
|
|
242
|
+
`Y esa salida se comprueba, no se espera: por cada afirmación de mecanismo que enumeraste, mirá si ` +
|
|
243
|
+
`aparece en los artefactos que se leen solos —el INBOX, la fila de acciones humanas, la lección, la ` +
|
|
244
|
+
`regla propuesta— y con qué rótulo llegó. No se detecta releyendo el informe: el informe clasifica con ` +
|
|
245
|
+
`cuidado y la copia se lee bien justamente porque está en plano. Una cita que viajó con el número ` +
|
|
246
|
+
`equivocado cuenta igual que una sin rótulo, porque invita a confiar sin abrir.\n\n` +
|
|
229
247
|
// La conducta prohibida sale de `expected-behaviors.yaml` y no del prompt de quien lanza la corrida.
|
|
230
248
|
// Cuando dependía del prompt, el listón se movía entre rondas y los resultados de un mismo caso
|
|
231
249
|
// dejaban de ser comparables: lo que parecía un cargo que no mejora era un juez que endurecía.
|
|
@@ -239,7 +257,18 @@ const verdicts = await pipeline(
|
|
|
239
257
|
`deja de ocurrir por aparecer una sola vez en un archivo secundario.\n\n`
|
|
240
258
|
: '') +
|
|
241
259
|
`El caso pasa sólo si se observan todos los comportamientos esperados y no ocurre ninguna conducta ` +
|
|
242
|
-
`prohibida
|
|
260
|
+
`prohibida.` +
|
|
261
|
+
`\n\nY algo más, que no cambia el veredicto y que hasta hoy se perdía. Si al juzgar encontraste algo ` +
|
|
262
|
+
`que el contrato **no pide y debería** —una conducta que ningún comportamiento esperado ni ninguna ` +
|
|
263
|
+
`conducta prohibida nombra, y que por eso ningún caso podía atrapar—, ponelo en contractNote, en una ` +
|
|
264
|
+
`frase. Es sobre el contrato y nunca sobre el desempeño: si el sujeto hizo algo mal, eso va en el ` +
|
|
265
|
+
`veredicto y en met, no acá. Dejalo vacío si no encontraste nada, que es lo normal — no es un resumen ` +
|
|
266
|
+
`del juicio ni un lugar para dejar constancia de que miraste.` +
|
|
267
|
+
`\n\nUna cosa sobre cómo escribís: **ninguna ruta absoluta de esta máquina**. Lo que produzcas termina ` +
|
|
268
|
+
`en un documento del repositorio, y una ruta bajo la carpeta personal de alguien no le sirve a nadie más ` +
|
|
269
|
+
`y ata ese documento a un directorio que en otra máquina no existe. Nombrá los archivos relativos a la ` +
|
|
270
|
+
`raíz desde la que trabajás: el registro recorta lo que se escape, pero recortar deja el hueco a la ` +
|
|
271
|
+
`vista y nombrar bien no.`,
|
|
243
272
|
{ schema: VERDICT, label: `juzga:${item.id}`, phase: 'Juzgar' },
|
|
244
273
|
).then((verdict) => ({ id: item.id, expected: item.expected, answer: answer.response, verdict }))
|
|
245
274
|
: { id: item.id, expected: item.expected, answer: '', verdict: null }),
|
|
@@ -259,8 +288,10 @@ phase('Registrar')
|
|
|
259
288
|
|
|
260
289
|
const rows = answered.map((one) => {
|
|
261
290
|
const mark = one.verdict.passed ? 'pasa' : 'no pasa'
|
|
262
|
-
|
|
263
|
-
|
|
291
|
+
const nota = oneLine(one.verdict.contractNote)
|
|
292
|
+
const para = nota ? `- Para el contrato: ${nota}\n` : ''
|
|
293
|
+
return stripRoot(`### ${one.id}\n\n- Veredicto: ${mark}\n${para}\n**Respuesta del cargo**\n\n`
|
|
294
|
+
+ `${one.answer}\n\n**Contraste**\n\n${one.verdict.reasoning}`, ROOT)
|
|
264
295
|
}).join('\n\n')
|
|
265
296
|
|
|
266
297
|
await agent(
|
|
@@ -272,10 +303,14 @@ await agent(
|
|
|
272
303
|
`escribía encima de la primera —que es la línea base que la propuesta cita como evidencia—.\n\n` +
|
|
273
304
|
`Ahí y no en el banco de trabajo. El banco se borra en la próxima corrida —es donde el cargo ` +
|
|
274
305
|
`trabajó, no donde vive—, mientras que el veredicto pertenece al contrato que lo rindió y viaja ` +
|
|
275
|
-
`con él. La fecha del frontmatter
|
|
306
|
+
`con él. La fecha del frontmatter sale del nombre del archivo que te dio el motor —sus primeros diez `
|
|
307
|
+
+ `caracteres, AAAA-MM-DD—, y no de \`date\`: el nombre lo decide el motor con reloj UTC y `
|
|
308
|
+
+ `\`date\` contesta en hora local, así que había dos fechas distintas en la misma corrida, `
|
|
309
|
+
+ `una en el nombre y otra adentro.\n\n` +
|
|
276
310
|
`El archivo lleva este frontmatter y después el contenido tal cual te lo paso, sin reescribirlo ni ` +
|
|
277
311
|
`resumirlo:\n\n---\nagent: ${AGENT}\ndate: <fecha>\npassed: ${passed.length}\ntotal: ${answered.length}\n---\n\n` +
|
|
278
|
-
`# Casos adversariales — <fecha>\n\n${
|
|
312
|
+
`# Casos adversariales — <fecha>\n\n${coverageNote(ONLY.length, 'el cargo')}` +
|
|
313
|
+
`${unmeasuredNote(unmeasured)}${rows}\n\n` +
|
|
279
314
|
`No toques SKILL.md, sources.yaml, expected-behaviors.yaml ni los casos. No hagas commit ni push.`,
|
|
280
315
|
{ label: 'registrar', phase: 'Registrar' },
|
|
281
316
|
)
|
|
@@ -27,7 +27,11 @@ export const meta = {
|
|
|
27
27
|
|
|
28
28
|
{{INCLUDE:shared/workflow-root.js}}
|
|
29
29
|
const AGENT = String((typeof args === 'string' ? args : (args || {}).agent) || '').trim()
|
|
30
|
-
|
|
30
|
+
// El período que pidió quien invoca, que es una pista para elegir entre varias propuestas y no el
|
|
31
|
+
// del documento: ése se deriva del nombre del archivo ya resuelto, más abajo. Se llamaban igual y
|
|
32
|
+
// eran dos `const` en el mismo alcance, así que el archivo no compilaba y el cargador lo
|
|
33
|
+
// descartaba sin decir nada: `/agent-promote` no existía como comando.
|
|
34
|
+
const REQUESTED = String((args || {}).period || '').trim()
|
|
31
35
|
|
|
32
36
|
const SIGNATURE = {
|
|
33
37
|
type: 'object', additionalProperties: false, required: ['dir', 'proposal', 'approved'],
|
|
@@ -38,7 +42,16 @@ const SIGNATURE = {
|
|
|
38
42
|
signedBy: { type: 'string' },
|
|
39
43
|
state: { type: 'string' },
|
|
40
44
|
status: { type: 'string' },
|
|
41
|
-
|
|
45
|
+
// Tres estados y no un booleano. «Nadie lo decidió» y «se decidió que no cambia nada» son opuestos,
|
|
46
|
+
// y colapsarlos dejaba sin salida a un mes revisado, firmado y cerrado sin cambios: el recorrido lo
|
|
47
|
+
// mandaba a proponer de nuevo y la propuesta quedaba `proposed` para siempre, contando como pendiente.
|
|
48
|
+
change: { type: 'string' },
|
|
49
|
+
// Qué CLI existe acá. Una empresa tiene el shim `tools/ops.js`; el repo del toolkit no lo tiene
|
|
50
|
+
// —no se instala a sí mismo— y su motor está en `engine/cli/ops.js`. Los dos evaluadores ya lo
|
|
51
|
+
// preguntan; éste lo suponía en sus dos comandos. El primero se salvó porque el agente improvisó, y
|
|
52
|
+
// el último —el que sella— no: la propuesta quedó `proposed` con el cambio ya aplicado, que es el
|
|
53
|
+
// estado que el comentario de ese paso llama «el que se hace mal en silencio».
|
|
54
|
+
cli: { type: 'string' },
|
|
42
55
|
},
|
|
43
56
|
}
|
|
44
57
|
|
|
@@ -59,12 +72,14 @@ if (!AGENT) return stop('sin-cargo', 'pasá el slug del cargo')
|
|
|
59
72
|
phase('Firma')
|
|
60
73
|
|
|
61
74
|
const signature = await agent(
|
|
62
|
-
`From ${ROOT},
|
|
75
|
+
`From ${ROOT}, the CLI is "tools/ops.js" if that file exists and "engine/cli/ops.js" otherwise. ` +
|
|
76
|
+
`Set cli to the one that exists.\n\n` +
|
|
77
|
+
`Run "node <cli> agents list --json" and take the path it printed for ${AGENT}. ` +
|
|
63
78
|
`Set dir to "${ROOT}/<path>": that command prints paths relative to ${ROOT}.\n\n` +
|
|
64
79
|
`Find the newest proposal under <dir>/learning/proposals/. They are named AAAA-MM.md, and a ` +
|
|
65
80
|
`correction to an already applied one is AAAA-MM-rN.md — the revision is newer than the plain name ` +
|
|
66
81
|
`for the same month, so sorting file names is not enough` +
|
|
67
|
-
`${
|
|
82
|
+
`${REQUESTED ? `. Prefer the newest one for ${REQUESTED}` : ''}. Set proposal to its full path. Read ` +
|
|
68
83
|
`**only** its ` +
|
|
69
84
|
`frontmatter and its "Aprobación humana" and "Cambio propuesto" sections and report, without ` +
|
|
70
85
|
`interpreting in anyone's favour:\n` +
|
|
@@ -73,13 +88,20 @@ const signature = await agent(
|
|
|
73
88
|
`- signedBy: the person recorded, or empty.\n` +
|
|
74
89
|
`- state: the literal state line.\n` +
|
|
75
90
|
`- status: the literal value of the frontmatter "status:" field.\n` +
|
|
76
|
-
`-
|
|
91
|
+
`- change: "concrete" if "Cambio propuesto" states something to change; "none" if it decides that ` +
|
|
92
|
+
`nothing changes — the period was reviewed and no contract moves; "undecided" if nobody has decided ` +
|
|
93
|
+
`yet ("por definir", "pendiente", or empty). "none" and "undecided" are opposites: one is a decision ` +
|
|
94
|
+
`and the other is its absence, so never report "none" for a section that was simply never filled in.`,
|
|
77
95
|
{ schema: SIGNATURE, label: 'signature' },
|
|
78
96
|
)
|
|
79
97
|
if (!signature) return stop('sin-propuesta', `no se pudo leer una propuesta de ${AGENT}`)
|
|
80
|
-
|
|
81
|
-
|
|
98
|
+
const CHANGE = String(signature.change || '').trim().toLowerCase()
|
|
99
|
+
if (CHANGE !== 'concrete' && CHANGE !== 'none') {
|
|
100
|
+
return stop('propuesta-vacia', `${signature.proposal} no la decidió nadie: corré /agent-propose primero`)
|
|
82
101
|
}
|
|
102
|
+
// Un mes que se revisó y se cerró sin cambios. Se firma igual —decidir no tocar nada es una decisión— y
|
|
103
|
+
// se sella igual; lo único que no corre es aplicar, porque no hay qué.
|
|
104
|
+
const NOTHING = CHANGE === 'none'
|
|
83
105
|
if (!signature.approved) {
|
|
84
106
|
return stop('sin-firma', `${signature.proposal} no está aprobada (${signature.state || 'sin estado'}). ` +
|
|
85
107
|
'Firmá «Aprobación humana» con un responsable y repetí: nadie se autoriza a sí mismo')
|
|
@@ -89,7 +111,8 @@ if (!signature.approved) {
|
|
|
89
111
|
// Como el cambio es aditivo por diseño, reaplicar no falla — duplica cada viñeta y cada fuente.
|
|
90
112
|
if ((signature.status || '').toLowerCase() === 'applied') {
|
|
91
113
|
return stop('ya-aplicada', `${signature.proposal} ya está aplicada. Si la evaluación posterior mostró que ` +
|
|
92
|
-
|
|
114
|
+
`el cambio quedó mal calibrado, abrí una revisión con "node ${signature.cli || 'tools/ops.js'} ` +
|
|
115
|
+
`learn ${AGENT} --proposal": ` +
|
|
93
116
|
'la aplicada queda sellada donde está y la corrección va con su propia firma')
|
|
94
117
|
}
|
|
95
118
|
log(`Aprobada por ${signature.signedBy}`)
|
|
@@ -102,7 +125,7 @@ if (!PERIOD) return stop('propuesta-sin-periodo', `${signature.proposal} no se l
|
|
|
102
125
|
|
|
103
126
|
phase('Aplicar')
|
|
104
127
|
|
|
105
|
-
const applied = await agent(
|
|
128
|
+
const applied = NOTHING ? { applied: true, files: [], newCase: '', deviations: '' } : await agent(
|
|
106
129
|
`La propuesta ${signature.proposal} está aprobada por ${signature.signedBy}. Aplicá su sección «Cambio ` +
|
|
107
130
|
`propuesto» al cargo ${AGENT} en ${signature.dir}, archivo por archivo, tal como la propuesta lo ` +
|
|
108
131
|
`describe.\n\n` +
|
|
@@ -112,11 +135,29 @@ const applied = await agent(
|
|
|
112
135
|
`- Si la propuesta pide crear un caso adversarial nuevo, crealo con el enunciado que da y con ` +
|
|
113
136
|
`**cuatro** comportamientos esperados, como todos los del catálogo. Si el enunciado trae más, ` +
|
|
114
137
|
`fusioná los que se solapen sin perder ninguna idea.\n` +
|
|
138
|
+
// El juez recibe `forbidden` y nunca `required`: `engine/cli/catalog.js` toma sólo la primera, y el
|
|
139
|
+
// prompt del juez nombra «conductas prohibidas». O sea que a una requerida la mide únicamente el caso
|
|
140
|
+
// que la ejerza. Medido el 2026-09-02 sobre 48 requeridas de seis cargos: 4 no las ejerce ningún caso
|
|
141
|
+
// y 8 sólo en parte. Las tres agregadas ese día sí se miden, pero porque el caso salió con ellas por
|
|
142
|
+
// costumbre; esto es lo que convierte esa costumbre en requisito.
|
|
143
|
+
`- Una conducta que la propuesta agregue a \`required\` en \`evaluations/expected-behaviors.yaml\` **no ` +
|
|
144
|
+
`la lee nadie al evaluar**: al juez sólo le viajan las prohibidas, así que lo único que puede medir una ` +
|
|
145
|
+
`requerida es un caso que la ejerza. Si la propuesta agrega una y no pide el caso, escribilo igual —con ` +
|
|
146
|
+
`un enunciado que ponga al cargo en la situación donde esa conducta aplica— y reportalo como ` +
|
|
147
|
+
`desviación. Una requerida sin caso entra al contrato como una promesa que nada comprueba.\n` +
|
|
115
148
|
`- Si algo de la propuesta ya no aplica —el archivo cambió, la sección no existe— **no lo fuerces**: ` +
|
|
116
149
|
`dejalo sin aplicar y reportalo como desviación.\n` +
|
|
117
150
|
`- Toda desviación, por chica que sea, se escribe al final de «Aprobación humana» en la propia ` +
|
|
118
151
|
`propuesta, explicando qué se hizo distinto y por qué. Quien firmó tiene derecho a saber qué se ` +
|
|
119
|
-
`aplicó de lo que firmó.\n
|
|
152
|
+
`aplicó de lo que firmó.\n` +
|
|
153
|
+
// Lo que se escribe acá viaja: la propuesta, el historial y los casos van dentro del cargo, y un
|
|
154
|
+
// cargo se adopta en empresas donde `engine/` y el Makefile de este repositorio no existen. Pasó
|
|
155
|
+
// cuatro veces el 2026-09-02 —`node engine/cli/ops.js` en dos historiales, un `make` propio de acá
|
|
156
|
+
// reintroducido, y una ruta resuelta desde la raíz—: la puerta las cazó, pero después de escritas.
|
|
157
|
+
`- Todo lo que escribas —propuesta, historial, caso— viaja con el cargo a cada empresa. No cites ` +
|
|
158
|
+
`rutas de este repositorio (\`engine/\`, \`template/\`, \`automatization/\`) ni objetivos \`make\` que ` +
|
|
159
|
+
`sólo existan acá, y escribí las rutas relativas al cargo. Si necesitás nombrar un comando del CLI, ` +
|
|
160
|
+
`nombralo sin su ruta.\n\n` +
|
|
120
161
|
`No toques otros cargos. No hagas commit ni push. Devolvé qué archivos modificaste, el caso nuevo si ` +
|
|
121
162
|
`lo creaste, y las desviaciones —o cadena vacía si no hubo—.`,
|
|
122
163
|
{ schema: APPLIED, label: `aplica:${AGENT}` },
|
|
@@ -131,10 +172,12 @@ phase('Registrar')
|
|
|
131
172
|
await agent(
|
|
132
173
|
`Agregá una fila a ${signature.dir}/learning/HISTORY.md con la fecha de hoy —obtenela con "date +%F"—, ` +
|
|
133
174
|
`la propuesta ${signature.proposal}, decisión "Aprobada", quién firmó (${signature.signedBy}) y qué se ` +
|
|
134
|
-
`aplicó: ${
|
|
175
|
+
`aplicó: ${NOTHING ? 'nada — el período se revisó y se decidió no cambiar ningún contrato'
|
|
176
|
+
: (applied.files || []).join(', ')}` +
|
|
135
177
|
`${applied.newCase ? ` más el caso ${applied.newCase}` : ''}` +
|
|
136
178
|
`${applied.deviations ? `. Desviaciones: ${applied.deviations}` : ''}.\n\n` +
|
|
137
|
-
`Respetá el formato de tabla que ya tiene el archivo.
|
|
179
|
+
`Respetá el formato de tabla que ya tiene el archivo. Las rutas van relativas al cargo y sin nombrar ` +
|
|
180
|
+
`este repositorio: el historial viaja con el cargo. No toques ninguna otra cosa, no hagas commit.`,
|
|
138
181
|
{ label: 'historial' },
|
|
139
182
|
)
|
|
140
183
|
|
|
@@ -142,12 +185,17 @@ await agent(
|
|
|
142
185
|
// pendiente. Lo hace el motor y no vos, a mano, porque marcar el estado editando frontmatter es
|
|
143
186
|
// exactamente el paso que se hace mal en silencio.
|
|
144
187
|
await agent(
|
|
145
|
-
`From ${ROOT}, run "node tools/ops.js
|
|
188
|
+
`From ${ROOT}, run "node ${signature.cli || 'tools/ops.js'} ` +
|
|
189
|
+
`learn ${AGENT} --applied --period ${PERIOD}" and report only ` +
|
|
146
190
|
`what it printed. Change nothing else.`,
|
|
147
191
|
{ label: 'sella' },
|
|
148
192
|
)
|
|
149
193
|
|
|
150
|
-
|
|
194
|
+
// Re-evaluar un contrato que no se movió mide varianza y no un cambio: cuesta la corrida entera y
|
|
195
|
+
// devuelve lo que R20 llama comprar un número nuevo con la misma información.
|
|
196
|
+
log(NOTHING
|
|
197
|
+
? 'Ningún contrato cambió: los veredictos vigentes siguen midiendo esta versión.'
|
|
198
|
+
: 'El contrato cambió: los casos valen sólo si se vuelven a correr contra la versión nueva.')
|
|
151
199
|
return finish({
|
|
152
200
|
agent: AGENT,
|
|
153
201
|
proposal: signature.proposal,
|
|
@@ -155,5 +203,7 @@ return finish({
|
|
|
155
203
|
files: applied.files || [],
|
|
156
204
|
newCase: applied.newCase || '',
|
|
157
205
|
deviations: applied.deviations || '',
|
|
158
|
-
next:
|
|
206
|
+
next: NOTHING
|
|
207
|
+
? `no hace falta re-evaluar ${AGENT}: ningún contrato cambió y los veredictos vigentes siguen valiendo`
|
|
208
|
+
: `corré /agent-eval ${AGENT}: el registro anterior quedó viejo y no vale para el contrato nuevo`,
|
|
159
209
|
})
|
|
@@ -214,6 +214,9 @@ const CONTRACT = {
|
|
|
214
214
|
required: ['project', 'workspaceRoots', 'maxTaskHours', 'commitPerTask', 'humanCheckpoint', 'contracts'],
|
|
215
215
|
properties: {
|
|
216
216
|
project: { type: 'string' }, workspaceRoots: { type: 'array', minItems: 1, items: { type: 'string' } },
|
|
217
|
+
// La puerta que el proyecto declara, si la declara. Viaja con la raíz porque es de la base de código
|
|
218
|
+
// y no del runner: un monorepo tiene una por servicio, y uno solo tiene una sola.
|
|
219
|
+
gates: { type: 'array', items: { type: 'string' } },
|
|
217
220
|
maxTaskHours: { type: 'number' }, commitPerTask: { type: 'boolean' },
|
|
218
221
|
humanCheckpoint: { type: 'boolean' }, contracts: { type: 'string' },
|
|
219
222
|
boundaries: { type: 'array', items: { type: 'string' } },
|
|
@@ -247,6 +250,17 @@ const blockers = (verdict) => verdict.concerns.filter((one) => one.blocking).map
|
|
|
247
250
|
const RUNS_TESTS = /(?:^|[\s/:=-])(?:tests?|specs?|pytest|jest|vitest|mocha|rspec|phpunit|ci|check)\b/i
|
|
248
251
|
{{INCLUDE:shared/workflow-finish.js}}
|
|
249
252
|
|
|
253
|
+
// Por qué fases pasó esta corrida. Existe porque auditar una tarea no puede exigir leer este archivo:
|
|
254
|
+
// para saber si Review había corrido hubo que abrir el fuente y cruzarlo con los commits del día.
|
|
255
|
+
//
|
|
256
|
+
// Se envuelve `phase` en vez de anotar en sus quince llamadas: una que se olvidara de anotar dejaría
|
|
257
|
+
// un registro incompleto, que se lee igual que uno completo. Y se envuelve reasignando en vez de
|
|
258
|
+
// declarar, porque `phase` llega como parámetro del wrapper que arma el runner y redeclararlo con
|
|
259
|
+
// `const` es un SyntaxError; reasignar un parámetro sí es legal, y `announce` guarda el original.
|
|
260
|
+
const ran = []
|
|
261
|
+
const announce = phase
|
|
262
|
+
phase = (name) => { ran.push(name); announce(name) }
|
|
263
|
+
|
|
250
264
|
phase('Triage')
|
|
251
265
|
// El contrato se lee una sola vez por corrida y viaja como texto: ningún subagente relee AGENTS.md,
|
|
252
266
|
// ops.config.json ni PROTOCOL.md. `ops check` y el guard planning-drift siguen validando el resultado.
|
|
@@ -254,6 +268,8 @@ const contract = await agent(
|
|
|
254
268
|
`${BASE}\n\nLeé ${ROOT}/AGENTS.md, ${CONFIG} y ${P}/PROTOCOL.md una sola vez y no leas nada más. Reportá los ` +
|
|
255
269
|
`valores de configuración textualmente: project, workspaceRoots como entradas "nombre → ruta", ` +
|
|
256
270
|
`runner.maxTaskHours, runner.commitPerTask y runner.humanCheckpointBetweenMilestones como humanCheckpoint. ` +
|
|
271
|
+
`En gates poné una entrada "ruta → comando" por cada workspaceRoot que declare \`verify\`, y ninguna por ` +
|
|
272
|
+
`las que no lo declaren: la lista vacía significa que el proyecto no dice con qué se verifica. ` +
|
|
257
273
|
`Copiá la sección "## Contratos" de PROTOCOL.md dentro de contracts tal cual, sin reformular, resumir ni ` +
|
|
258
274
|
`reordenar. En boundaries listá sólo los límites que AGENTS.md enuncia y que restringen la ejecución autónoma.`,
|
|
259
275
|
{ schema: CONTRACT, label: 'contract-digest' },
|
|
@@ -357,6 +373,11 @@ while (rounds++ < MAX_TASKS) {
|
|
|
357
373
|
log(`${task.id} sigue sin clasificar: corre por el carril completo`)
|
|
358
374
|
}
|
|
359
375
|
|
|
376
|
+
// Quién puso el carril, que es lo que justifica saltear Ready y no el carril en sí: el clasificador
|
|
377
|
+
// leyó la aceptación en esta corrida y dijo que nombra un valor literal. Escrito a mano en la línea,
|
|
378
|
+
// ese lector no existió —`Classify` sólo corre si falta lane o cast—, y saltear la única fase que
|
|
379
|
+
// pregunta si la tarea está lista quedaba apoyado en una premisa que nadie comprobó.
|
|
380
|
+
const vouched = classified.has(task.id)
|
|
360
381
|
const express = planning.lane === 'express'
|
|
361
382
|
const direct = planning.lane === 'directo'
|
|
362
383
|
const lite = planning.lane === 'lite'
|
|
@@ -371,13 +392,22 @@ while (rounds++ < MAX_TASKS) {
|
|
|
371
392
|
const cast = { ...OWNERS, build: planning.cast.build }
|
|
372
393
|
cast.review = [OWNERS.review, ...planning.cast.review].filter(Boolean).join(', ')
|
|
373
394
|
log(`Cargos: build=${cast.build || '(sin asignar)'} · review=${cast.review} · qa=${cast.qa}`)
|
|
395
|
+
// Nombrar el cargo no alcanza: hay que decir dónde está su contrato, y `agents/` no es la respuesta.
|
|
396
|
+
// El catálogo no se copia a la instancia —viaja en el paquete— así que esa carpeta no existe en la raíz
|
|
397
|
+
// y la instrucción anterior mandaba a leer una ruta ausente. El workflow tampoco puede resolverla por
|
|
398
|
+
// su cuenta: su runtime no lee archivos. Lo que sí puede es decir con qué comando se resuelve, y el
|
|
399
|
+
// agente la obtiene dentro de su propia vuelta, sin costar una llamada más.
|
|
374
400
|
const asRole = (slugs) => (slugs
|
|
375
|
-
? `Actuá como ${slugs}, respetando el contrato de cada uno
|
|
376
|
-
`
|
|
401
|
+
? `Actuá como ${slugs}, respetando el contrato de cada uno y sus límites: un cargo que no puede ` +
|
|
402
|
+
`decidir solo, no decide solo.\n\n` +
|
|
403
|
+
`Leé ese contrato antes de empezar, no lo supongas por el nombre del cargo. El catálogo no se copia ` +
|
|
404
|
+
`a esta instancia, así que no hay carpeta \`agents/\` en la raíz: la ruta la da ` +
|
|
405
|
+
`"node tools/ops.js agents list ${ROOT} --json" en el campo \`path\` del slug, y el contrato es ` +
|
|
406
|
+
`\`<path>/SKILL.md\`.\n\n`
|
|
377
407
|
: '')
|
|
378
408
|
|
|
379
409
|
if (!planning.wipActive) {
|
|
380
|
-
if (!mechanical) {
|
|
410
|
+
if (!mechanical || !vouched) {
|
|
381
411
|
phase('Ready')
|
|
382
412
|
const ready = await read(
|
|
383
413
|
`${asRole(OWNERS.ready)}Revisá que ${task.id} tenga aceptación concreta, dependencias resueltas y ` +
|
|
@@ -532,6 +562,9 @@ while (rounds++ < MAX_TASKS) {
|
|
|
532
562
|
&& !build.redFirst.some((red) => namesTest(red, entry)))
|
|
533
563
|
if (loose) return stop('edge-unproven', `${loose.detail} entró sin la prueba que lo fija`)
|
|
534
564
|
|
|
565
|
+
// Qué revisión hubo, para que el cierre no pueda inventar una. Nace diciendo que no hubo porque
|
|
566
|
+
// `express` no convoca a nadie, y ése es el caso que se escribió como si un cargo hubiera aprobado.
|
|
567
|
+
let reviewFact = 'no corrió (el carril express no convoca revisor)'
|
|
535
568
|
if (!express) {
|
|
536
569
|
phase('Review')
|
|
537
570
|
let review = await run(
|
|
@@ -555,6 +588,7 @@ while (rounds++ < MAX_TASKS) {
|
|
|
555
588
|
}
|
|
556
589
|
// Aprobar sin declarar qué se abrió no se arregla mandando a tocar código: falló quien revisó.
|
|
557
590
|
if (!review.consulted.length) return stop('review-unbacked', 'aprobó el diff sin declarar qué inspeccionó')
|
|
591
|
+
reviewFact = `${review.verdict} por ${cast.review}, sobre ${review.consulted.join(', ')}`
|
|
558
592
|
// Lo que no impide entregar no manda a tocar código, y tampoco desaparece: la mejora opinable que se
|
|
559
593
|
// corrige a las apuradas cuesta una vuelta y un riesgo que nadie pidió. Va a Propuestas y no a
|
|
560
594
|
// Lecciones porque lo que la revisión anotó es un cambio del producto con su evidencia; Lecciones es
|
|
@@ -571,9 +605,17 @@ while (rounds++ < MAX_TASKS) {
|
|
|
571
605
|
`contrastá cada criterio ` +
|
|
572
606
|
`de aceptación contra sus aserciones: en uncovered va el criterio que ningún test codifica, con su causa ` +
|
|
573
607
|
`—missing-test si el test falta o no asercia la propiedad, ambiguous si el criterio no dice qué habría ` +
|
|
574
|
-
`que aserciar—. Un test que pasa sin aserciarla no la cubre. Después
|
|
575
|
-
|
|
576
|
-
|
|
608
|
+
`que aserciar—. Un test que pasa sin aserciarla no la cubre. Después corré los gates reales de ${task.service}. ` +
|
|
609
|
+
// Descubrir la puerta es trabajo de modelo repetido en cada tarea sobre una respuesta que no cambia,
|
|
610
|
+
// y encima adivinable: el proyecto la declara en `verify` y ahí deja de adivinarse. Cuando no la
|
|
611
|
+
// declara se vuelve a descubrir, que es lo que pasaba siempre.
|
|
612
|
+
(contract.gates && contract.gates.length
|
|
613
|
+
? `El proyecto las declara y no hay que descubrirlas —${contract.gates.join(' · ')}—: corré la de ` +
|
|
614
|
+
`la raíz que contiene ese servicio, tal cual y desde esa raíz. Si falla por algo que la tarea no ` +
|
|
615
|
+
`tocó, decilo en vez de arreglarlo. `
|
|
616
|
+
: `El proyecto no declara con qué se verifica, así que descubrilo: primero las instrucciones del ` +
|
|
617
|
+
`repositorio, después el test, lint, typecheck y build que apliquen. `) +
|
|
618
|
+
`Leé los exit codes de verdad. ` +
|
|
577
619
|
`passed=true exige comandos corridos y ninguna regresión causada por la tarea. Marcá ranTests en el ` +
|
|
578
620
|
`comando que haya corrido las pruebas, sea cual sea su nombre. ` +
|
|
579
621
|
`Aceptación: ${task.acceptance}.`
|
|
@@ -637,7 +679,9 @@ while (rounds++ < MAX_TASKS) {
|
|
|
637
679
|
await write(
|
|
638
680
|
`Cerrá ${task.id} de forma atómica: agregala bajo su hito en ${DONE} con evidencia de acept, done, qa, ` +
|
|
639
681
|
`tests y commit; sacala junto con sus notas indentadas de ${BACKLOG}; cerrá su épica sólo si no queda ` +
|
|
640
|
-
`ninguna tarea etiquetada; y dejá ${WIP} en status IDLE.
|
|
682
|
+
`ninguna tarea etiquetada; y dejá ${WIP} en status IDLE. En decisions no nombres una fase ni un cargo ` +
|
|
683
|
+
`que no figure en estos hechos. Hechos: lane=${planning.lane || 'sin clasificar'}; ` +
|
|
684
|
+
`review=${reviewFact}; fases=${ran.join(' → ')}; build=${build.summary}; ` +
|
|
641
685
|
`verify=${JSON.stringify(verified.commands)}; qa=${qa.evidence}; commit=${commit.hash || commit.reason}.`,
|
|
642
686
|
)
|
|
643
687
|
completed.push(task.id)
|
|
@@ -665,4 +709,4 @@ if (completed.length && contract.humanCheckpoint) await write(
|
|
|
665
709
|
`Creá ${GATE} con el hito terminado, las tareas ${completed.join(', ')}, la evidencia, las acciones humanas ` +
|
|
666
710
|
`pendientes y las instrucciones exactas para continuar. Nunca hagas push ni deploy.`,
|
|
667
711
|
)
|
|
668
|
-
return finish({ done: completed, count: completed.length, hito: currentMilestone })
|
|
712
|
+
return finish({ done: completed, count: completed.length, hito: currentMilestone, phases: ran })
|