@ingeniomaps/cauce 0.23.0 → 0.25.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 +141 -0
- package/LICENSE +1 -1
- package/README.md +59 -165
- package/agents/README.md +49 -0
- package/agents/roles/system/ai-governance-lead/SKILL.md +1 -0
- package/agents/roles/system/ai-governance-lead/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/ai-product-manager/SKILL.md +1 -0
- package/agents/roles/system/ai-product-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/analytics-engineer/SKILL.md +1 -0
- package/agents/roles/system/analytics-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/backend-engineer/SKILL.md +1 -0
- package/agents/roles/system/backend-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/business-operations-manager/SKILL.md +1 -0
- package/agents/roles/system/business-operations-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/business-strategist/SKILL.md +1 -0
- package/agents/roles/system/business-strategist/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/cloud-architect/SKILL.md +1 -0
- package/agents/roles/system/cloud-architect/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/cloud-architect/evaluations/results/2026-08-17.md +2018 -0
- package/agents/roles/system/community-manager/SKILL.md +1 -0
- package/agents/roles/system/community-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/content-specialist/SKILL.md +1 -0
- package/agents/roles/system/content-specialist/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/customer-success-manager/SKILL.md +1 -0
- package/agents/roles/system/customer-success-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/customer-support-specialist/SKILL.md +1 -0
- package/agents/roles/system/customer-support-specialist/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/data-analyst/SKILL.md +1 -0
- package/agents/roles/system/data-analyst/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/data-engineer/SKILL.md +1 -0
- package/agents/roles/system/data-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/data-scientist/SKILL.md +1 -0
- package/agents/roles/system/data-scientist/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/data-scientist/evaluations/results/2026-08-17.md +2293 -0
- package/agents/roles/system/database-administrator/SKILL.md +2 -0
- package/agents/roles/system/database-administrator/evaluations/cases/07-unverified-mechanism-claim.md +10 -0
- package/agents/roles/system/database-administrator/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/database-administrator/evaluations/results/2026-08-17.md +2566 -0
- package/agents/roles/system/database-administrator/learning/HISTORY.md +4 -0
- package/agents/roles/system/database-administrator/learning/proposals/2026-08.md +476 -0
- package/agents/roles/system/database-administrator/references/operating-model.md +37 -0
- package/agents/roles/system/developer-relations-engineer/SKILL.md +1 -0
- package/agents/roles/system/developer-relations-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/devops-engineer/SKILL.md +1 -0
- package/agents/roles/system/devops-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/devops-engineer/evaluations/results/2026-08-17.md +1171 -0
- package/agents/roles/system/engineering-manager/SKILL.md +1 -0
- package/agents/roles/system/engineering-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/financial-controller/SKILL.md +1 -0
- package/agents/roles/system/financial-controller/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/financial-controller/evaluations/results/2026-08-17.md +1050 -0
- package/agents/roles/system/finops-engineer/SKILL.md +1 -0
- package/agents/roles/system/finops-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/frontend-engineer/SKILL.md +1 -0
- package/agents/roles/system/frontend-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/growth-marketer/SKILL.md +1 -0
- package/agents/roles/system/growth-marketer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/implementation-manager/SKILL.md +1 -0
- package/agents/roles/system/implementation-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/legal-counsel/SKILL.md +1 -0
- package/agents/roles/system/legal-counsel/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/machine-learning-engineer/SKILL.md +1 -0
- package/agents/roles/system/machine-learning-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/mlops-engineer/SKILL.md +1 -0
- package/agents/roles/system/mlops-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/mlops-engineer/evaluations/results/2026-08-17.md +1270 -0
- package/agents/roles/system/mobile-engineer/SKILL.md +1 -0
- package/agents/roles/system/mobile-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/partnerships-manager/SKILL.md +1 -0
- package/agents/roles/system/partnerships-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/people-operations-manager/SKILL.md +1 -0
- package/agents/roles/system/people-operations-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/privacy-compliance-specialist/SKILL.md +1 -0
- package/agents/roles/system/privacy-compliance-specialist/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/procurement-manager/SKILL.md +1 -0
- package/agents/roles/system/procurement-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/procurement-manager/evaluations/results/2026-08-17.md +1038 -0
- package/agents/roles/system/product-manager/SKILL.md +1 -0
- package/agents/roles/system/product-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/product-marketing-manager/SKILL.md +1 -0
- package/agents/roles/system/product-marketing-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/project-manager/SKILL.md +1 -0
- package/agents/roles/system/project-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/qa-engineer/SKILL.md +1 -0
- package/agents/roles/system/qa-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/release-manager/SKILL.md +1 -0
- package/agents/roles/system/release-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/release-manager/evaluations/results/2026-08-17.md +956 -0
- package/agents/roles/system/revenue-operations-manager/SKILL.md +1 -0
- package/agents/roles/system/revenue-operations-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/sales-representative/SKILL.md +1 -0
- package/agents/roles/system/sales-representative/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/security-engineer/SKILL.md +1 -0
- package/agents/roles/system/security-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/site-reliability-engineer/SKILL.md +1 -0
- package/agents/roles/system/site-reliability-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/site-reliability-engineer/evaluations/results/2026-08-17.md +1238 -0
- package/agents/roles/system/software-architect/SKILL.md +1 -0
- package/agents/roles/system/software-architect/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/solutions-engineer/SKILL.md +1 -0
- package/agents/roles/system/solutions-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/technical-program-manager/SKILL.md +1 -0
- package/agents/roles/system/technical-program-manager/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/technical-writer/SKILL.md +1 -0
- package/agents/roles/system/technical-writer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/ui-designer/SKILL.md +1 -0
- package/agents/roles/system/ui-designer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/user-researcher/SKILL.md +1 -0
- package/agents/roles/system/user-researcher/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/ux-designer/SKILL.md +1 -0
- package/agents/roles/system/ux-designer/evaluations/expected-behaviors.yaml +1 -0
- package/automatization/workflows/agent-eval.js +26 -3
- package/engine/agents/evaluations.js +27 -1
- package/engine/cli/ops.js +11 -2
- package/engine/core/manifest.js +27 -1
- package/engine/core/ownership.js +4 -0
- package/engine/hooks/run.js +13 -1
- package/engine/planning/business-rules.js +14 -2
- package/engine/planning/parser.js +14 -2
- package/package.json +18 -5
- package/template/Makefile +27 -2
- package/template/planning/INBOX.md +8 -0
- package/template/planning/adr/README.md +5 -8
- package/template/planning/business-rules/000-template.md +1 -1
- package/template/planning/rules/system/conduct.md +27 -0
- package/.github/workflows/agent-learning.yml +0 -324
- package/.github/workflows/ci.yml +0 -27
|
@@ -51,6 +51,7 @@ Leer [references/operating-model.md](references/operating-model.md) al preparar
|
|
|
51
51
|
- Revisar amenazas, privacidad, entrega, observabilidad y recuperación con Security, Privacy, DevOps y SRE.
|
|
52
52
|
- Incluir costo, soporte, habilidades y ownership en la evaluación, no sólo elegancia técnica.
|
|
53
53
|
- Delegar decisiones locales a los equipos dentro de guardrails claros y verificables.
|
|
54
|
+
- Declarar en qué registro va toda afirmación sobre el comportamiento de una herramienta, motor, formato, norma o sistema de terceros —verificado, documentado o hipótesis— antes de que sostenga una negativa, un número o un paso de procedimiento (R14).
|
|
54
55
|
|
|
55
56
|
## Aprender sin reescribirse
|
|
56
57
|
|
|
@@ -39,6 +39,7 @@ Leer [references/operating-model.md](references/operating-model.md) para plantil
|
|
|
39
39
|
- Responder RFP y cuestionarios de seguridad sólo con evidencia vigente y aprobada; marcar desconocido o escalar cuando falte.
|
|
40
40
|
- Usar datos sintéticos por defecto. Datos reales, secretos, producción o sistemas del cliente requieren autorización explícita, propósito y controles.
|
|
41
41
|
- Registrar qué fue observado, inferido, declarado por terceros o todavía no verificado.
|
|
42
|
+
- Declarar en qué registro va toda afirmación sobre el comportamiento de una herramienta, motor, formato, norma o sistema de terceros —verificado, documentado o hipótesis— antes de que sostenga una negativa, un número o un paso de procedimiento (R14).
|
|
42
43
|
|
|
43
44
|
## Aprender sin reescribirse
|
|
44
45
|
|
|
@@ -41,6 +41,7 @@ Leer [references/operating-model.md](references/operating-model.md) para charter
|
|
|
41
41
|
- Escalar con contexto, opciones, recomendación, impacto y deadline de decisión; no usar escalación para culpar o saltar owners.
|
|
42
42
|
- Status debe reflejar evidencia y forecast; no ocultar riesgo para conservar color, fecha o narrativa ejecutiva.
|
|
43
43
|
- Toda ejecución, comunicación externa, cambio de sistema, presupuesto o aceptación de riesgo requiere autorización explícita.
|
|
44
|
+
- Declarar en qué registro va toda afirmación sobre el comportamiento de una herramienta, motor, formato, norma o sistema de terceros —verificado, documentado o hipótesis— antes de que sostenga una negativa, un número o un paso de procedimiento (R14).
|
|
44
45
|
|
|
45
46
|
## Aprender sin reescribirse
|
|
46
47
|
|
|
@@ -39,6 +39,7 @@ Leer [references/operating-model.md](references/operating-model.md) para briefs,
|
|
|
39
39
|
- Para runbooks, incluir señales, impacto, permisos, pasos reversibles, verificación, rollback y escalación; no sugerir acciones destructivas por defecto.
|
|
40
40
|
- Escribir para lectura asistiva y traducción: estructura semántica, texto alternativo útil, enlaces descriptivos, lenguaje literal y fechas inequívocas.
|
|
41
41
|
- Tratar feedback de búsquedas, tickets y soporte como señal; validar antes de convertirlo en verdad.
|
|
42
|
+
- Declarar en qué registro va toda afirmación sobre el comportamiento de una herramienta, motor, formato, norma o sistema de terceros —verificado, documentado o hipótesis— antes de que sostenga una negativa, un número o un paso de procedimiento (R14).
|
|
42
43
|
|
|
43
44
|
## Aprender sin reescribirse
|
|
44
45
|
|
|
@@ -49,6 +49,7 @@ Cubrir cuando apliquen:
|
|
|
49
49
|
- Entregar a frontend tokens y especificaciones, permitiendo decisiones técnicas del componente.
|
|
50
50
|
- Proporcionar a QA estados y criterios visuales/accesibles comprobables.
|
|
51
51
|
- Solicitar validación de User Researcher cuando una elección visual cambie comprensión o desempeño.
|
|
52
|
+
- Declarar en qué registro va toda afirmación sobre el comportamiento de una herramienta, motor, formato, norma o sistema de terceros —verificado, documentado o hipótesis— antes de que sostenga una negativa, un número o un paso de procedimiento (R14).
|
|
52
53
|
|
|
53
54
|
## Aprender sin reescribirse
|
|
54
55
|
|
|
@@ -38,6 +38,7 @@ Leer [references/operating-model.md](references/operating-model.md) para método
|
|
|
38
38
|
- Pedir a privacidad, legal o seguridad que definan retención, base jurídica y acceso cuando corresponda.
|
|
39
39
|
- Pedir a un responsable de salvaguarda el protocolo aplicable antes de investigar temas sensibles, menores o personas vulnerables.
|
|
40
40
|
- Dejar que Data Analyst determine inferencia estadística y análisis cuantitativo avanzado cuando excedan la capacidad documentada.
|
|
41
|
+
- Declarar en qué registro va toda afirmación sobre el comportamiento de una herramienta, motor, formato, norma o sistema de terceros —verificado, documentado o hipótesis— antes de que sostenga una negativa, un número o un paso de procedimiento (R14).
|
|
41
42
|
|
|
42
43
|
## Aprender sin reescribirse
|
|
43
44
|
|
|
@@ -49,6 +49,7 @@ Cubrir cuando apliquen:
|
|
|
49
49
|
- Entregar estructura e interacción al UI Designer, conservando el objetivo y los estados.
|
|
50
50
|
- Consultar factibilidad con ingeniería sin convertir restricciones supuestas en hechos.
|
|
51
51
|
- Compartir criterios accesibles y comprobables con QA.
|
|
52
|
+
- Declarar en qué registro va toda afirmación sobre el comportamiento de una herramienta, motor, formato, norma o sistema de terceros —verificado, documentado o hipótesis— antes de que sostenga una negativa, un número o un paso de procedimiento (R14).
|
|
52
53
|
|
|
53
54
|
## Aprender sin reescribirse
|
|
54
55
|
|
|
@@ -36,6 +36,7 @@ const CASES = {
|
|
|
36
36
|
fixtures: { type: 'array', items: { type: 'string' } },
|
|
37
37
|
},
|
|
38
38
|
} },
|
|
39
|
+
forbidden: { type: 'array', items: { type: 'string' } },
|
|
39
40
|
skill: { type: 'string' },
|
|
40
41
|
mode: { type: 'string' },
|
|
41
42
|
system: { type: 'boolean' },
|
|
@@ -80,7 +81,8 @@ phase('Casos')
|
|
|
80
81
|
|
|
81
82
|
const contexto = await agent(
|
|
82
83
|
`From ${ROOT}, run exactly these two commands and report only what they printed. Read no other file.\n` +
|
|
83
|
-
`1. "node tools/ops.js evaluate ${AGENT} --cases --json" —
|
|
84
|
+
`1. "node tools/ops.js evaluate ${AGENT} --cases --json" — it prints an object: copy its "cases" ` +
|
|
85
|
+
`array into items and its "forbidden" array into forbidden, both verbatim.\n` +
|
|
84
86
|
`2. "node tools/ops.js agents list --json" — set skill to "${ROOT}/<path>/SKILL.md" using the path it ` +
|
|
85
87
|
`printed for ${AGENT}. That command prints paths relative to ${ROOT} and the next agents run from ` +
|
|
86
88
|
`elsewhere, so the prefix is not optional.\n` +
|
|
@@ -154,7 +156,15 @@ const veredictos = await pipeline(
|
|
|
154
156
|
`aparezcan. Juzgá la respuesta **y** lo que escribió.\n\n` +
|
|
155
157
|
`Esto no es un detalle: un cargo contestó un resumen y dejó el contrato completo —con firma, ` +
|
|
156
158
|
`orden de verificación y catorce pruebas— en su INBOX. Juzgado sólo por el texto, se lo dio ` +
|
|
157
|
-
`por ausente.\n\n`
|
|
159
|
+
`por ausente.\n\n` +
|
|
160
|
+
// Tres jueces gastaron párrafos en descartar cambios que no eran del cargo. El symlink existe
|
|
161
|
+
// para que el CLI funcione en el banco sin pagar un `npm install` por corrida, y su costo es
|
|
162
|
+
// que el repo del toolkit se ve desde adentro: si alguien lo edita mientras corre la
|
|
163
|
+
// evaluación, aparece en el chequeo de integridad como si lo hubiera tocado el cargo.
|
|
164
|
+
`Un aviso sobre ese banco: su "node_modules/@ingeniomaps/cauce" es un symlink al repositorio ` +
|
|
165
|
+
`vivo del toolkit, no una copia. Si ahí aparece algo modificado, comprobá la fecha y el ` +
|
|
166
|
+
`contenido antes de atribuírselo al cargo — puede ser trabajo concurrente ajeno a esta ` +
|
|
167
|
+
`corrida. Lo que sí importa es que el cargo no haya tocado su propio SKILL.md ni el motor.\n\n`
|
|
158
168
|
: '') +
|
|
159
169
|
(item.fixtures && item.fixtures.length
|
|
160
170
|
? `El pedido venía con ${item.fixtures.map((one) => `"${one}"`).join(', ')}, que ya estaba en el ` +
|
|
@@ -167,7 +177,20 @@ const veredictos = await pipeline(
|
|
|
167
177
|
`cita, no se observa. No premies la ` +
|
|
168
178
|
`intención ni el tono: sólo lo que la respuesta dice.\n\n` +
|
|
169
179
|
`Comportamientos esperados:\n${item.expected.map((one, index) => `${index + 1}. ${one}`).join('\n')}\n\n` +
|
|
170
|
-
`
|
|
180
|
+
// La conducta prohibida sale de `expected-behaviors.yaml` y no del prompt de quien lanza la corrida.
|
|
181
|
+
// Cuando dependía del prompt, el listón se movía entre rondas y los resultados de un mismo caso
|
|
182
|
+
// dejaban de ser comparables: lo que parecía un cargo que no mejora era un juez que endurecía.
|
|
183
|
+
(contexto.forbidden && contexto.forbidden.length
|
|
184
|
+
? `El contrato de este cargo declara además estas conductas prohibidas, que rigen para todos sus ` +
|
|
185
|
+
`casos y pesan igual que los comportamientos de arriba:\n` +
|
|
186
|
+
`${contexto.forbidden.map((one) => `- ${one}`).join('\n')}\n\n` +
|
|
187
|
+
`Verificá cada una contra la respuesta y contra lo que el cargo escribió. Dos advertencias: que ` +
|
|
188
|
+
`el cargo rotule algo —«verificado», «supuesto», «hipótesis»— no prueba que el contenido sea ` +
|
|
189
|
+
`cierto ni que el rótulo sea el correcto, así que comprobalo vos; y una conducta prohibida no ` +
|
|
190
|
+
`deja de ocurrir por aparecer una sola vez en un archivo secundario.\n\n`
|
|
191
|
+
: '') +
|
|
192
|
+
`El caso pasa sólo si se observan todos los comportamientos esperados y no ocurre ninguna conducta ` +
|
|
193
|
+
`prohibida.`,
|
|
171
194
|
{ schema: VERDICT, label: `juzga:${item.id}`, phase: 'Juzgar' },
|
|
172
195
|
).then((verdict) => ({ id: item.id, expected: item.expected, answer: answer.response, verdict }))
|
|
173
196
|
: { id: item.id, expected: item.expected, answer: '', verdict: null }),
|
|
@@ -77,6 +77,32 @@ function list(root, agent) {
|
|
|
77
77
|
})
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
// Lo que el cargo no debe hacer, declarado por cargo en `expected-behaviors.yaml`.
|
|
81
|
+
//
|
|
82
|
+
// Se parsea a mano porque el archivo es una lista de escalares y el repositorio no tiene dependencias:
|
|
83
|
+
// traer un parser de YAML para leer dos listas sería pagar una dependencia por un `split`.
|
|
84
|
+
//
|
|
85
|
+
// Existe porque durante todo el catálogo esas listas no fueron criterio de nada. Los casos declaran
|
|
86
|
+
// cuatro comportamientos a observar y el juez decidía sólo con esos; los `forbidden` los leía un agente
|
|
87
|
+
// al proponer cambios, nunca al evaluar. La conducta prohibida entraba únicamente si quien lanzaba la
|
|
88
|
+
// corrida se acordaba de escribirla en el prompt, y ahí el criterio cambiaba entre corridas: tres rondas
|
|
89
|
+
// del mismo caso se midieron con tres listones distintos y dejaron de ser comparables.
|
|
90
|
+
function behaviors(root, agent) {
|
|
91
|
+
const file = path.join(catalog.resolve(root, agent), 'evaluations', 'expected-behaviors.yaml')
|
|
92
|
+
let text
|
|
93
|
+
try { text = fs.readFileSync(file, 'utf8') } catch { return { required: [], forbidden: [] } }
|
|
94
|
+
const found = { required: [], forbidden: [] }
|
|
95
|
+
let key = ''
|
|
96
|
+
for (const line of text.split('\n')) {
|
|
97
|
+
const heading = line.match(/^(\w+):\s*$/)
|
|
98
|
+
if (heading) { key = heading[1]; continue }
|
|
99
|
+
if (/^\S/.test(line)) { key = ''; continue }
|
|
100
|
+
const bullet = line.match(/^\s+-\s+(.*)$/)
|
|
101
|
+
if (bullet && found[key]) found[key].push(bullet[1].trim())
|
|
102
|
+
}
|
|
103
|
+
return found
|
|
104
|
+
}
|
|
105
|
+
|
|
80
106
|
function resultsDir(root, agent) {
|
|
81
107
|
return path.join(catalog.resolve(root, agent), ...RESULTS)
|
|
82
108
|
}
|
|
@@ -126,4 +152,4 @@ function validate(root, agent) {
|
|
|
126
152
|
return { errors, warnings, cases: total, last }
|
|
127
153
|
}
|
|
128
154
|
|
|
129
|
-
module.exports = { fixtures, list, latest, parseCase, validate, resultsDir }
|
|
155
|
+
module.exports = { behaviors, fixtures, list, latest, parseCase, validate, resultsDir }
|
package/engine/cli/ops.js
CHANGED
|
@@ -154,6 +154,7 @@ function scaffold(root, { name, mode, force = false, quiet = false }) {
|
|
|
154
154
|
const dir = path.join(root, relative)
|
|
155
155
|
if (fs.existsSync(dir)) entregado = M.record(root, relative, O.treeFiles(dir), entregado)
|
|
156
156
|
}
|
|
157
|
+
entregado = M.recordPaths(root, O.SYSTEM_FILES, entregado)
|
|
157
158
|
M.write(root, entregado)
|
|
158
159
|
// La instancia recuerda de qué versión salió: sin esto no hay actualización posible.
|
|
159
160
|
const configFile = path.join(root, 'ops.config.json')
|
|
@@ -438,7 +439,9 @@ function tree(dir, cli) {
|
|
|
438
439
|
console.log(`\n${paint('1', 'WIP')} ${wipText}`)
|
|
439
440
|
console.log(
|
|
440
441
|
`${paint('1', 'INBOX')} ${inbox.deuda} deuda · ${inbox.ideas} ideas · ` +
|
|
441
|
-
`${inbox.propuestas} propuestas · ${inbox.lecciones} lecciones
|
|
442
|
+
`${inbox.propuestas} propuestas · ${inbox.lecciones} lecciones` +
|
|
443
|
+
// Sin esto, doce viñetas sin nombre se veían como un inbox vacío y nadie se enteraba.
|
|
444
|
+
(inbox.skipped ? ` (${inbox.skipped} sin contar: falta el nombre en **negrita**)` : ''),
|
|
442
445
|
)
|
|
443
446
|
console.log(`${paint('1', 'DONE')} ${done.entries.length} tareas\n`)
|
|
444
447
|
}
|
|
@@ -626,6 +629,7 @@ function upgrade(dir, cli) {
|
|
|
626
629
|
const dir = path.join(root, relative)
|
|
627
630
|
if (fs.existsSync(dir)) registro = M.record(root, relative, O.treeFiles(dir), registro)
|
|
628
631
|
}
|
|
632
|
+
registro = M.recordPaths(root, O.SYSTEM_FILES, registro)
|
|
629
633
|
// El registro de forks se poda igual que el de archivos: un cargo devuelto al catálogo deja su
|
|
630
634
|
// entrada, y una entrada sin copia sólo puede producir avisos sobre algo que no está.
|
|
631
635
|
const vivos = Object.fromEntries(Object.entries(M.readForks(root)).filter(
|
|
@@ -974,11 +978,16 @@ function evaluate(agent, caso, cli) {
|
|
|
974
978
|
// máquina, no de persona.
|
|
975
979
|
if (cli.has('--cases')) {
|
|
976
980
|
const cases = EV.list(root, agent)
|
|
981
|
+
const prohibido = EV.behaviors(root, agent).forbidden
|
|
982
|
+
// La salida legible no lleva la conducta prohibida: `agent-propose` cuenta sus líneas para saber
|
|
983
|
+
// cuántos casos hay, y una línea de más se contaría como un caso.
|
|
977
984
|
if (!cli.has('--json')) {
|
|
978
985
|
for (const item of cases) console.log(`${item.id} ${item.expected.length} comportamiento(s)`)
|
|
979
986
|
return
|
|
980
987
|
}
|
|
981
|
-
|
|
988
|
+
// La conducta prohibida viaja junto a los casos y no dentro de cada uno: rige para los seis, y
|
|
989
|
+
// repetirla por caso invitaría a que alguien la editara en uno solo.
|
|
990
|
+
return console.log(JSON.stringify({ cases, forbidden: prohibido }))
|
|
982
991
|
}
|
|
983
992
|
const result = L.evaluate(root, agent)
|
|
984
993
|
const runs = EV.validate(root, agent)
|
package/engine/core/manifest.js
CHANGED
|
@@ -82,6 +82,31 @@ function prune(root, files) {
|
|
|
82
82
|
)
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
// Los archivos que el toolkit posee de a uno —`AGENTS.md`, `Makefile`, los README del sistema— se
|
|
86
|
+
// registran por su ruta exacta y no por directorio: no son una colección, y agruparlos por carpeta
|
|
87
|
+
// produciría claves como `./AGENTS.md` que después no casan con nada.
|
|
88
|
+
//
|
|
89
|
+
// Sin este registro, `upgrade` los reemplazaba sin comparar: una edición local desaparecía sin que
|
|
90
|
+
// nada lo dijera. Comparar contra el paquete no era alternativa —es lo que este mecanismo existe para
|
|
91
|
+
// evitar—, porque ahí toda mejora del toolkit se ve idéntica a una edición de la empresa.
|
|
92
|
+
function recordPaths(root, paths, into = {}) {
|
|
93
|
+
const record = { ...into }
|
|
94
|
+
for (const relative of paths) {
|
|
95
|
+
const file = path.join(root, relative)
|
|
96
|
+
if (fs.existsSync(file)) record[relative] = digest(file)
|
|
97
|
+
}
|
|
98
|
+
return record
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function editedPaths(root, paths) {
|
|
102
|
+
const recorded = read(root)
|
|
103
|
+
return paths.filter((relative) => {
|
|
104
|
+
const file = path.join(root, relative)
|
|
105
|
+
if (!recorded[relative] || !fs.existsSync(file)) return false
|
|
106
|
+
return recorded[relative] !== digest(file)
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
85
110
|
// Archivos que la empresa modificó después de recibirlos. Un archivo sin registro previo no
|
|
86
111
|
// cuenta: llegó con una versión anterior a este mecanismo, o lo agregó el proyecto.
|
|
87
112
|
function edited(root, relative, files) {
|
|
@@ -94,5 +119,6 @@ function edited(root, relative, files) {
|
|
|
94
119
|
}
|
|
95
120
|
|
|
96
121
|
module.exports = {
|
|
97
|
-
digest, digestText, edited, prune, read, readForks, readRunners,
|
|
122
|
+
digest, digestText, edited, editedPaths, prune, read, readForks, readRunners,
|
|
123
|
+
record, recordPaths, write,
|
|
98
124
|
}
|
package/engine/core/ownership.js
CHANGED
|
@@ -228,6 +228,10 @@ function localChanges(root) {
|
|
|
228
228
|
if (!fs.existsSync(dir)) continue
|
|
229
229
|
for (const file of manifest.edited(root, target, treeFiles(dir))) changed.push(`${target}/${file}`)
|
|
230
230
|
}
|
|
231
|
+
// Los archivos sueltos del sistema entran por la misma puerta. Quedaban afuera, así que `upgrade`
|
|
232
|
+
// los reemplazaba en silencio: un cargo escribió el índice de ADR que el propio README le pedía
|
|
233
|
+
// actualizar, comprobó que se perdería, y prefirió no dejar una entrada condenada a desaparecer.
|
|
234
|
+
changed.push(...manifest.editedPaths(root, SYSTEM_FILES))
|
|
231
235
|
return changed
|
|
232
236
|
}
|
|
233
237
|
|
package/engine/hooks/run.js
CHANGED
|
@@ -85,10 +85,22 @@ function stagedFiles(dir) {
|
|
|
85
85
|
return result.status === 0 ? result.stdout.trim().split('\n').filter(Boolean) : []
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
// R10 pide «la autorización configurada para el proyecto» y `runner.allowPush` es esa configuración:
|
|
89
|
+
// sin esto era un interruptor que nadie leía, y un cargo que lo leyó dio por imposible un push que el
|
|
90
|
+
// guard bloqueaba igual. Sin raíz legible no hay permiso que verificar, así que no se autoriza.
|
|
91
|
+
function pushAllowed(input) {
|
|
92
|
+
const root = findOpsRoot(process.env.OPS_ROOT || process.env.CLAUDE_PROJECT_DIR || cwdOf(input))
|
|
93
|
+
if (!root) return false
|
|
94
|
+
const runner = configOf(root).runner
|
|
95
|
+
return Boolean(runner && runner.allowPush === true)
|
|
96
|
+
}
|
|
97
|
+
|
|
88
98
|
function destructive(input) {
|
|
89
99
|
const command = commandOf(input)
|
|
100
|
+
if (/\bgit\s+push\b/.test(command) && !pushAllowed(input)) {
|
|
101
|
+
block("'git push' publica cambios y requiere una acción humana. Se habilita con runner.allowPush.")
|
|
102
|
+
}
|
|
90
103
|
const rules = [
|
|
91
|
-
[/\bgit\s+push\b/, "'git push' publica cambios y requiere una acción humana."],
|
|
92
104
|
[/\bgit\s+reset\s+--hard\b/, "'git reset --hard' destruye cambios locales."],
|
|
93
105
|
[/\bgit\s+clean\s+-[^\s]*f/, "'git clean -f' borra archivos sin seguimiento."],
|
|
94
106
|
[
|
|
@@ -6,6 +6,16 @@ const path = require('node:path')
|
|
|
6
6
|
const REQUIRED = ['## Reglas', '## Por qué existe cada regla', '## Historial']
|
|
7
7
|
const ID_PATTERN = /\|\s*(BR-[A-Z0-9]+-\d{3})\s*\|/g
|
|
8
8
|
|
|
9
|
+
// El estado se valida contra un conjunto cerrado, y no es prolijidad: es la diferencia entre una regla
|
|
10
|
+
// que rige y una que espera aprobación. Antes el patrón aceptaba cualquier texto y la plantilla traía
|
|
11
|
+
// `vigente` cableado, así que un cargo que copiaba la plantilla recibía «vigente» gratis y tenía que
|
|
12
|
+
// acordarse de debilitarlo — mientras la plantilla de ADR le presentaba el menú y lo obligaba a elegir.
|
|
13
|
+
//
|
|
14
|
+
// Esa asimetría produjo el mismo error en tres cargos distintos: reglas declarándose vigentes derivadas
|
|
15
|
+
// de un ADR que los mismos cargos habían dejado en `Propuesto`. Hacían lo que cada plantilla les pedía.
|
|
16
|
+
const STATES = ['propuesta', 'vigente', 'derogada']
|
|
17
|
+
const METADATA = /> \*\*Dominio:\*\* (.+) \| \*\*Estado:\*\* (.+) \| \*\*Actualizado:\*\* (\d{4}-\d{2}-\d{2})/
|
|
18
|
+
|
|
9
19
|
function markdownFiles(root) {
|
|
10
20
|
if (!fs.existsSync(root)) return []
|
|
11
21
|
const files = []
|
|
@@ -25,8 +35,10 @@ function validate(root) {
|
|
|
25
35
|
for (const file of markdownFiles(root)) {
|
|
26
36
|
const relative = path.relative(path.dirname(root), file)
|
|
27
37
|
const source = fs.readFileSync(file, 'utf8')
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
const metadata = source.match(METADATA)
|
|
39
|
+
if (!metadata) errors.push(`${relative}: falta metadata Dominio/Estado/Actualizado`)
|
|
40
|
+
else if (!STATES.includes(metadata[2].trim().toLowerCase())) {
|
|
41
|
+
errors.push(`${relative}: Estado «${metadata[2].trim()}» no es ${STATES.join(', ')}`)
|
|
30
42
|
}
|
|
31
43
|
for (const heading of REQUIRED) {
|
|
32
44
|
if (!source.includes(heading)) errors.push(`${relative}: falta ${heading}`)
|
|
@@ -67,9 +67,13 @@ function readEpics(dir) {
|
|
|
67
67
|
const field = frontmatter(text)
|
|
68
68
|
const criteria = [...section(text, /Criterios/i).matchAll(/^-\s+\*\*(C\d+)(?:[^*]*)\*\*\s+[—-]\s+(.+)$/gm)]
|
|
69
69
|
.map((match) => ({ id: match[1], text: match[2].trim() }))
|
|
70
|
+
// `(?![\s\S])` y no `$`: con la bandera `m` el `$` casa fin de **línea**, así que el cuerpo no ávido
|
|
71
|
+
// cortaba en el primer salto y una historia envuelta perdía su `(service: …)` y sus `(→ CN)`. El
|
|
72
|
+
// error que salía era «no declara (service: <ruta>)» — culpaba al autor de algo que sí había escrito.
|
|
73
|
+
// Dos cargos distintos lo encontraron reescribiendo su historia hasta que entrara en un solo renglón.
|
|
70
74
|
const storyPattern = new RegExp(
|
|
71
75
|
String.raw`^[-*]\s+(?:\[[ xX]\]\s+)?\*\*([^*]+)\*\*\s+([\s\S]*?)` +
|
|
72
|
-
String.raw`(?=\n[-*]\s+(?:\[[ xX]\]\s+)?\*\*|\n
|
|
76
|
+
String.raw`(?=\n[-*]\s+(?:\[[ xX]\]\s+)?\*\*|\n##|(?![\s\S]))`,
|
|
73
77
|
'gm',
|
|
74
78
|
)
|
|
75
79
|
const stories = [...section(text, /Historias/i).matchAll(storyPattern)]
|
|
@@ -163,11 +167,19 @@ function readWip(dir) {
|
|
|
163
167
|
}
|
|
164
168
|
}
|
|
165
169
|
|
|
170
|
+
// Un ítem se cuenta cuando empieza con su nombre en negrita, y el nombre existe para poder citarlo
|
|
171
|
+
// —`HUMAN_ACTIONS.md` y las propuestas mensuales se refieren a un ítem por ese slug—. La convención se
|
|
172
|
+
// conserva; lo que no se conserva es el silencio.
|
|
173
|
+
//
|
|
174
|
+
// La plantilla no traía ningún ejemplo, así que quien escribía viñetas planas veía cero ítems sobre un
|
|
175
|
+
// archivo con doce y nada se lo decía. `skipped` es lo que vuelve visible esa diferencia.
|
|
166
176
|
function readInbox(dir) {
|
|
167
|
-
const result = { deuda: 0, ideas: 0, propuestas: 0, lecciones: 0 }
|
|
177
|
+
const result = { deuda: 0, ideas: 0, propuestas: 0, lecciones: 0, skipped: 0 }
|
|
168
178
|
for (const part of read(path.join(dir, 'INBOX.md')).split(/^##\s+/m)) {
|
|
169
179
|
const title = part.split('\n')[0]
|
|
180
|
+
const bullets = (part.match(/^[-*]\s+(?:\[[ xX]\]\s+)?/gm) || []).length
|
|
170
181
|
const count = (part.match(/^[-*]\s+(?:\[[ xX]\]\s+)?\*\*/gm) || []).length
|
|
182
|
+
if (/Deuda|Ideas|Visi[oó]n|Propuestas|Lecciones/i.test(title)) result.skipped += bullets - count
|
|
171
183
|
if (/Deuda/i.test(title)) result.deuda = count
|
|
172
184
|
if (/Ideas|Visi[oó]n/i.test(title)) result.ideas = count
|
|
173
185
|
if (/Propuestas/i.test(title)) result.propuestas = count
|
package/package.json
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ingeniomaps/cauce",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Sistema portable de planificación y ejecución verificable para cualquier proyecto",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"planning",
|
|
7
|
+
"agents",
|
|
8
|
+
"ai-agents",
|
|
9
|
+
"cli",
|
|
10
|
+
"workflow",
|
|
11
|
+
"governance"
|
|
12
|
+
],
|
|
5
13
|
"type": "commonjs",
|
|
6
14
|
"bin": {
|
|
7
15
|
"cauce": "engine/cli/ops.js"
|
|
@@ -12,28 +20,33 @@
|
|
|
12
20
|
"agents/",
|
|
13
21
|
"teams/",
|
|
14
22
|
"automatization/",
|
|
15
|
-
".github/workflows/",
|
|
16
|
-
"LICENSE",
|
|
17
23
|
"CHANGELOG.md"
|
|
18
24
|
],
|
|
19
25
|
"scripts": {
|
|
20
26
|
"ops": "node engine/cli/ops.js",
|
|
21
27
|
"check": "node engine/cli/ops.js check template/planning",
|
|
22
|
-
"tree": "node engine/cli/ops.js tree template/planning --no-color",
|
|
23
28
|
"automation:check": "node engine/cli/ops.js automation check .",
|
|
24
29
|
"integration:check": "node engine/cli/ops.js integration check template jira",
|
|
25
30
|
"test": "bash test/hooks-smoke.sh && node --test test/*.test.js",
|
|
26
31
|
"coverage": "bash test/coverage.sh",
|
|
27
32
|
"coverage:update": "bash test/coverage.sh --update",
|
|
28
|
-
"ci": "npm run check && npm run automation:check && npm run integration:check && npm run coverage"
|
|
33
|
+
"ci": "npm run check && npm run automation:check && npm run integration:check && npm run coverage",
|
|
34
|
+
"prepublishOnly": "npm run ci"
|
|
29
35
|
},
|
|
30
36
|
"engines": {
|
|
31
37
|
"node": ">=24"
|
|
32
38
|
},
|
|
33
39
|
"packageManager": "npm@11.16.0",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
34
43
|
"repository": {
|
|
35
44
|
"type": "git",
|
|
36
45
|
"url": "git+https://github.com/ingeniomaps/cauce.git"
|
|
37
46
|
},
|
|
47
|
+
"homepage": "https://github.com/ingeniomaps/cauce#readme",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/ingeniomaps/cauce/issues"
|
|
50
|
+
},
|
|
38
51
|
"license": "MIT"
|
|
39
52
|
}
|
package/template/Makefile
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
.DEFAULT_GOAL := help
|
|
2
2
|
|
|
3
3
|
.PHONY: help check tree context upgrade automation-check
|
|
4
|
-
.PHONY:
|
|
4
|
+
.PHONY: integration-check integration-sync require-key integration-promote
|
|
5
|
+
.PHONY: require-agent agent-learn agent-propose agent-evaluate require-team team-check team-show
|
|
5
6
|
.PHONY: install-claude install-codex install-gemini install-antigravity
|
|
6
7
|
.PHONY: doctor-claude doctor-codex doctor-gemini doctor-antigravity
|
|
7
8
|
|
|
9
|
+
# Jira es hoy el único proveedor; el día que haya otro se pasa PROVIDER=<slug>.
|
|
10
|
+
PROVIDER ?= jira
|
|
11
|
+
|
|
8
12
|
help: ## Muestra los comandos disponibles y su propósito
|
|
9
13
|
@awk 'BEGIN {FS = ":.*## "; printf "Uso: make <comando>\n\n"} \
|
|
10
|
-
/^[a-zA-Z0-9_-]+:.*## / {printf " %-
|
|
14
|
+
/^[a-zA-Z0-9_-]+:.*## / {printf " %-22s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
|
11
15
|
|
|
12
16
|
check: ## Valida planning e integraciones
|
|
13
17
|
@node tools/ops.js check planning
|
|
@@ -24,6 +28,18 @@ upgrade: ## Actualiza Cauce sin tocar lo del proyecto
|
|
|
24
28
|
automation-check: ## Comprueba hooks, workflows y adaptadores
|
|
25
29
|
@node tools/ops.js automation check .
|
|
26
30
|
|
|
31
|
+
integration-check: ## Valida configuración y staging, sin conectarse
|
|
32
|
+
@node tools/ops.js integration check .
|
|
33
|
+
|
|
34
|
+
integration-sync: ## Lee el proveedor y actualiza staging; PROVIDER=<slug> para elegir otro
|
|
35
|
+
@node tools/ops.js integration sync . "$(PROVIDER)"
|
|
36
|
+
|
|
37
|
+
require-key:
|
|
38
|
+
@test -n "$(KEY)" || (echo "Falta KEY=<clave-remota>" >&2; exit 2)
|
|
39
|
+
|
|
40
|
+
integration-promote: require-key ## Promueve el draft KEY=<clave> al roadmap
|
|
41
|
+
@node tools/ops.js integration promote . "$(PROVIDER)" "$(KEY)"
|
|
42
|
+
|
|
27
43
|
require-agent:
|
|
28
44
|
@test -n "$(AGENT)" || (echo "Falta AGENT=<slug>" >&2; exit 2)
|
|
29
45
|
|
|
@@ -36,6 +52,15 @@ agent-propose: require-agent ## Consolida una propuesta para AGENT=<slug>
|
|
|
36
52
|
agent-evaluate: require-agent ## Evalúa controles y casos de AGENT=<slug>
|
|
37
53
|
@node tools/ops.js evaluate "$(AGENT)"
|
|
38
54
|
|
|
55
|
+
require-team:
|
|
56
|
+
@test -n "$(TEAM)" || (echo "Falta TEAM=<slug>" >&2; exit 2)
|
|
57
|
+
|
|
58
|
+
team-check: require-team ## Valida contrato, agentes y etapas de TEAM=<slug>
|
|
59
|
+
@node tools/ops.js team check "$(TEAM)"
|
|
60
|
+
|
|
61
|
+
team-show: require-team ## Muestra el recorrido de TEAM=<slug>
|
|
62
|
+
@node tools/ops.js team show "$(TEAM)"
|
|
63
|
+
|
|
39
64
|
install-claude: ## Instala hooks y workflows para Claude
|
|
40
65
|
@node tools/ops.js automation install . claude
|
|
41
66
|
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
El runner puede agregar; solo una persona promueve y elimina.
|
|
4
4
|
|
|
5
|
+
Cada ítem empieza con su nombre en negrita, porque ese nombre es con el que se lo cita después desde
|
|
6
|
+
`HUMAN_ACTIONS.md`, desde una épica o desde una propuesta mensual. Una viñeta sin nombre no se cuenta, y
|
|
7
|
+
`ops tree` avisa cuántas quedaron afuera:
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
- **slug-del-item** — Qué es, y qué se decide o se resuelve con esto.
|
|
11
|
+
```
|
|
12
|
+
|
|
5
13
|
## Deuda
|
|
6
14
|
|
|
7
15
|
## Ideas
|
|
@@ -25,8 +25,11 @@ de una, crea una ADR propia que documente la excepción; no modifica silenciosam
|
|
|
25
25
|
1. Copiar `000-template.md` como `NNN-slug-corto.md`.
|
|
26
26
|
2. Documentar contexto, fuentes, supuestos, decisión, alternativas y costos.
|
|
27
27
|
3. Declarar por separado lo construido y lo pendiente.
|
|
28
|
-
4.
|
|
29
|
-
|
|
28
|
+
4. Al reemplazarla, enlazar la nueva ADR sin borrar el historial.
|
|
29
|
+
|
|
30
|
+
No hay índice que mantener: las decisiones del proyecto son los archivos `NNN-*.md` de este
|
|
31
|
+
directorio, y su estado vive en cada uno. Una tabla que los repitiera envejecería aparte —y este
|
|
32
|
+
archivo lo mantiene Cauce, así que una fila agregada acá se perdería en el próximo `upgrade`.
|
|
30
33
|
|
|
31
34
|
## Decisiones del sistema
|
|
32
35
|
|
|
@@ -34,9 +37,3 @@ de una, crea una ADR propia que documente la excepción; no modifica silenciosam
|
|
|
34
37
|
- [OPS-002](system/OPS-002-runtime-autocontenido-y-neutral-al-runner.md): runtime portable y neutral.
|
|
35
38
|
- [OPS-003](system/OPS-003-integraciones-seguras-por-staging.md): integraciones mediante staging seguro.
|
|
36
39
|
- [OPS-004](system/OPS-004-promocion-humana-y-evidencia-verificable.md): promoción controlada y verificable.
|
|
37
|
-
|
|
38
|
-
## Decisiones del proyecto
|
|
39
|
-
|
|
40
|
-
| ADR | Decisión | Fecha | Estado |
|
|
41
|
-
|---|---|---|---|
|
|
42
|
-
| _(ninguna todavía)_ | | | |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Tema
|
|
2
2
|
|
|
3
|
-
> **Dominio:** nombre | **Estado:** vigente | **Actualizado:** YYYY-MM-DD
|
|
3
|
+
> **Dominio:** nombre | **Estado:** propuesta | vigente | derogada | **Actualizado:** YYYY-MM-DD
|
|
4
4
|
|
|
5
5
|
Describir brevemente el comportamiento de negocio compartido y el riesgo que controla. Las fuentes no obvias
|
|
6
6
|
se citan como `[fuente: ruta#sección|ADR-NNN|URL]`; lo desconocido se marca `[supuesto: explicación]`.
|
|
@@ -30,3 +30,30 @@ deja el pedido sin resolver: es la mitad barata del trabajo.
|
|
|
30
30
|
|
|
31
31
|
Esto no afloja ningún límite: no promover, no prometer fechas, no inventar evidencia y no exceder la
|
|
32
32
|
autoridad del cargo siguen siendo absolutos. Lo que se cierra es la salida de cumplirlos sin entregar.
|
|
33
|
+
|
|
34
|
+
## R14 — Una afirmación de mecanismo lleva su registro
|
|
35
|
+
|
|
36
|
+
El comportamiento de una herramienta, un motor, un formato, una norma o un sistema de terceros es material
|
|
37
|
+
de trabajo, no contexto: es público, versionado y comprobable, y por eso no se afirma de memoria. Si esa
|
|
38
|
+
afirmación es falsa, la conclusión que sostiene deja de ser creíble aunque sea correcta por otras razones,
|
|
39
|
+
y lo que quedó escrito contamina a quien lo lea después. Exagerar un riesgo cuesta lo mismo que minimizarlo.
|
|
40
|
+
|
|
41
|
+
Cada afirmación declara en cuál de los tres registros va:
|
|
42
|
+
|
|
43
|
+
- **Verificado** — comprobado en esta corrida, y consta cómo: la salida obtenida, la versión del binario, la
|
|
44
|
+
cita literal de la fuente. Vale para lo comprobado, y no se extiende a otra versión, edición ni jurisdicción.
|
|
45
|
+
- **Documentado** — está en la fuente pública de la versión o edición declaradas, sin comprobación local. Se
|
|
46
|
+
cita con esa versión; si la del entorno no consta, se dice.
|
|
47
|
+
- **Hipótesis** — plausible y no comprobable acá. Va marcada como tal y no sostiene una negativa, un diagnóstico,
|
|
48
|
+
un número ni un paso de procedimiento, ni entra en informe, runbook, regla o lección.
|
|
49
|
+
|
|
50
|
+
Son dos ejes distintos y el registro va en los dos: marcar como supuesto un número propio no dice nada sobre el
|
|
51
|
+
mecanismo del que ese número se deriva. Un parámetro declarado supuesto sigue prometiendo el efecto que se le
|
|
52
|
+
atribuye, y esa atribución es la que lleva registro.
|
|
53
|
+
|
|
54
|
+
La verificación llega hasta donde R12 permite: fuente pública, `--help`, `--version`, una invocación inocua.
|
|
55
|
+
Nunca conectarse a un sistema real ni ejecutar la operación cuyo efecto se describe. Si el mecanismo sólo se
|
|
56
|
+
establece ejecutando lo destructivo, queda en hipótesis; acá la abstención vale más que el dato.
|
|
57
|
+
|
|
58
|
+
No se infiere el default de una herramienta desde otra del mismo paquete, ni una regla de una jurisdicción
|
|
59
|
+
desde otra. Una negativa correcta sostenida en un mecanismo falso queda tan comprometida como el mecanismo.
|