@ingeniomaps/cauce 0.70.0 → 0.71.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 +127 -0
- package/agents/README.md +38 -0
- package/agents/roles/system/accounting-specialist/learning/HISTORY.md +2 -0
- package/agents/roles/system/ai-governance-lead/learning/HISTORY.md +6 -1
- package/agents/roles/system/ai-governance-lead/learning/sources.yaml +4 -4
- package/agents/roles/system/ai-governance-lead/references/operating-model.md +2 -2
- package/agents/roles/system/ai-product-manager/learning/HISTORY.md +4 -1
- package/agents/roles/system/ai-product-manager/learning/sources.yaml +2 -2
- package/agents/roles/system/ai-product-manager/references/operating-model.md +2 -2
- package/agents/roles/system/analytics-engineer/learning/HISTORY.md +4 -1
- package/agents/roles/system/analytics-engineer/learning/sources.yaml +2 -2
- package/agents/roles/system/analytics-engineer/references/operating-model.md +2 -2
- package/agents/roles/system/backend-engineer/learning/HISTORY.md +2 -0
- package/agents/roles/system/business-strategist/learning/HISTORY.md +2 -0
- package/agents/roles/system/business-strategist/learning/sources.yaml +1 -1
- package/agents/roles/system/business-strategist/references/operating-model.md +2 -2
- package/agents/roles/system/cloud-architect/learning/HISTORY.md +1 -1
- package/agents/roles/system/cloud-architect/learning/sources.yaml +2 -2
- package/agents/roles/system/cloud-architect/references/operating-model.md +2 -2
- package/agents/roles/system/community-manager/learning/HISTORY.md +4 -1
- package/agents/roles/system/community-manager/learning/sources.yaml +2 -2
- package/agents/roles/system/community-manager/references/operating-model.md +1 -1
- package/agents/roles/system/content-specialist/learning/HISTORY.md +2 -0
- package/agents/roles/system/customer-success-manager/learning/HISTORY.md +2 -0
- package/agents/roles/system/customer-success-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/customer-success-manager/references/operating-model.md +3 -3
- package/agents/roles/system/customer-support-specialist/learning/HISTORY.md +2 -0
- package/agents/roles/system/customer-support-specialist/learning/sources.yaml +2 -2
- package/agents/roles/system/customer-support-specialist/references/operating-model.md +2 -2
- package/agents/roles/system/data-analyst/learning/HISTORY.md +2 -0
- package/agents/roles/system/data-engineer/learning/HISTORY.md +4 -1
- package/agents/roles/system/data-engineer/learning/sources.yaml +3 -3
- package/agents/roles/system/data-engineer/references/operating-model.md +2 -2
- package/agents/roles/system/data-governance-steward/learning/HISTORY.md +2 -0
- package/agents/roles/system/data-scientist/learning/HISTORY.md +4 -1
- package/agents/roles/system/data-scientist/learning/sources.yaml +3 -3
- package/agents/roles/system/data-scientist/references/operating-model.md +2 -2
- package/agents/roles/system/database-administrator/learning/HISTORY.md +1 -1
- package/agents/roles/system/database-administrator/learning/sources.yaml +2 -2
- package/agents/roles/system/database-administrator/references/operating-model.md +2 -2
- package/agents/roles/system/developer-relations-engineer/learning/HISTORY.md +4 -1
- package/agents/roles/system/devops-engineer/learning/HISTORY.md +2 -0
- package/agents/roles/system/engineering-manager/learning/HISTORY.md +2 -0
- package/agents/roles/system/engineering-manager/learning/sources.yaml +1 -1
- package/agents/roles/system/engineering-manager/references/operating-model.md +2 -2
- package/agents/roles/system/financial-controller/learning/HISTORY.md +2 -0
- package/agents/roles/system/financial-controller/learning/sources.yaml +2 -2
- package/agents/roles/system/financial-controller/references/operating-model.md +1 -1
- package/agents/roles/system/finops-engineer/learning/HISTORY.md +2 -0
- package/agents/roles/system/fraud-risk-analyst/learning/HISTORY.md +2 -0
- package/agents/roles/system/frontend-engineer/learning/HISTORY.md +2 -0
- package/agents/roles/system/growth-marketer/learning/HISTORY.md +2 -0
- package/agents/roles/system/growth-marketer/learning/sources.yaml +1 -1
- package/agents/roles/system/implementation-manager/learning/HISTORY.md +1 -1
- package/agents/roles/system/implementation-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/implementation-manager/references/operating-model.md +3 -3
- package/agents/roles/system/integrations-engineer/learning/HISTORY.md +2 -0
- package/agents/roles/system/kyc-aml-specialist/learning/HISTORY.md +3 -2
- package/agents/roles/system/kyc-aml-specialist/learning/sources.yaml +1 -1
- package/agents/roles/system/kyc-aml-specialist/references/operating-model.md +1 -1
- package/agents/roles/system/legal-counsel/learning/HISTORY.md +6 -1
- package/agents/roles/system/legal-counsel/learning/sources.yaml +1 -1
- package/agents/roles/system/legal-counsel/references/operating-model.md +1 -1
- package/agents/roles/system/logistics-operations-manager/learning/HISTORY.md +2 -0
- package/agents/roles/system/machine-learning-engineer/learning/HISTORY.md +4 -1
- package/agents/roles/system/machine-learning-engineer/learning/sources.yaml +6 -6
- package/agents/roles/system/machine-learning-engineer/references/operating-model.md +3 -3
- package/agents/roles/system/mlops-engineer/learning/HISTORY.md +4 -1
- package/agents/roles/system/mlops-engineer/learning/sources.yaml +2 -2
- package/agents/roles/system/mlops-engineer/references/operating-model.md +2 -2
- package/agents/roles/system/mobile-engineer/learning/HISTORY.md +2 -0
- package/agents/roles/system/mobile-engineer/learning/sources.yaml +1 -1
- package/agents/roles/system/partnerships-manager/learning/HISTORY.md +4 -1
- package/agents/roles/system/partnerships-manager/learning/sources.yaml +2 -2
- package/agents/roles/system/partnerships-manager/references/operating-model.md +2 -2
- package/agents/roles/system/people-operations-manager/learning/HISTORY.md +4 -1
- package/agents/roles/system/people-operations-manager/learning/sources.yaml +2 -2
- package/agents/roles/system/people-operations-manager/references/operating-model.md +2 -2
- package/agents/roles/system/privacy-compliance-specialist/learning/HISTORY.md +2 -0
- package/agents/roles/system/privacy-compliance-specialist/learning/sources.yaml +1 -1
- package/agents/roles/system/privacy-compliance-specialist/references/operating-model.md +1 -1
- package/agents/roles/system/procurement-manager/learning/HISTORY.md +1 -1
- package/agents/roles/system/procurement-manager/learning/sources.yaml +2 -2
- package/agents/roles/system/procurement-manager/references/operating-model.md +2 -2
- package/agents/roles/system/product-manager/learning/HISTORY.md +1 -1
- package/agents/roles/system/product-marketing-manager/learning/HISTORY.md +2 -0
- package/agents/roles/system/product-marketing-manager/learning/sources.yaml +1 -1
- package/agents/roles/system/project-manager/learning/HISTORY.md +4 -1
- package/agents/roles/system/project-manager/learning/sources.yaml +1 -1
- package/agents/roles/system/project-manager/references/operating-model.md +2 -2
- package/agents/roles/system/qa-engineer/learning/HISTORY.md +2 -0
- package/agents/roles/system/qa-engineer/learning/sources.yaml +9 -11
- package/agents/roles/system/qa-engineer/references/operating-model.md +2 -2
- package/agents/roles/system/release-manager/learning/HISTORY.md +1 -1
- package/agents/roles/system/sales-representative/learning/HISTORY.md +2 -0
- package/agents/roles/system/sales-representative/learning/sources.yaml +2 -2
- package/agents/roles/system/sales-representative/references/operating-model.md +1 -1
- package/agents/roles/system/security-engineer/learning/HISTORY.md +2 -0
- package/agents/roles/system/security-engineer/learning/sources.yaml +1 -1
- package/agents/roles/system/security-engineer/references/operating-model.md +1 -1
- package/agents/roles/system/site-reliability-engineer/learning/HISTORY.md +2 -0
- package/agents/roles/system/software-architect/learning/HISTORY.md +2 -0
- package/agents/roles/system/software-architect/learning/sources.yaml +2 -2
- package/agents/roles/system/software-architect/references/operating-model.md +1 -1
- package/agents/roles/system/solutions-engineer/learning/HISTORY.md +4 -1
- package/agents/roles/system/solutions-engineer/learning/sources.yaml +4 -4
- package/agents/roles/system/solutions-engineer/references/operating-model.md +2 -2
- package/agents/roles/system/tech-lead/learning/HISTORY.md +2 -0
- package/agents/roles/system/technical-program-manager/learning/HISTORY.md +4 -1
- package/agents/roles/system/technical-program-manager/learning/sources.yaml +2 -2
- package/agents/roles/system/technical-program-manager/references/operating-model.md +3 -3
- package/agents/roles/system/technical-writer/learning/HISTORY.md +4 -1
- package/agents/roles/system/treasury-analyst/learning/HISTORY.md +2 -0
- package/agents/roles/system/ui-designer/learning/HISTORY.md +2 -0
- package/agents/roles/system/ui-designer/learning/sources.yaml +2 -2
- package/agents/roles/system/user-researcher/learning/HISTORY.md +1 -1
- package/agents/roles/system/user-researcher/learning/sources.yaml +1 -1
- package/agents/roles/system/ux-designer/learning/HISTORY.md +2 -0
- package/agents/roles/system/ux-designer/learning/sources.yaml +1 -1
- package/agents/roles/system/ux-designer/references/operating-model.md +1 -1
- package/automatization/workflows/autobuild.js +38 -20
- package/engine/agents/learning-seal.js +36 -5
- package/engine/agents/learning-sources.js +46 -2
- package/engine/agents/learning.js +13 -1
- package/engine/cli/planning.js +29 -0
- package/package.json +1 -1
- package/template/planning/PROTOCOL.md +14 -0
|
@@ -90,6 +90,12 @@ function sourceTiers(text) {
|
|
|
90
90
|
return [...body.matchAll(/tier:\s*([A-Za-z-]+)/g)].map((hit) => hit[1])
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
// Una entrada escrita en una sola línea: seis cargos del catálogo la escriben así y cuarenta y siete la
|
|
94
|
+
// reparten en varias. Leyendo sólo la segunda forma, esos seis no tenían el chequeo de URL duplicada que
|
|
95
|
+
// hay más abajo, y nada lo decía porque no encontrar duplicados y no mirar se ven igual.
|
|
96
|
+
const FLOW_ENTRY = /^\s*-\s*\{[^}]*\bname:\s*([^,}]+?)\s*,[^}]*\burl:\s*"?([^",}\s]+)/
|
|
97
|
+
const quitar = (value) => value.replace(/^['"]|['"]$/g, '')
|
|
98
|
+
|
|
93
99
|
// Las fuentes de un cargo, por su URL. La misma URL con dos nombres es una sola fuente contada dos
|
|
94
100
|
// veces: el catálogo llegó a tener la especificación OpenAPI bajo tres —`OpenAPI Specification`,
|
|
95
101
|
// `...latest published` y `...3.2.0`— así que arreglarle el `tier` a un cargo no se lo arreglaba a los
|
|
@@ -99,14 +105,52 @@ function sourceUrls(text) {
|
|
|
99
105
|
const out = []
|
|
100
106
|
let name = ''
|
|
101
107
|
for (const line of body.split('\n')) {
|
|
108
|
+
const flow = line.match(FLOW_ENTRY)
|
|
109
|
+
if (flow) { out.push({ name: quitar(flow[1]), url: flow[2].replace(/\/+$/, '') }); continue }
|
|
102
110
|
const declared = line.match(/^\s*-\s*name:\s*(.+?)\s*$/)
|
|
103
|
-
if (declared) { name = declared[1]
|
|
111
|
+
if (declared) { name = quitar(declared[1]); continue }
|
|
104
112
|
const url = line.match(/^\s*url:\s*(\S+)/)
|
|
105
113
|
if (url && name) out.push({ name, url: url[1].replace(/\/+$/, '') })
|
|
106
114
|
}
|
|
107
115
|
return out
|
|
108
116
|
}
|
|
109
117
|
|
|
118
|
+
// Todo lo que un cargo cita y alguien va a abrir. `sources.yaml` es lo que investiga; `references/` y
|
|
119
|
+
// `SKILL.md` son el método que sigue, y hasta 0.71.0 nadie las miraba: 29 de las 207 URLs de esos
|
|
120
|
+
// documentos no servían, entre ellas dos 404 de páginas que se habían movido.
|
|
121
|
+
//
|
|
122
|
+
// `evaluations/` queda afuera y no por costo: los casos adversariales **inventan** dominios a propósito
|
|
123
|
+
// —veintiuna URLs bajo `.example` y marcas que no existen— y comprobarlas reportaría rotas las que están
|
|
124
|
+
// bien escritas. `learning/reports` y `learning/proposals` también, por lo contrario: son evidencia
|
|
125
|
+
// fechada de que algo dio 403 el día que se consultó, y eso no se arregla.
|
|
126
|
+
const DOC_URL = /https?:\/\/[^\s)>"`\]]+/g
|
|
127
|
+
function documentUrls(dir) {
|
|
128
|
+
const out = []
|
|
129
|
+
const seen = new Set()
|
|
130
|
+
const add = (raw, origin) => {
|
|
131
|
+
const url = raw.replace(/[.,;:]+$/, '')
|
|
132
|
+
if (seen.has(url)) return
|
|
133
|
+
seen.add(url)
|
|
134
|
+
out.push({ url, origin })
|
|
135
|
+
}
|
|
136
|
+
const below = (base, relative) => {
|
|
137
|
+
let entries = []
|
|
138
|
+
try { entries = fs.readdirSync(path.join(base, relative), { withFileTypes: true }) } catch { return }
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
const next = `${relative}/${entry.name}`
|
|
141
|
+
if (entry.isDirectory()) { below(base, next); continue }
|
|
142
|
+
if (!entry.name.endsWith('.md')) continue
|
|
143
|
+
for (const hit of fs.readFileSync(path.join(base, next), 'utf8').match(DOC_URL) || []) add(hit, next)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
below(dir, 'references')
|
|
147
|
+
const skill = path.join(dir, 'SKILL.md')
|
|
148
|
+
if (fs.existsSync(skill)) {
|
|
149
|
+
for (const hit of fs.readFileSync(skill, 'utf8').match(DOC_URL) || []) add(hit, 'SKILL.md')
|
|
150
|
+
}
|
|
151
|
+
return out
|
|
152
|
+
}
|
|
153
|
+
|
|
110
154
|
function evaluate(root, agent) {
|
|
111
155
|
const target = catalog.resolve(root, agent)
|
|
112
156
|
const errors = []
|
|
@@ -193,4 +237,4 @@ function evaluate(root, agent) {
|
|
|
193
237
|
return { errors, warnings, proposals: proposals.length, pending, cases }
|
|
194
238
|
}
|
|
195
239
|
|
|
196
|
-
module.exports = { SOURCE_TIERS, cadence, evaluate, evaluateTeam }
|
|
240
|
+
module.exports = { SOURCE_TIERS, cadence, documentUrls, evaluate, evaluateTeam, sourceUrls }
|
|
@@ -40,15 +40,21 @@ function prepareReport(root, agent, now = new Date()) {
|
|
|
40
40
|
// Los sellos de estado de este módulo escriben atómico y esto no, y la diferencia es qué se pierde
|
|
41
41
|
// si la escritura se corta: allá el archivo ya existía y quedaría truncado, acá no había nada. Un
|
|
42
42
|
// documento nuevo a medio escribir se ve; uno viejo a medio pisar se lee como si estuviera entero.
|
|
43
|
+
// `propone` nace sin contestar y lo completa quien escribe el informe. No se deduce del texto de
|
|
44
|
+
// «Recomendación»: los veinte informes del 2026-09-07 tenían texto ahí y once no proponían ningún
|
|
45
|
+
// cambio —explicaban por qué, que es lo correcto—, así que «sección vacía» no distingue nada. Con
|
|
46
|
+
// el campo, un informe que no cambia ningún contrato se mergea solo en vez de gastar una revisión
|
|
47
|
+
// humana que no puede decidir nada.
|
|
43
48
|
fs.writeFileSync(file, `---
|
|
44
49
|
agent: ${agent}
|
|
45
50
|
date: ${isoDate(now)}
|
|
46
51
|
status: draft
|
|
52
|
+
propone: por-completar
|
|
47
53
|
---
|
|
48
54
|
|
|
49
55
|
# Investigación semanal — ${isoDate(now)}
|
|
50
56
|
|
|
51
|
-
<!--
|
|
57
|
+
<!-- Tres convenciones que el ciclo necesita y que nada más sostiene:
|
|
52
58
|
|
|
53
59
|
· Etiquetá cada hallazgo H1, H2, … en el orden en que aparecen. «Evidencia» y «Recomendación» se
|
|
54
60
|
refieren a ellos por esa clave, y la propuesta mensual la cita para decir de qué hallazgo sale
|
|
@@ -57,6 +63,12 @@ status: draft
|
|
|
57
63
|
· No renombres los títulos. «## Recomendación» se lee con un patrón exacto y es lo único que la
|
|
58
64
|
propuesta consolida de cada informe: renombrarlo no da error, deja la propuesta vacía.
|
|
59
65
|
|
|
66
|
+
· Contestá «propone» en el frontmatter, con «si» o con «no». Es «si» cuando el informe pide tocar
|
|
67
|
+
algún archivo del cargo —SKILL.md, sources.yaml, references/, un caso— y «no» cuando lo hallado
|
|
68
|
+
no cambia ningún contrato, aunque «Recomendación» explique largamente por qué. Un «no» se
|
|
69
|
+
mergea sin revisión humana: no hay nada que decidir. Ante la duda va «si», que sólo cuesta una
|
|
70
|
+
mirada.
|
|
71
|
+
|
|
60
72
|
Este comentario vive fuera de toda sección a propósito — dentro de «Recomendación» viajaría a cada
|
|
61
73
|
propuesta consolidada. -->
|
|
62
74
|
|
package/engine/cli/planning.js
CHANGED
|
@@ -266,6 +266,9 @@ function treeJson({ epics, milestones, done, wips, inbox, queued, claims }) {
|
|
|
266
266
|
|
|
267
267
|
function tree(dir, cli) {
|
|
268
268
|
const root = path.resolve(dir || '.')
|
|
269
|
+
// Mismo motivo que en `context`, y por eso comparten la comprobación: sin ella un planning ausente
|
|
270
|
+
// dibujaba un árbol vacío, que se lee como un roadmap sin épicas en vez de como una ruta equivocada.
|
|
271
|
+
assertPlanning(root)
|
|
269
272
|
const state = ST.snapshot(root)
|
|
270
273
|
if (cli.has('--json')) return treeJson(state)
|
|
271
274
|
const { epics, milestones, done, wips, inbox, queued, claims } = state
|
|
@@ -304,9 +307,29 @@ function tree(dir, cli) {
|
|
|
304
307
|
console.log(`${paint('1', 'DONE')} ${done.entries.length} tareas\n`)
|
|
305
308
|
}
|
|
306
309
|
|
|
310
|
+
// Lo que no se pudo leer no contesta como si se hubiera leído: la misma regla que `stagedFiles` aplica
|
|
311
|
+
// sobre el índice de git, y que esta familia ya aplicaba al `--hito` inexistente. Faltaba la raíz, y ahí
|
|
312
|
+
// pesa más, porque el consumidor no siempre es una persona: `autobuild` toma la cola vacía como permiso
|
|
313
|
+
// para expandir una épica, así que un error de ruta promovía trabajo en vez de fallar.
|
|
314
|
+
//
|
|
315
|
+
// La ruta va **resuelta** y no como se escribió, porque el error que ataca es de resolución: en sidecar
|
|
316
|
+
// `<empresa>-ops/planning` desde adentro de la raíz apunta a `<empresa>-ops/<empresa>-ops/planning`.
|
|
317
|
+
function assertPlanning(root) {
|
|
318
|
+
if (!fs.existsSync(root)) {
|
|
319
|
+
return fail(`no existe el planning en ${root} (ruta resuelta). Comprobá desde dónde estás invocando.`, 2)
|
|
320
|
+
}
|
|
321
|
+
// Existir no alcanza: un directorio cualquiera contestaría cola vacía igual. `BACKLOG.md` es el archivo
|
|
322
|
+
// del que sale la cola, así que sin él la respuesta no significa nada.
|
|
323
|
+
if (!fs.existsSync(path.join(root, 'BACKLOG.md'))) {
|
|
324
|
+
return fail(`${root} (ruta resuelta) no es un planning: falta BACKLOG.md.`, 2)
|
|
325
|
+
}
|
|
326
|
+
return null
|
|
327
|
+
}
|
|
328
|
+
|
|
307
329
|
// Contexto mínimo suficiente para ejecutar una tarea, en lugar de releer roadmap, BACKLOG y WIP enteros.
|
|
308
330
|
function context(dir, cli) {
|
|
309
331
|
const root = path.resolve(dir || '.')
|
|
332
|
+
assertPlanning(root)
|
|
310
333
|
const state = ST.snapshot(root)
|
|
311
334
|
// Acotar la cola a un hito es como un equipo se reparte trabajo sin coordinarse: dos personas en hitos
|
|
312
335
|
// distintos casi nunca dependen entre sí ni tocan los mismos archivos. Lo que se acota es qué se
|
|
@@ -372,6 +395,10 @@ function context(dir, cli) {
|
|
|
372
395
|
// vuelta. Que aparezca es la señal.
|
|
373
396
|
recurring: RC.status({ ...RC.read(root), done: state.done, today: TODAY() })
|
|
374
397
|
.filter((one) => one.overdue),
|
|
398
|
+
// La próxima sin promover: se nombra, no se encola, igual que la recurrencia de arriba y por su
|
|
399
|
+
// mismo criterio. Por qué no la encola una máquina está en la fase Pick de `autobuild`.
|
|
400
|
+
nextEpic: (!task && [...state.epics].filter((one) => one.status === 'open')
|
|
401
|
+
.sort((a, b) => String(a.num).localeCompare(String(b.num)))[0]) || null,
|
|
375
402
|
}
|
|
376
403
|
if (cli.has('--json')) return console.log(JSON.stringify(report))
|
|
377
404
|
|
|
@@ -403,6 +430,8 @@ function context(dir, cli) {
|
|
|
403
430
|
}
|
|
404
431
|
if (!report.task) {
|
|
405
432
|
console.log('TASK (sin tarea disponible)')
|
|
433
|
+
const { nextEpic: next } = report
|
|
434
|
+
if (next) console.log(`EPIC ${next.num}: ${next.title} — sin promover`)
|
|
406
435
|
// Mismo motivo que `blocked` arriba, con otra causa: acá la cola no la traba una persona, la tiene
|
|
407
436
|
// el equipo, y lo que corresponde es hablar con quien la tiene.
|
|
408
437
|
for (const one of report.taken) console.log(`TAKEN ${one.slug} (${dueño(one)})`)
|
package/package.json
CHANGED
|
@@ -81,6 +81,20 @@ El lane reduce ceremonia, nunca seguridad, aceptación ni evidencia: Verify y el
|
|
|
81
81
|
cuatro. Lo que decide el carril es la superficie del cambio y no su tamaño en líneas — un `if` en el
|
|
82
82
|
chequeo de permisos es `full`, y un componente entero de presentación puede ser `directo`.
|
|
83
83
|
|
|
84
|
+
Escribir la línea es también contrastarla. Declara cuatro cosas —qué hace, en qué carril, quién entrega y
|
|
85
|
+
revisa, con qué se comprueba— y las cuatro salen de la misma mano en el mismo acto, así que nada las cruza
|
|
86
|
+
después. Releerlas no encuentra el hueco: una aceptación incompleta se lee perfecta, porque todo lo que
|
|
87
|
+
dice es cierto.
|
|
88
|
+
|
|
89
|
+
Antes de dar la tarea por escrita se recorre su descripción frase por frase y se contesta, por cada cosa
|
|
90
|
+
que promete, cuál condición de aceptación la comprueba; se lee el carril contra la superficie que toca y
|
|
91
|
+
no contra su tamaño; y se comprueba que el cast entregue a quien construye. Lo que quede sin condición se
|
|
92
|
+
agrega o se declara fuera de alcance en la línea.
|
|
93
|
+
|
|
94
|
+
Es el único momento en que las cuatro se pueden mirar juntas, y por eso la pasada vive acá y no en una
|
|
95
|
+
fase: después el carril ya decide cuáles corren, y la que revisaría es una de las que ese carril puede
|
|
96
|
+
saltar. Una tarea mal marcada `express` es justamente la que se salta la fase donde alguien lo notaría.
|
|
97
|
+
|
|
84
98
|
## Invariantes
|
|
85
99
|
|
|
86
100
|
1. Una tarea tiene un dueño de estado: roadmap → BACKLOG → overlay WIP → DONE.
|