diff --git a/reference/core/control-center/evidence/gap-closure-evidence.json b/reference/core/control-center/evidence/gap-closure-evidence.json index 83c449f6..06c133a5 100644 --- a/reference/core/control-center/evidence/gap-closure-evidence.json +++ b/reference/core/control-center/evidence/gap-closure-evidence.json @@ -10635,6 +10635,23 @@ "BOTH HALVES: evolith_arch32#623 (144fce23) and evolith_tracker#153 (97e1bc8e).", "node .harness/scripts/ci/08-validate-tracking.mjs && node --test src/rulesets/standards/iso-5055-mapping.test.mjs" ] + }, + { + "id": "GT-709", + "closedAt": "2026-09-05", + "closureCommit": "eb458372", + "dependencyDisposition": "none", + "evidence": [ + "package.json", + "package-lock.json" + ], + "validationCommands": [ + "BASELINE MEASURED ON THE TREE WITHOUT THE CHANGE, with the same guard CI runs, rather than inherited from the CI log: `63-validate-npm-audit-gate.mjs` on `origin/main` (11562cce) reports 11 blocking rows / 7 high, naming fast-uri's four advisories (GHSA-jqff-g426-hqxp, GHSA-5jgf-p345-68v8, GHSA-f65p-4m7j-42xc, GHSA-fph4-wmhf-6fwf), the five ajv/commitlint chain rows it reports as `via:fast-uri`, and browserslist's two (GHSA-73wf-gq98-2v4g, GHSA-c83g-rgw3-j3cx).", + "THE CAUSE WAS A PIN OF OUR OWN, NOT A MISSING UPSTREAM FIX. `overrides.fast-uri` was `3.1.5` — exactly the last vulnerable release of the 3.x line. The advisory patches in `3.1.6`, inside the `^3.0.1` that `ajv@8.20.0` declares, so the fix fitted in the pin that already existed.", + "AFTER `fast-uri` 3.1.7: 2 blocking rows / 2 high, both browserslist — so 9 of the 11 rows are attributable to the single pin, measured and not inferred. AFTER `browserslist` 4.28.9: `0 undeclared high/critical advisories; 0 accepted with a recorded reason, 0 stale`.", + "NO EXCEPTION WAS DECLARED. `.harness/config/npm-audit-exceptions.json` still holds zero entries; that file is for advisories with no upstream fix, and both of these had one. The moderate `qs` advisory survives on purpose — the gate does not block below HIGH.", + "WHY THE GATE STOPPED NOTHING is registered as its own row rather than as prose here: `Security Audit` is not among the nine required contexts, and eight PRs merged into `main` while it was red. See GT-710." + ] } ] } diff --git a/reference/core/control-center/gaps/gap-reference-catalog.es.md b/reference/core/control-center/gaps/gap-reference-catalog.es.md index 8eebe3aa..eecd176e 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.es.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.es.md @@ -10050,3 +10050,66 @@ Los dos se arreglaron de forma estructural y no como correcciones: el rethrow no - [x] **FALSABILIDAD:** ningún enlace de ninguno de los dos repositorios resuelve a un fichero KDD borrado, comprobado tras el barrido y no supuesto desde la lista de borrados. **CUMPLIDO para el Core** — buscar los ocho nombres borrados en todos los markdown no devuelve nada fuera del `ADR-0127` y del aviso de corrección del documento de rediseño, que los nombran como retirados en vez de enlazarlos. - **Estado:** `COMPLETADO` +#### GT-709 + +**Título:** Un `overrides` puesto para cerrar un advisory se vuelve el techo que impide cerrarlo la vez siguiente + +- **Propósito:** Que el bloque de `overrides` deje de ser el sitio donde una CVE se queda a vivir, y que `Security Audit` vuelva a significar algo. +- **Evidencia, medida el 2026-09-05 con `63-validate-npm-audit-gate.mjs` —el guard que corre CI— sobre el mismo árbol:** + + | árbol | filas bloqueantes | altas | + |---|---:|---:| + | `origin/main` (`11562cce`) | 11 | 7 | + | + `fast-uri` `3.1.7` | 2 | 2 | + | + `browserslist` `4.28.9` | **0** | **0** | + +- **La causa no era una dependencia sin arreglo, sino dos pins propios.** `overrides.fast-uri` estaba fijado en `3.1.5` — exactamente la última versión vulnerable de la rama 3.x. `GHSA-jqff-g426-hqxp` parchea en `3.1.6`, dentro del `^3.0.1` que declara `ajv@8.20.0`, así que el arreglo cabía en el pin que ya existía. Las nueve filas que se van con ese cambio son las cuatro advisories de `fast-uri` (`GHSA-jqff-g426-hqxp`, `GHSA-5jgf-p345-68v8`, `GHSA-f65p-4m7j-42xc`, `GHSA-fph4-wmhf-6fwf`) más las cinco filas de la cadena `ajv` / `@commitlint/config-validator` / `@commitlint/resolve-extends` / `@commitlint/load` / `@commitlint/cli`, que el gate reportaba `via:fast-uri`. +- **Las dos restantes eran el mismo patrón otra vez.** `browserslist` `4.28.4`, transitivo solo-dev por `ts-jest`→`@babel/core`→`@babel/helper-compilation-targets` y por `@nestjs/cli`→`webpack`, con `GHSA-73wf-gq98-2v4g` y `GHSA-c83g-rgw3-j3cx` parcheados en `4.28.7`. Se pinea `4.28.9` con la misma forma de override que el comentario del propio `sdk-cli-ci.yml` prescribe para este caso. +- **Lo que NO se hizo, y por qué:** no se declaró ninguna excepción en `.harness/config/npm-audit-exceptions.json`. Ese fichero es para advisories sin arreglo upstream, y aquí había arreglo en las dos; declararlas habría convertido un pin caduco en una excepción permanente. +- **Casos de uso:** + - Un revisor ve `Security Audit` en rojo, comprueba que la advisory es de un transitivo y la descarta como ajena, sin mirar que el techo lo pone un `overrides` propio. + - Alguien añade un `overrides` para cerrar una CVE y con ello fija la versión que impedirá cerrar la siguiente de ese mismo paquete. +- **Impacto:** Una CVE ALTA viva en `main` durante tres días, con el gate que la detecta en rojo y ocho merges pasando por encima. +- **Resultado esperado:** `63-validate-npm-audit-gate.mjs` en verde sobre `main` sin excepciones declaradas. +- **Ficheros afectados:** `package.json`, `package-lock.json` +- **Componente:** `Infra` · **Criticidad:** P2 · **Complejidad:** S +- **Principal:** `XS` · **Interest:** `HIGH` · **Basis:** `estimate` +- **Procedencia:** Registrado el 2026-09-05, encontrado barriendo los pull requests de dependabot: los cinco de npm salían con `Security Audit` en rojo, y contrastarlo contra `main` mostró que el rojo era anterior a los cinco. +- **Criterios de aceptación:** + - [x] El gate pasa de 11 filas bloqueantes a 0, medido con el guard real sobre el árbol y no deducido del advisory. **CUMPLIDO** — `0 undeclared high/critical advisories; 0 accepted with a recorded reason, 0 stale`. + - [x] El arreglo es un cambio de versión, no una excepción declarada. **CUMPLIDO** — `npm-audit-exceptions.json` sigue con cero entradas. + - [x] La versión elegida respeta el rango que declara el consumidor, en vez de forzarlo. **CUMPLIDO** — `3.1.7` cae dentro del `^3.0.1` de `ajv@8.20.0`. + - [x] **FALSABILIDAD:** el baseline se midió sobre el árbol SIN el cambio y con el mismo guard, no se heredó del log de CI. **CUMPLIDO** — 11 filas / 7 altas sobre `origin/main`, con las cuatro filas de `fast-uri` y las dos de `browserslist` nombradas una a una antes de tocar nada. +- **Estado:** `COMPLETADO` + +#### GT-710 + +**Título:** El gate que mide las CVE no es un check requerido, así que ocho merges pasaron por encima de él en rojo + +- **Propósito:** Que un advisory ALTA sin declarar bloquee el merge en lugar de limitarse a informarlo. +- **Evidencia, medida el 2026-09-05:** + + | dato | valor | + |---|---| + | contextos requeridos en `main` y `develop` | 9 | + | ¿incluyen `Security Audit`? | **no** (tampoco `Trivy` ni `build-and-test`) | + | `Security Audit` en rojo desde | `b84523b4`, 2026-09-02 | + | merges a `main` en ese intervalo | **8**, cuatro de ellos de dependencias npm | + | cómo lo presenta GitHub | `UNSTABLE`, no `BLOCKED` | + +- **Los nueve requeridos son** `CodeQL SAST`, `Secret Detection (gitleaks)`, `Services build (GHCR)`, `Test`, `Test core`, `Test core-api`, `Test core-domain`, `Test mcp-server` y `Validate documentation`. +- **El workflow nombra el modo de fallo y luego lo construye.** El comentario de `sdk-cli-ci.yml` advierte de que un check permanentemente rojo enseña a los revisores a descontar el rojo — y el check queda fuera del conjunto requerido, que es la vía más directa a ese resultado. Los cuatro PR de dependencias npm mergeados en el intervalo ([#664](https://github.com/beyondnetcode/evolith_arch32/pull/664), [#665](https://github.com/beyondnetcode/evolith_arch32/pull/665), [#666](https://github.com/beyondnetcode/evolith_arch32/pull/666), [#667](https://github.com/beyondnetcode/evolith_arch32/pull/667)) son exactamente la clase de cambio que el gate existe para juzgar. +- **Casos de uso:** + - Un bump de dependencias introduce una CVE ALTA y se mergea igual, porque el único check que la ve no es requerido. + - Un revisor aprende que `Security Audit` rojo es normal y deja de leerlo. +- **Impacto:** El gate de seguridad de dependencias es informativo y no impide ningún merge, incluidos los de dependencias. +- **Resultado esperado:** `Security Audit` entre los contextos requeridos de `main` y `develop`, con un PR que lleve una alta sin declarar quedando `BLOCKED`. +- **Ficheros afectados:** protección de rama de `main` y `develop`, `.github/workflows/sdk-cli-ci.yml` +- **Componente:** `Infra` · **Criticidad:** P1 · **Complejidad:** S +- **Principal:** `XS` · **Interest:** `HIGH` · **Basis:** `estimate` +- **Procedencia:** Registrado el 2026-09-05 al cerrar [`GT-709`](#gt-709): explicar por qué una CVE ALTA había sobrevivido tres días exigía mirar qué checks bloquean de verdad, y `Security Audit` no estaba entre ellos. +- **Criterios de aceptación:** + - [ ] `Security Audit` figura en los contextos requeridos de `main` y de `develop`. + - [ ] **FALSABILIDAD:** un PR con un advisory ALTA sin declarar sale `BLOCKED` y no `UNSTABLE`, observado y no supuesto. + - [ ] La decisión sobre `Trivy` y `build-and-test` queda escrita — requeridos también, o registrado por qué no lo son. +- **Estado:** `PENDIENTE` diff --git a/reference/core/control-center/gaps/gap-reference-catalog.md b/reference/core/control-center/gaps/gap-reference-catalog.md index d554cebc..e8283a20 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.md @@ -10143,3 +10143,66 @@ Both were fixed structurally rather than corrected: the rethrow now names BOTH f - [x] **FALSIFIABILITY:** no link in either repository resolves to a deleted KDD file, checked after the sweep rather than assumed from the delete list. **MET for the Core** — searching the eight deleted filenames across every markdown file returns nothing outside `ADR-0127` and the redesign doc's correction notice, both of which name them as retired rather than link to them. - **Status:** `DONE` +#### GT-709 + +**Title:** An `overrides` pin added to close an advisory becomes the ceiling that prevents closing it the next time + +- **Purpose:** Stop the `overrides` block being the place a CVE settles in, and make `Security Audit` mean something again. +- **Evidence, measured 2026-09-05 with `63-validate-npm-audit-gate.mjs` — the guard CI runs — against the same tree:** + + | tree | blocking rows | high | + |---|---:|---:| + | `origin/main` (`11562cce`) | 11 | 7 | + | + `fast-uri` `3.1.7` | 2 | 2 | + | + `browserslist` `4.28.9` | **0** | **0** | + +- **The cause was not a dependency without a fix, but two pins of our own.** `overrides.fast-uri` was set to `3.1.5` — exactly the last vulnerable release of the 3.x line. `GHSA-jqff-g426-hqxp` patches in `3.1.6`, inside the `^3.0.1` that `ajv@8.20.0` declares, so the fix fitted in the pin that was already there. The nine rows that go with that change are `fast-uri`'s own four advisories (`GHSA-jqff-g426-hqxp`, `GHSA-5jgf-p345-68v8`, `GHSA-f65p-4m7j-42xc`, `GHSA-fph4-wmhf-6fwf`) plus the five rows of the `ajv` / `@commitlint/config-validator` / `@commitlint/resolve-extends` / `@commitlint/load` / `@commitlint/cli` chain, which the gate reported as `via:fast-uri`. +- **The remaining two were the same pattern again.** `browserslist` `4.28.4`, a dev-only transitive through `ts-jest`→`@babel/core`→`@babel/helper-compilation-targets` and `@nestjs/cli`→`webpack`, with `GHSA-73wf-gq98-2v4g` and `GHSA-c83g-rgw3-j3cx` patched in `4.28.7`. Pinned to `4.28.9` with the same override shape `sdk-cli-ci.yml`'s own comment prescribes for this case. +- **What was NOT done, and why:** no exception was declared in `.harness/config/npm-audit-exceptions.json`. That file is for advisories with no upstream fix, and both of these had one; declaring them would have turned a stale pin into a permanent exception. +- **Use cases:** + - A reviewer sees `Security Audit` red, checks that the advisory is transitive and dismisses it as somebody else's, without noticing that the ceiling is set by an `overrides` entry of our own. + - Someone adds an `overrides` entry to close a CVE and thereby pins the version that will prevent closing the next one in that same package. +- **Impact:** A HIGH CVE live on `main` for three days, with the gate that detects it red and eight merges going past it. +- **Expected outcome:** `63-validate-npm-audit-gate.mjs` green on `main` with no declared exceptions. +- **Files affected:** `package.json`, `package-lock.json` +- **Component:** `Infra` · **Criticality:** P2 · **Complexity:** S +- **Principal:** `XS` · **Interest:** `HIGH` · **Basis:** `estimate` +- **Provenance:** Registered 2026-09-05, found while sweeping the dependabot pull requests: all five npm ones came out with `Security Audit` red, and contrasting that against `main` showed the red predated all five. +- **Acceptance criteria:** + - [x] The gate goes from 11 blocking rows to 0, measured with the real guard against the tree rather than deduced from the advisory. **MET** — `0 undeclared high/critical advisories; 0 accepted with a recorded reason, 0 stale`. + - [x] The fix is a version change, not a declared exception. **MET** — `npm-audit-exceptions.json` still holds zero entries. + - [x] The chosen version respects the range the consumer declares rather than forcing it. **MET** — `3.1.7` falls inside `ajv@8.20.0`'s `^3.0.1`. + - [x] **FALSIFIABILITY:** the baseline was measured on the tree WITHOUT the change and with the same guard, not inherited from the CI log. **MET** — 11 rows / 7 high on `origin/main`, with `fast-uri`'s four rows and `browserslist`'s two named one by one before anything was touched. +- **Status:** `DONE` + +#### GT-710 + +**Title:** The gate that measures CVEs is not a required check, so eight merges went past it while it was red + +- **Purpose:** Make an undeclared HIGH advisory block the merge instead of merely reporting it. +- **Evidence, measured 2026-09-05:** + + | fact | value | + |---|---| + | required contexts on `main` and `develop` | 9 | + | do they include `Security Audit`? | **no** (nor `Trivy` nor `build-and-test`) | + | `Security Audit` red since | `b84523b4`, 2026-09-02 | + | merges into `main` in that window | **8**, four of them npm dependency changes | + | how GitHub renders them | `UNSTABLE`, not `BLOCKED` | + +- **The nine required are** `CodeQL SAST`, `Secret Detection (gitleaks)`, `Services build (GHCR)`, `Test`, `Test core`, `Test core-api`, `Test core-domain`, `Test mcp-server` and `Validate documentation`. +- **The workflow names the failure mode and then builds it.** The comment in `sdk-cli-ci.yml` warns that a permanently red check trains reviewers to discount red — and the check is left out of the required set, which is the most direct route to that outcome. The four npm dependency PRs merged in the window ([#664](https://github.com/beyondnetcode/evolith_arch32/pull/664), [#665](https://github.com/beyondnetcode/evolith_arch32/pull/665), [#666](https://github.com/beyondnetcode/evolith_arch32/pull/666), [#667](https://github.com/beyondnetcode/evolith_arch32/pull/667)) are exactly the class of change that gate exists to judge. +- **Use cases:** + - A dependency bump introduces a HIGH CVE and merges anyway, because the only check that sees it is not required. + - A reviewer learns that a red `Security Audit` is normal and stops reading it. +- **Impact:** The dependency security gate is advisory and blocks no merge, including dependency merges. +- **Expected outcome:** `Security Audit` among the required contexts on `main` and `develop`, with a PR carrying an undeclared high coming out `BLOCKED`. +- **Files affected:** branch protection on `main` and `develop`, `.github/workflows/sdk-cli-ci.yml` +- **Component:** `Infra` · **Criticality:** P1 · **Complexity:** S +- **Principal:** `XS` · **Interest:** `HIGH` · **Basis:** `estimate` +- **Provenance:** Registered 2026-09-05 while closing [`GT-709`](#gt-709): explaining why a HIGH CVE had survived three days required looking at which checks actually block, and `Security Audit` was not among them. +- **Acceptance criteria:** + - [ ] `Security Audit` appears in the required contexts of both `main` and `develop`. + - [ ] **FALSIFIABILITY:** a PR carrying an undeclared HIGH advisory comes out `BLOCKED` rather than `UNSTABLE`, observed and not assumed. + - [ ] The decision on `Trivy` and `build-and-test` is written down — required too, or a recorded reason why not. +- **Status:** `PENDING` diff --git a/reference/core/control-center/gaps/gap-tracking.es.md b/reference/core/control-center/gaps/gap-tracking.es.md index 654a1927..3aeda0be 100644 --- a/reference/core/control-center/gaps/gap-tracking.es.md +++ b/reference/core/control-center/gaps/gap-tracking.es.md @@ -4,6 +4,7 @@ **Estado:** Seguimiento Activo **Responsable:** Evolith Architecture Board +**Última Actualización:** 2026-09-05 (**Dos filas nuevas de un mismo hilo: una CVE ALTA que llevaba tres días viva en `main` y la razón por la que no detuvo nada.** `GT-709` → COMPLETADO: `Security Audit` estaba rojo desde `b84523b4` no por una dependencia sin arreglo, sino porque `overrides.fast-uri` estaba fijado en `3.1.5`, **exactamente la última versión vulnerable de la rama 3.x**, con el parche en `3.1.6` y dentro del rango que `ajv` declara. Medido con el guard real, `63-validate-npm-audit-gate.mjs`: de **11 filas bloqueantes / 7 altas a 0 y 0**, sin declarar ninguna excepción. `GT-710` → PENDIENTE: ese gate **no es un check requerido**, y en el intervalo en que estuvo rojo se mergearon **ocho** PR a `main`, cuatro de ellos de dependencias npm. **Lo que merece llevarse:** el mecanismo que se usa para cerrar un advisory —el `overrides`— es el mismo que después impide cerrarlo, y el único check que lo ve no bloquea nada.) **Última Actualización:** 2026-08-18 (**Un gap cerrado por el disparador que él mismo había escrito — que se activó dos días después de escribirlo, y nombraba la release equivocada.** `GT-691` → COMPLETADO. La CVE ALTA de `js-yaml` que bloqueaba toda promoción a `main` salió del árbol por una actualización, no por un descarte: `@nestjs/swagger@11.4.7` —un PARCHE sobre la línea `11.4.x` que la fila había dado por agotada, no la 12 estable que decía esperar— declara `"js-yaml": "5.3.0"`, y `package-lock.json` resuelve ahora `node_modules/@nestjs/swagger/node_modules/js-yaml` hacia ella. `npm audit` pasa de 1 alta a 0 altas / 0 críticas; el criterio de falsabilidad de la fila se volvió a medir contra el árbol nuevo en vez de heredarlo, y ninguno de los dos falsadores se disparó. **Lo que merece llevarse es el elemento a vigilar, no la CVE:** la fila identificó bien que había que vigilar `@nestjs/swagger` y no `js-yaml`, y luego ató esa vigilancia a un major que no había salido.) **Última Actualización:** 2026-08-08 (**Un gap cerrado haciendo lo irreversible que el board había diferido a propósito — y lo que vale registrar es la medición que se tomó antes.** `GT-622` → COMPLETADO, y lo que lo cerró es la mitad irreversible que el board había diferido: los 210 análisis huérfanos de `.github/workflows/ci.yml:codeql` están borrados de `refs/heads/main` (201) y `refs/heads/develop` (9), la clave muerta no aparece en ninguna de las dos, y el corpus de alerts es idéntico a través del borrado — 242 abiertos / 82 descartados / 60 corregidos, CodeQL 75 / Scorecard 158 / Trivy 9. **Tres afirmaciones de esa misma fila no sobrevivieron a la re-medición, y la fila ya se había re-medido dos veces sin que ninguna saliera, porque cada pasada comprobó el RECUENTO y ninguna comprobó la AFIRMACIÓN:** el check era `neutral`, no rojo, desde el PR #250; la configuración que nombra el aviso está en `refs/heads/develop`, no en `main`, así que solo los PR contra `develop` seguían arrastrándolo mientras `main` salía limpio por su cuenta desde el PR #420 con los 201 huérfanos todavía puestos; y los 9 análisis de `develop` —los que importaban— nunca se contaron. **El campo que convirtió un juicio del dueño en una decisión fácil no se había leído nunca:** los 210 análisis tienen `results_count: 0`, así que lo descartado son 210 registros de “escaneé y no encontré nada” de una configuración muerta desde junio. La irreversibilidad era real; la pérdida no. Quedan a propósito 22 análisis de la clave muerta en `refs/pull/{4..17}/merge`, refs efímeras por PR que jamás pueden ser base de un PR. **Observado también al medir, y fuera de este cierre:** el conjunto de contextos requeridos en `main` y `develop` es ahora de **8**, tras ganar `Secret Detection (gitleaks)` — la promoción que `GT-653` registraba como su único pendiente. El criterio de cierre que solo podía observarse en un PR contra `develop` se observó en el PR #440, el que trae este mismo cierre, 105s después de abrirlo — escrito tras leer el check, no antes. Contadores recalculados desde las filas: **640 / 653 completados · 3 en progreso · 3 pendientes · 7 diferidos**.) **Última Actualización (ola):** 2026-08-14 · segunda pasada (**Trece gaps más registrados desde una ola de benchmarking de 30 agentes, cuatro filas `DONE` corregidas, y una de las filas de esta misma mañana acotada.** La ola produjo 40 candidatos desde tres lentes, 16 pasaron por verificación adversarial con REFUTADO por defecto, **15 sobrevivieron y 1 murió formalmente** — «una regla de tenant nunca puede ejecutarse» es FALSO: una regla con id nuevo se EJECUTÓ hasta un veredicto `failed` real por dos caminos cableados en producción, lo que acota [`GT-669`](./gap-reference-catalog.es.md#gt-669) a extensibilidad de ejecutores y documentación. **Las cuatro mediciones portantes se repitieron a mano antes de escribir nada**, y la más aguda se reprodujo al primer intento: el mismo pack devuelve **exit 0 `passed`** con `--engine opa` y **exit 2 `failed` con 2 issues bloqueantes** con `--engine native` ([`GT-675`](./gap-reference-catalog.es.md#gt-675)). **Cuatro filas `DONE` llevan criterios que el código no satisface** — `GT-321` (ledger con cero instanciaciones), `GT-266` (aprovisionamiento con cero llamantes), `GT-221` (loggers de recursos y prompts sin llamantes de producción), `GT-158` (nada emite un token de aprobación) — cada una anotada in situ con su medición y re-registrada como fila viva en vez de reabierta, porque un registro de cierre sobre una fila no-DONE es error duro en `08-validate-tracking`. `GT-518` queda anotada igual por [`GT-677`](./gap-reference-catalog.es.md#gt-677). **Diez de las trece filas son MÁS ESTRECHAS de lo propuesto**, y cada una registra la subafirmación que su verificación mató. Ids nuevos `GT-675`…`GT-687`. Contadores recalculados desde las filas: **654 / 685 completados · 2 en progreso · 19 pendientes · 10 diferidos**.) @@ -20,6 +21,8 @@ Este tablero es la única fuente de verdad para deuda técnica, gaps, oportunida | ID | Gap | En simple | Qué resuelve | Componente | Fase | Criticidad | Complejidad | Estado | |---|---|---|---|:---:|:---:|:---:|:---:|:---:| +| [`GT-710`](./gap-reference-catalog.es.md#gt-710) | **El gate que mide las CVE no es un check requerido, así que ocho merges pasaron por encima de él estando rojo.** Los nueve contextos requeridos de `main` y `develop` son `CodeQL SAST`, `Secret Detection (gitleaks)`, `Services build (GHCR)`, `Test`, `Test core`, `Test core-api`, `Test core-domain`, `Test mcp-server` y `Validate documentation`. **`Security Audit` no está entre ellos**, y tampoco lo están `Trivy` ni `build-and-test`. Medido el 2026-09-05: entre `b84523b4` (02-sep), el commit donde `Security Audit` se puso rojo, y su arreglo en [`GT-709`](./gap-reference-catalog.es.md#gt-709), se mergearon a `main` **ocho pull requests con el gate en rojo** — y cuatro de ellos ([#664](https://github.com/beyondnetcode/evolith_arch32/pull/664), [#665](https://github.com/beyondnetcode/evolith_arch32/pull/665), [#666](https://github.com/beyondnetcode/evolith_arch32/pull/666), [#667](https://github.com/beyondnetcode/evolith_arch32/pull/667)) eran cambios de dependencias npm, exactamente la clase de cambio que ese gate existe para juzgar. GitHub los presenta como `UNSTABLE` y no como `BLOCKED`, así que el flujo normal de revisión los mergea sin fricción. **El propio workflow nombra este modo de fallo por escrito:** el comentario de `sdk-cli-ci.yml` dice que un check siempre rojo enseña a los revisores a descontar el rojo, y luego deja el check fuera de los requeridos, que es la manera más directa de garantizar que eso ocurra. Se registra aparte de [`GT-709`](./gap-reference-catalog.es.md#gt-709) a propósito: aquella era una CVE con arreglo de dos líneas, esta es la razón por la que la CVE pudo vivir tres días sin detener nada. | Tenemos un chequeo de seguridad que mide bien y no impide nada; ocho cambios entraron con él en rojo. | Que un advisory ALTA sin declarar bloquee el merge en lugar de limitarse a informarlo. | `Infra` | Cross | P1 | S | `PENDIENTE` | +| [`GT-709`](./gap-reference-catalog.es.md#gt-709) | **Un `overrides` puesto para cerrar un advisory se convierte en el techo que impide cerrarlo la vez siguiente.** `Security Audit` llevaba rojo en `main` desde `b84523b4` (2026-09-02) por `GHSA-jqff-g426-hqxp`, una CVE ALTA en `fast-uri` — y la causa no era una dependencia sin arreglo publicado, sino **dos pins propios que se quedaron por debajo de la versión parcheada**, que el gate reporta con la misma forma que una advisory ajena. `overrides.fast-uri` estaba fijado en `3.1.5`, **exactamente la última versión vulnerable de la rama 3.x**; el parche es `3.1.6`, dentro del `^3.0.1` que declara `ajv@8.20.0`, así que el arreglo cabía en el pin que ya existía y nadie lo miró porque parecía configuración resuelta. **Medido con `63-validate-npm-audit-gate.mjs`, el mismo guard que corre CI, y no inferido de changelogs:** `origin/main` daba **11 filas bloqueantes / 7 altas**; con `fast-uri` a `3.1.7` caen 9 de las 11 — las cuatro advisories suyas más las cinco filas de la cadena `ajv`/`commitlint` que llegaban *via* `fast-uri`. Las dos restantes eran `browserslist` `4.28.4`, transitivo solo-dev (`ts-jest`→`@babel/core`, `@nestjs/cli`→`webpack`) con arreglo en `4.28.7`: mismo patrón, mismo tipo de override, pineado a `4.28.9`. **Resultado: 0 filas bloqueantes, 0 altas.** La moderada de `qs` sobrevive a propósito — el gate no bloquea por debajo de HIGH. **CERRADA el 2026-09-05** por [#689](https://github.com/beyondnetcode/evolith_arch32/pull/689), merge `eb458372` en `main`. **Lo que merece llevarse no es la CVE sino el modo de fallo:** el mecanismo que se usa para cerrar un advisory es el mismo que después lo mantiene abierto, y no hay nada que vigile los pins. Mismo patrón que [`GT-691`](./gap-reference-catalog.es.md#gt-691), donde la vigilancia quedó atada a un major que no había salido; allí el elemento mal vigilado fue `@nestjs/swagger`, aquí es el propio bloque `overrides`. El defecto de que este gate no bloquee ningún merge queda registrado aparte, en [`GT-710`](./gap-reference-catalog.es.md#gt-710). | El chequeo de vulnerabilidades llevaba tres días en rojo por dos versiones que nosotros mismos habíamos fijado una por debajo del arreglo. | Que el bloque de `overrides` deje de ser el sitio donde una CVE se queda a vivir, y que el chequeo de seguridad vuelva a significar algo. | `Infra` | Cross | P2 | S | `COMPLETADO` | | [`GT-708`](./gap-reference-catalog.es.md#gt-708) | **Un concepto de gobierno que existía solo en prosa, en dos repositorios, y que una compuerta real citaba como precondición.** «KDD» nombraba dos cosas distintas y ninguna llegó a construirse: la **Fase 1.1 — Knowledge-First Discovery**, subfase opcional con su propia compuerta de preparación y siete plantillas de artefacto; y **KDD — Knowledge-Driven Development**, lectura posterior de la sesión con el dueño del 2026-07-04 (`L-009`, `D-004`) que lo convertía en sección opcional *dentro del PRD*, activable por tenant. **Medido en todas las superficies ejecutables, y ausente en todas:** `phase-gates.rules.json` tiene cinco gates para las fases 1..5; ninguno de los siete artefactos KDD está entre los 33 de `artifact-registry.json`; cero ficheros TypeScript con `KDD`/`knowledge-first`/`knowledgeBrief`/`discoveryReadiness`/`storySeed`/`epicCandidate`; el CLI tiene 31 comandos y cero menciones, y su `--phase discovery` mapea a la **fase 1 entera** (`phase-id.ts`: `f1: 'discovery'`), no a la 1.1; el servidor MCP, cero; el Tracker no tiene ni pantalla ni entidad; y `prd.schema.json` no lleva sección KDD, así que `D-004` tampoco llegó nunca a schema. **Aun así la prosa tenía dientes:** `phase-1-business-signoff.es.md` convertía *«el nivel de adopción de la Fase 1.1 ha sido declarado»* en **precondición para abrir el Gate 1**, y tres filas de su tabla de evidencia llevaban cláusulas condicionadas a niveles de KDD — una compuerta que nadie implementa bloqueando una que implementa todo el mundo. **CERRADA el 2026-08-18 por eliminación, por decisión del dueño de que Evolith Core y Tracker dejan de manejar el concepto en cualquier forma.** 16 ficheros borrados (el playbook de la Fase 1.1 y las siete plantillas, EN y ES); eliminadas la precondición del Gate 1 y sus tres cláusulas de evidencia condicionadas a KDD; eliminadas la tabla `Subfase 01.1`, la fila del índice de playbooks y las referencias a Story Seeds / Epic Candidates en el playbook de Fase 2 y en el índice de plantillas; `D-004`/`L-009` reescritas a lo que las sobrevive — el PRD es el piso canónico y el Gate 1 lo exige siempre. **La retirada es el [`ADR-0127`](../../architecture/adrs/core/0127-retire-knowledge-first-discovery.es.md), y el `ADR-0103` queda ENMENDADO por él en vez de editado:** una decisión aceptada del Architecture Board se supersede, no se reescribe, así que su razonamiento se mantiene y lo único que desapareció es su vecino. `CHANGELOG.md` y el `ADR-0103` conservan su texto sobre KDD a propósito — registran lo que era cierto cuando se escribieron, y editarlos falsificaría la historia que este repositorio guarda deliberadamente. **Falsabilidad, comprobada tras el barrido y no inferida de la lista de borrados:** toda referencia a los ocho ficheros borrados no devuelve nada fuera del ADR y del aviso de corrección, y `KDD`/`knowledge-first` solo sobreviven en los seis ficheros citados. **Lo que deja esta fila es la lección, no el barrido:** un concepto puede ser citado como precondición dura por una compuerta que todo el mundo implementa mientras no lo implementa nadie, y seguir así meses, porque nadie contrasta la prosa contra los datos. **CERRADA el 2026-08-18 — aterrizaron las dos mitades.** La del Tracker es `evolith_tracker#153` (`97e1bc8e`): `REQ-DIS-12` y `REQ-DIS-13` eliminados junto con la viñeta de gobierno de la subfase 01.1, la sección del catálogo de artefactos, la viñeta del blueprint, las cláusulas del índice de Discovery y los bloques de `.bmad-core`. **Los dos repositorios tenían la misma forma de dientes con distintas palabras:** el Core convertía *«el nivel de adopción de la Fase 1.1 ha sido declarado»* en precondición para abrir el Gate 1, y el Tracker daba al `REQ-DIS-13` el criterio de aceptación *«un resultado FAIL bloquea la apertura de la compuerta de Business Sign-Off»* — dos documentos, una compuerta inexistente, ambos bloqueando la única compuerta por la que pasa toda iniciativa. Lo que sobrevive es deliberado: `CHANGELOG.md` y el `ADR-0103` en el Core, y las dos filas del board de auditoría del Tracker, todos ellos registros de lo que era cierto cuando se escribieron. | Un concepto que describimos por todas partes y no construimos en ninguna, del que depende una de nuestras compuertas reales. | Que el modelo de cinco fases se lea igual en los documentos que en los datos, y que el Gate 1 deje de depender de una subfase que nadie puede ejecutar. | `Governance` | Cross | P2 | M | `COMPLETADO` | | [`GT-707`](./gap-reference-catalog.es.md#gt-707) | **Todo binario autónomo que publica este repositorio falla en `--help`, y ninguna release ha llevado nunca uno.** Medido el 2026-08-18 en los cuatro pull requests abiertos y, antes de ellos, en el push del tag `v1.3.6` y en el pull request que hizo por primera vez que el release pipeline corriera en pull requests: `smoke-test` y `smoke-test-functional` fallan con `ERR_REQUIRE_ESM: require() of ES Module /snapshot/…/@clack/prompts/dist/index.mjs`, lanzado desde `prompt.service.js` — así que el binario muere antes de parsear un argumento, en las tres plataformas. `gh release view` sobre `v1.3.0` y `v1.1.0` devuelve **cero assets**: `upload-assets` depende de `smoke-test`, así que el canal nunca ha entregado nada, y la propia puerta del pipeline es lo que lo detuvo. **Medido además, para que el próximo intento arranque aquí:** `@clack/prompts@1.5.1` es la ÚNICA dependencia solo-ESM del CLI (`chalk` 4.1.2, `ora` 5.4.1, `inquirer` 8.2.7 y `cli-table3` son todas CommonJS); `esbuild` la empaqueta en un CJS de 107 kB que carga limpio; y empaquetar con el fork mantenido `@yao-pkg/pkg@6` ELIMINA el `ERR_REQUIRE_ESM` y falla distinto — `MODULE_NOT_FOUND` por el mismo `.mjs`, porque el fichero no está en el snapshot — lo que significa que el empaquetador y el conjunto de assets son dos defectos distintos, no uno. **El arreglo que parece barato no lo es:** redirigir el import toca 6 ficheros de producción y ~24 specs que hacen `jest.mock('@clack/prompts')` con ese especificador exacto. **Deliberadamente NO arreglado dentro de los cuatro pull requests que lo encontraron:** están verdes en los 8 checks requeridos y esto es un fallo previo en un workflow no requerido; meter un rediseño de empaquetado ahí sería el cambio-ajeno-dentro-de-una-promoción que este tablero no deja de rechazar. **CERRADA el 2026-08-18 vendorizando a CommonJS las dependencias solo-ESM — y la primera evidencia de esta misma fila estaba mal dos veces, que es la parte que merece conservarse.** El binario empaquetado ya arranca: construido desde este árbol, `--help` sale **0**, `--version` imprime `1.3.2`, e `init --runtime nodejs --monorepo none --arch clean` sale **0** y escribe un satélite. **MAL #1 — «la única dependencia solo-ESM».** Esta fila lo midió sondeando `require('/package.json')`, que ocho de las 25 dependencias directas rechazan con `ERR_PACKAGE_PATH_NOT_EXPORTED` — un error que se lee como «bien». Leyendo los manifiestos DESDE DISCO aparecen **tres**: `@clack/prompts@1.5.1`, `conf@15.1.0` y `@modelcontextprotocol/sdk@1.29.0`. Dos se cargan en runtime y ambas quedan vendorizadas; el sdk de MCP es solo un `.d.ts` en este paquete, así que nunca entra en el snapshot. `pkg` llevaba avisando de `conf` todo el tiempo —muere nombrando `conf/package.json` y `config.service.js`— y nadie leyó más allá del primer error. **MAL #2 — el fallback apuntaba a nada.** `clack.ts` compila a `dist/infrastructure/prompts/` mientras el bundle se escribe en `dist/vendor/`, así que `require('./vendor/clack.cjs')` resolvía a una ruta inexistente. Nada falló en build; falló el BINARIO en ejecución, con el error del propio paquete, porque el `MODULE_NOT_FOUND` del fallback quedaba tragado por un rethrow del original. Ambos son ahora arreglos estructurales y no correcciones: el rethrow nombra LOS DOS fallos, y `vendor-esm-deps.mjs` lee la ruta relativa del shim COMPILADO y la resuelve — observado en rojo contra la ruta rota, con la ubicación resuelta en el mensaje. **Lo que se entrega:** `scripts/vendor-esm-deps.mjs` empaqueta cada dependencia solo-ESM con esbuild (clack 107 kB, conf 410 kB) y luego la carga de vuelta en un proceso hijo con `--no-experimental-require-module` —lo más cerca que un proceso Node normal está del contrato sin-ESM del snapshot— y compara su superficie de exports con la del paquete real. Dos shims (`prompts/clack.ts`, `config/conf-module.ts`) prueban PRIMERO EL PAQUETE y caen al bundle ante CUALQUIER fallo de carga: el orden es lo que mantiene las ~24 specs que hacen `jest.mock('@clack/prompts')` interceptando el mismo especificador de siempre, y «cualquier fallo» es porque la misma causa aflora como `ERR_REQUIRE_ESM` con un empaquetador y como `MODULE_NOT_FOUND` con otro. 106 suites / 1485 tests en verde, `tsc -b` limpio. El release pipeline no cambia: el arreglo es agnóstico del empaquetador, y su `smoke-test` es la falsabilidad que esta fila pedía. | El programa descargable que publicamos se cae al instante y, de hecho, nunca hemos publicado ninguno. | O un binario autónomo que arranca, o una retirada honesta del canal — no un check rojo que todo el mundo aprende a ignorar. | `Infra` | Cross | P2 | M | `COMPLETADO` | | [`GT-688`](./gap-reference-catalog.es.md#gt-688) | **Una composición de topologías confirmada se trunca a un solo id antes de la compuerta, así que un sistema mixto obtiene un veredicto verde por la única topología que sobrevivió.** Medido en vivo el 2026-08-14 contra el dist compilado: `manifestFromWorkspace` con `design.topologyConfirmedRefs: [modular-monolith, agentic-ai, event-driven]` devuelve `{"topology":"modular-monolith","facts":{"context":{"topologyRef":"modular-monolith"}}}` — **las otras dos no aparecen por ninguna parte**. Causa: `evaluation-context.builder.ts:26` `topology: ctx.topologyRef`, y `grep -n "design"` sobre ese fichero entero devuelve un hit, un comentario en `:149`. **Peor sin el escalar:** el manifiesto sale sin clave `topology`, el kind SALTA (`kind-evaluators.ts:363`), y el pipeline se reinventa una **con un regex sobre el YAML en disco**, ganando la primera coincidencia (`satellite-evaluation-pipeline.service.ts:354`). El contrato de resultado tampoco puede llevar dos (`TopologyEvaluationResult.topologyRef`, escalar obligatorio). Así que el kind devuelve `PASS, conformant: true` por el id que conservó — **un veredicto verde sobre un sistema del que la mitad nunca se comprobó**. **La observación del dueño que originó esta fila quedó REFUTADA a medias, y la mitad refutada importa:** el modelo NO es singular — `ADR-0079:44` rechaza por escrito el diseño excluyente, la transversalidad es declaración formal (`maturityLevel: "cross"` en las cinco no progresivas), y el corpus, el contrato de satélite y el evaluador de diseño son plurales; `evolith topology phase-artifacts -t agentic-ai,event-driven` sí une ambas. Lo que sobrevivió es el cable de APLICACIÓN. No lo cubre `MT-A*`: sus 26 filas están DONE y todas son corpus, esquema o documentación. | — | — | `Core Domain` | Cross | P1 | L | `COMPLETADO` | @@ -728,7 +731,7 @@ Este tablero es la única fuente de verdad para deuda técnica, gaps, oportunida | [`GT-706`](./gap-reference-catalog.es.md#gt-706) | **Nada asegura que los `exports` que un paquete declara resuelvan dentro de su propio tarball, así que un productor publica una subruta fantasma y solo la descubre un consumidor — una publicación demasiado tarde.** `contracts@1.1.0` declaró una subruta de export que no incluía; el fallo salió en el smoke de sala limpia de `infra-providers@1.2.1`, **después de que `core-domain@1.3.1` ya estuviera irreversiblemente en el registry**, dejando la release a medio entregar y sin despublicar posible pasadas 72 horas. La comprobación que existe es real y tiene la forma equivocada: `npm-release.yml:213` calcula «prometidos» como `[pkg.main, ...bin]`, y **`exports` no está en esa lista**. FALSABILIDAD DEMOSTRADA, OBSERVADA EN VERDE: un paquete de dos ficheros que declara `"./ingest"` con solo `dist/index.js` en disco pasa esa aserción corrida literal — `exit=0`, mientras `require pkg/ingest` responde `MODULE_NOT_FOUND`. El smoke de sala limpia tampoco lo cubre, y no es defecto suyo: resuelve lo que un paquete IMPORTA, así que el fantasma del productor es invisible hasta el turno de un consumidor, que es después del paso irreversible. Exposición: 3 de 8 paquetes publicables declaran **23 subrutas de export**, ninguna asegurada, y dos declaran además un `./*` sin cota. **ARREGLADO 2026-08-16 — `.harness/scripts/ci/67-validate-declared-exports.mjs`, corriendo en tiempo de PR sobre todos los workspaces publicables Y por paquete dentro del bucle de release, justo antes de `npm publish`.** Recoge cada hoja de texto del árbol de condiciones, así que `types` cuenta tanto como `default`, e incluye `main`/`bin`, siendo un superconjunto de la aserción que sustituye. **La propia afirmación de esta fila sobre el registry la refutó el guard en su primera corrida:** «22 de 22 resuelven, 0 fantasmas» excluía las claves con comodín por su propio filtro, y una está MUERTA — `core-domain` declara `./infrastructure/adapters/*` **sin ningún directorio `adapters`**, 0 coincidencias en un packlist de 796 ficheros, `MODULE_NOT_FOUND` en el 1.3.1 publicado, y **ningún commit de este repositorio llevó jamás ese path**. Borrada, no ampliada: nunca hubo nada detrás. Falsabilidad observada por los dos lados — rojo con la fixture `./ingest`, con `core-domain` de verdad, y con un fichero presente en disco pero excluido por `files`; verde con la misma fixture en cuanto se incluye y con el árbol entero, **68 destinos declarados en 9 paquetes**. | Un paquete puede prometer una ruta de import que nunca incluyó, y quien se entera es el siguiente paquete en publicarse. | La release se niega a publicar un manifiesto que miente, antes de que nada sea irreversible. | `Infra` | Cross | P1 | S | `COMPLETADO` | -**Progreso:** 677 / 706 completados · 3 en progreso · 0 pendientes · 26 diferidos +**Progreso:** 678 / 708 completados · 3 en progreso · 1 pendiente · 26 diferidos **Oleada 2026-06-23 (auditoría profunda de Winston III):** Añadidos 14 gaps nuevos `GT-212`…`GT-225` del Winston Audit Playbook que cubren: higiene de estado ADR (GT-212), metadata + presupuestos operativos + corpus de guías por topología (GT-213, GT-217, GT-219), observabilidad + OpenAPI en controladores REST (GT-214, GT-215), paridad de input-schemas OPA + densidad de tests por topología (GT-216, GT-222), plantillas de rollback + on-call de Fase 05 (GT-218), cobertura de ramas CLI + paridad de envelope --format + limpieza de skip-list (GT-220, GT-224, GT-225), audit logging HTTP de MCP (GT-221), y tests e2e de paridad cross-surface (GT-223). diff --git a/reference/core/control-center/gaps/gap-tracking.md b/reference/core/control-center/gaps/gap-tracking.md index 004f9be0..bab43606 100644 --- a/reference/core/control-center/gaps/gap-tracking.md +++ b/reference/core/control-center/gaps/gap-tracking.md @@ -4,6 +4,7 @@ **Status:** Active Tracking **Owner:** Evolith Architecture Board +**Last Updated:** 2026-09-05 (**Two new rows off one thread: a HIGH CVE that had been live on `main` for three days, and the reason it stopped nothing.** `GT-709` → DONE: `Security Audit` had been red since `b84523b4` not because of a dependency without a fix, but because `overrides.fast-uri` was pinned at `3.1.5`, **exactly the last vulnerable release of the 3.x line**, with the patch in `3.1.6` and inside the range `ajv` declares. Measured with the real guard, `63-validate-npm-audit-gate.mjs`: from **11 blocking rows / 7 high to 0 and 0**, with no exception declared. `GT-710` → PENDING: that gate **is not a required check**, and in the window it was red **eight** PRs were merged into `main`, four of them npm dependency changes. **What is worth keeping:** the mechanism used to close an advisory — the `overrides` entry — is the same one that later prevents closing it, and the only check that sees it blocks nothing.) **Last Updated:** 2026-08-18 (**One gap closed by the trigger it had written down for itself — which fired two days after it was written, and named the wrong release.** `GT-691` → DONE. The HIGH `js-yaml` CVE that blocked every promotion to `main` left the tree by an upgrade, not a dismissal: `@nestjs/swagger@11.4.7` — a PATCH on the `11.4.x` line the row had declared exhausted, not the stable 12 it said to wait for — declares `"js-yaml": "5.3.0"`, and `package-lock.json` now resolves `node_modules/@nestjs/swagger/node_modules/js-yaml` to it. `npm audit` goes from 1 high to 0 high / 0 critical; the row's falsifiability criterion was re-measured against the new tree rather than inherited, and neither falsifier fired. **What is worth carrying forward is the watch item, not the CVE:** the row correctly identified that the thing to watch was `@nestjs/swagger` rather than `js-yaml`, then tied that watch to a major release that had not shipped.) **Last Updated:** 2026-08-08 (**One gap closed by doing the irreversible thing the board had deliberately deferred — and the measurement taken first is what makes it worth recording.** `GT-622` → DONE, and the irreversible half the board had deferred is what closed it: the 210 orphaned `.github/workflows/ci.yml:codeql` code-scanning analyses are deleted from `refs/heads/main` (201) and `refs/heads/develop` (9), the dead key is absent from both, and the alert corpus is byte-identical across the deletion — 242 open / 82 dismissed / 60 fixed, CodeQL 75 / Scorecard 158 / Trivy 9. **Three of that row’s own claims did not survive re-measurement, and the row had already been re-measured twice without any of them surfacing, because each pass checked the COUNT and none checked the CLAIM:** the check was `neutral`, not red, from PR #250 onward; the configuration the warning names is on `refs/heads/develop`, not `main`, so only PRs into `develop` still carried it while `main` came back clean on its own from PR #420 with all 201 orphans still in place; and the 9 analyses on `develop` — the ones that mattered — were never counted. **The field that turned an owner judgement call into an easy one had never been read:** all 210 analyses carry `results_count: 0`, so what was discarded is 210 records of “scanned, found nothing” from a configuration dead since June. The irreversibility was real; the loss was not. 22 analyses under the dead key are deliberately left on `refs/pull/{4..17}/merge`, ephemeral per-PR refs that can never be the base of a PR. **Also observed while measuring, and not part of this closure:** the required-context set on `main` and `develop` is now **8**, having gained `Secret Detection (gitleaks)` — the promotion `GT-653` recorded as its one remaining item. The closure criterion that could only be observed on a PR into `develop` was observed on PR #440, the PR carrying this very closure, 105s after it opened — written after reading the check, not before. Counters recomputed from the rows: **640 / 653 done · 3 in progress · 3 pending · 7 deferred**.) **Last Updated (wave):** 2026-08-14 · second pass (**Thirteen more gaps registered from a 30-agent benchmark wave, four `DONE` rows corrected, and one of this morning's own rows narrowed.** The wave produced 40 candidates across three lenses, 16 went through adversarial verification with REFUTED as the default, **15 survived and 1 was formally killed** — "a tenant rule can never execute" is FALSE: a novel-id rule was RUN to a real `failed` verdict on two production-wired paths, which narrows [`GT-669`](./gap-reference-catalog.md#gt-669) to executor extensibility and documentation. **The four load-bearing measurements were re-run by hand before anything was written**, and the sharpest reproduced first try: the same pack returns **exit 0 `passed`** on `--engine opa` and **exit 2 `failed` with 2 blocking issues** on `--engine native` ([`GT-675`](./gap-reference-catalog.md#gt-675)). **Four `DONE` rows carry criteria the code does not satisfy** — `GT-321` (ledger with zero instantiations), `GT-266` (provisioning with zero call sites), `GT-221` (resource/prompt loggers with zero production callers), `GT-158` (nothing issues an approval token) — each annotated in place with its measurement and re-registered as a live row rather than reopened, because a closure record on a non-DONE row is a hard error in `08-validate-tracking`. `GT-518` is annotated the same way by [`GT-677`](./gap-reference-catalog.md#gt-677). **Ten of the thirteen rows are NARROWER than proposed**, and each records the sub-claim its verification killed. New ids `GT-675`…`GT-687`. Counters recomputed from the rows: **654 / 685 done · 2 in progress · 19 pending · 10 deferred**.) @@ -20,6 +21,8 @@ This board is the single source of truth for technical debt, gaps, opportunities | ID | Gap | In plain terms | What it fixes | Component | Phase | Criticality | Complexity | Status | |---|---|---|---|:---:|:---:|:---:|:---:|:---:| +| [`GT-710`](./gap-reference-catalog.md#gt-710) | **The gate that measures CVEs is not a required check, so eight merges went past it while it was red.** The nine required contexts on `main` and `develop` are `CodeQL SAST`, `Secret Detection (gitleaks)`, `Services build (GHCR)`, `Test`, `Test core`, `Test core-api`, `Test core-domain`, `Test mcp-server` and `Validate documentation`. **`Security Audit` is not among them**, and neither are `Trivy` or `build-and-test`. Measured 2026-09-05: between `b84523b4` (Sep 2), the commit where `Security Audit` turned red, and its fix in [`GT-709`](./gap-reference-catalog.md#gt-709), **eight pull requests were merged into `main` with the gate red** — four of them ([#664](https://github.com/beyondnetcode/evolith_arch32/pull/664), [#665](https://github.com/beyondnetcode/evolith_arch32/pull/665), [#666](https://github.com/beyondnetcode/evolith_arch32/pull/666), [#667](https://github.com/beyondnetcode/evolith_arch32/pull/667)) npm dependency changes, precisely the class of change that gate exists to judge. GitHub renders them `UNSTABLE` rather than `BLOCKED`, so ordinary review merges them without friction. **The workflow names this failure mode in its own words:** the comment in `sdk-cli-ci.yml` says a permanently red check trains reviewers to discount red, and then leaves the check out of the required set, which is the most direct way to guarantee exactly that. Registered separately from [`GT-709`](./gap-reference-catalog.md#gt-709) on purpose: that one was a CVE with a two-line fix, this is why the CVE could live for three days without stopping anything. | We have a security check that measures correctly and prevents nothing; eight changes landed while it was red. | Make an undeclared HIGH advisory block the merge instead of merely reporting it. | `Infra` | Cross | P1 | S | `PENDING` | +| [`GT-709`](./gap-reference-catalog.md#gt-709) | **An `overrides` pin added to close an advisory becomes the ceiling that prevents closing it the next time.** `Security Audit` had been red on `main` since `b84523b4` (2026-09-02) over `GHSA-jqff-g426-hqxp`, a HIGH CVE in `fast-uri` — and the cause was not a dependency without an upstream fix, but **two pins of our own left below the patched version**, which the gate reports in the same shape as somebody else's advisory. `overrides.fast-uri` was pinned at `3.1.5`, **exactly the last vulnerable release of the 3.x line**; the patch is `3.1.6`, inside the `^3.0.1` that `ajv@8.20.0` declares, so the fix fitted in the pin that was already there and nobody looked because it read as settled configuration. **Measured with `63-validate-npm-audit-gate.mjs`, the same guard CI runs, not inferred from changelogs:** `origin/main` reported **11 blocking rows / 7 high**; with `fast-uri` at `3.1.7`, 9 of the 11 go — its own four advisories plus the five `ajv`/`commitlint` chain rows that arrived *via* `fast-uri`. The remaining two were `browserslist` `4.28.4`, a dev-only transitive (`ts-jest`→`@babel/core`, `@nestjs/cli`→`webpack`) fixed in `4.28.7`: same pattern, same kind of override, pinned to `4.28.9`. **Result: 0 blocking rows, 0 high.** The moderate `qs` advisory survives on purpose — the gate does not block below HIGH. **CLOSED 2026-09-05** by [#689](https://github.com/beyondnetcode/evolith_arch32/pull/689), merge `eb458372` on `main`. **What is worth keeping is not the CVE but the failure mode:** the mechanism used to close an advisory is the same one that later holds it open, and nothing watches the pins. Same pattern as [`GT-691`](./gap-reference-catalog.md#gt-691), where the watch was tied to a major that had not shipped; there the mis-watched item was `@nestjs/swagger`, here it is the `overrides` block itself. That this gate blocks no merge at all is registered separately, as [`GT-710`](./gap-reference-catalog.md#gt-710). | The vulnerability check sat red for three days because of two versions we had ourselves pinned one release below the fix. | Stop the `overrides` block being the place a CVE settles in, and make the security check mean something again. | `Infra` | Cross | P2 | S | `DONE` | | [`GT-708`](./gap-reference-catalog.md#gt-708) | **A governance concept that existed only in prose, in two repositories, and was cited as a precondition by a gate that does exist.** «KDD» named two different things and neither was ever built: **Phase 1.1 — Knowledge-First Discovery**, an optional subphase with its own readiness gate and seven artifact templates; and **KDD — Knowledge-Driven Development**, a later reading from the 2026-07-04 owner session (`L-009`, `D-004`) that made it an optional section *inside the PRD*, activated per tenant. **Measured across every executable surface, and absent from all of them:** `phase-gates.rules.json` has five gates for phases 1..5; none of the seven KDD artifacts is among the 33 in `artifact-registry.json`; zero TypeScript files match `KDD`/`knowledge-first`/`knowledgeBrief`/`discoveryReadiness`/`storySeed`/`epicCandidate`; the CLI has 31 commands and zero mentions, and its `--phase discovery` maps to **phase 1 entire** (`phase-id.ts`: `f1: 'discovery'`), not to 1.1; the MCP server has zero; the Tracker has no screen and no entity; and `prd.schema.json` carries no KDD section, so `D-004` never reached a schema either. **The prose had teeth anyway:** `phase-1-business-signoff.md` made *"Phase 1.1 adoption level has been declared"* a **precondition for opening Gate 1**, and three rows of its evidence table carried clauses keyed to KDD levels — a gate nothing implements blocking a gate everything implements. **CLOSED 2026-08-18 by removal, on the owner's decision that Evolith Core and Tracker no longer carry the concept in any form.** 16 files deleted (the Phase 1.1 playbook and the seven artifact templates, EN and ES); Gate 1's precondition and its three KDD-keyed evidence clauses removed; the `Subphase 01.1` table, the playbook index row and the Story-Seed/Epic-Candidate references in the Phase 2 playbook and template index removed; `D-004`/`L-009` rewritten to what survives them — the PRD is the canonical floor and Gate 1 always requires it. **The retirement is [`ADR-0127`](../../architecture/adrs/core/0127-retire-knowledge-first-discovery.md), and `ADR-0103` is AMENDED by it rather than edited:** an accepted Architecture Board decision is superseded, not rewritten, so its reasoning stands and only its neighbour is gone. `CHANGELOG.md` and `ADR-0103` keep their KDD text on purpose — they record what was true when written, and editing them would falsify the history this repository keeps deliberately. **Falsifiability, checked after the sweep rather than inferred from the delete list:** every reference to the eight deleted filenames returns nothing outside the ADR and the correction notice, and `KDD`/`knowledge-first` survive only in the six files named above. **What this row leaves behind is the lesson, not the sweep:** a concept can be cited as a hard precondition by a gate that everything implements while being implemented by nothing, and stay that way for months, because nobody diffs the prose against the data. **CLOSED 2026-08-18 — both halves landed.** The Tracker half is `evolith_tracker#153` (`97e1bc8e`): `REQ-DIS-12` and `REQ-DIS-13` removed along with the subphase-01.1 governance bullet, the artifact-catalogue section, the blueprint bullet, the Discovery index clauses and the `.bmad-core` blocks. **Both repositories had the same shape of teeth in different words:** the Core made *"Phase 1.1 adoption level has been declared"* a precondition for opening Gate 1, and the Tracker gave `REQ-DIS-13` the acceptance criterion *"a FAIL result blocks opening the Business Sign-Off gate"* — two documents, one non-existent gate, both blocking the only gate every initiative must pass. What survives is deliberate: `CHANGELOG.md` and `ADR-0103` in the Core, and the Tracker's two audit-board rows, all of them records of what was true when written. | A concept we describe everywhere and have built nowhere, which one of our real gates depends on. | The five-phase model reads the same in the docs as in the data, and Gate 1 stops depending on a subphase nobody can execute. | `Governance` | Cross | P2 | M | `DONE` | | [`GT-707`](./gap-reference-catalog.md#gt-707) | **Every standalone binary this repository publishes fails on `--help`, and no release has ever carried one.** Measured 2026-08-18 across all four open pull requests and, before them, on the `v1.3.6` tag push and on the pull request that first made the release pipeline run on pull requests: `smoke-test` and `smoke-test-functional` fail with `ERR_REQUIRE_ESM: require() of ES Module /snapshot/…/@clack/prompts/dist/index.mjs`, raised from `prompt.service.js` — so the binary dies before parsing an argument, on all three platforms. `gh release view` on `v1.3.0` and `v1.1.0` returns **zero assets**: `upload-assets` depends on `smoke-test`, so the channel has never delivered anything, and the pipeline's own gate is what stopped it. **Measured further, so the next attempt starts here:** `@clack/prompts@1.5.1` is the ONLY ESM-only dependency the CLI has (`chalk` 4.1.2, `ora` 5.4.1, `inquirer` 8.2.7 and `cli-table3` are all CommonJS); `esbuild` bundles it to a 107 kB CJS file that loads clean; and packaging with the maintained fork `@yao-pkg/pkg@6` REMOVES the `ERR_REQUIRE_ESM` and then fails differently — `MODULE_NOT_FOUND` for the same `.mjs`, because the file is not in the snapshot — which means the packager and the asset set are two separate defects, not one. **The cheap-looking fix is not cheap:** redirecting the import touches 6 production files and ~24 spec files that `jest.mock('@clack/prompts')` by that exact specifier. **Deliberately NOT fixed inside the four pull requests that found it:** they are green on all 8 required checks and this is a pre-existing failure in a non-required workflow; smuggling a packaging redesign into them would be the unrelated-change-inside-a-promotion this board keeps refusing. **CLOSED 2026-08-18 by vendoring the ESM-only dependencies to CommonJS — and this row's own first evidence was wrong twice, which is the part worth keeping.** The packaged binary now runs: built from this tree, `--help` exits **0**, `--version` prints `1.3.2`, and `init --runtime nodejs --monorepo none --arch clean` exits **0** and writes a satellite. **WRONG #1 — "the only ESM-only dependency".** This row measured that by probing `require('/package.json')`, which eight of the 25 direct dependencies refuse with `ERR_PACKAGE_PATH_NOT_EXPORTED` — an error that reads like "fine". Reading the manifests FROM DISK instead found **three**: `@clack/prompts@1.5.1`, `conf@15.1.0` and `@modelcontextprotocol/sdk@1.29.0`. Two of them are loaded at runtime and both are vendored; the MCP sdk is only a `.d.ts` in this package, so it never enters the snapshot. `pkg` had been telling us about `conf` all along — it dies naming `conf/package.json` and `config.service.js` — and nobody read past the first error. **WRONG #2 — the fallback pointed at nothing.** `clack.ts` compiles to `dist/infrastructure/prompts/` while the bundle is written to `dist/vendor/`, so `require('./vendor/clack.cjs')` resolved to a path that does not exist. Nothing failed at build time; the BINARY failed at run time with the package's own error, because the fallback's `MODULE_NOT_FOUND` was swallowed by a rethrow of the original. Both are now structural fixes rather than corrections: the rethrow names BOTH failures, and `vendor-esm-deps.mjs` reads the relative path out of each COMPILED shim and resolves it — observed red against the broken path, with the resolved location in the message. **What ships:** `scripts/vendor-esm-deps.mjs` bundles each ESM-only dependency with esbuild (clack 107 kB, conf 410 kB), then loads each one back in a child process with `--no-experimental-require-module` — the closest an ordinary Node process gets to the snapshot's no-ESM contract — and compares its export surface against the real package. Two shims (`prompts/clack.ts`, `config/conf-module.ts`) try the PACKAGE FIRST and fall back to the bundle on ANY load failure: the order is what keeps the ~24 specs that `jest.mock('@clack/prompts')` intercepting the same specifier they always did, and "any failure" is because the same cause surfaces as `ERR_REQUIRE_ESM` under one packager and `MODULE_NOT_FOUND` under another. 106 suites / 1485 tests green, `tsc -b` clean. The release pipeline is unchanged: the fix is packager-agnostic, and its `smoke-test` is the falsifiability this row asked for. | The downloadable program we publish crashes instantly, and in fact we have never published one. | Either a standalone binary that runs, or an honest retirement of the channel — not a red check everybody learns to ignore. | `Infra` | Cross | P2 | M | `DONE` | | [`GT-688`](./gap-reference-catalog.md#gt-688) | **A confirmed topology composition is truncated to one id before the gate, so a mixed system gets a green topology verdict for the one topology that survived.** Measured live 2026-08-14 against the built dist: `manifestFromWorkspace` with `design.topologyConfirmedRefs: [modular-monolith, agentic-ai, event-driven]` returns `{"topology":"modular-monolith","facts":{"context":{"topologyRef":"modular-monolith"}}}` — **the other two appear nowhere**. Cause: `evaluation-context.builder.ts:26` `topology: ctx.topologyRef`, and `grep -n "design"` over that whole file returns one hit, a comment at `:149`. **Worse without the scalar:** the manifest carries no `topology` key at all, the kind SKIPs (`kind-evaluators.ts:363`), and the pipeline re-derives one **by regex over the YAML on disk**, first match wins (`satellite-evaluation-pipeline.service.ts:354`). The result contract cannot carry two either (`TopologyEvaluationResult.topologyRef`, required scalar). So the kind returns `PASS, conformant: true` for the id it kept — **a green verdict over a system half of which was never checked**. **The owner observation that produced this row was half REFUTED, and the refuted half matters:** the model is NOT singular — `ADR-0079:44` rejects the exclusive design in writing, transversality is a formal declaration (`maturityLevel: "cross"` on all five non-progressive manifests), and the corpus, satellite contract and design evaluator are plural; `evolith topology phase-artifacts -t agentic-ai,event-driven` really does union both. What survived is the ENFORCEMENT wire. Not covered by `MT-A*`: all 26 rows are DONE and every one is corpus/schema/docs work. | — | — | `Core Domain` | Cross | P1 | L | `DONE` | @@ -728,7 +731,7 @@ This board is the single source of truth for technical debt, gaps, opportunities | [`GT-706`](./gap-reference-catalog.md#gt-706) | **Nothing asserts that a package's own declared `exports` resolve inside its own tarball, so a producer publishes a phantom subpath and only a consumer discovers it — one publish too late.** `contracts@1.1.0` declared an export subpath it did not ship; the failure surfaced at `infra-providers@1.2.1`'s clean-room smoke, **after `core-domain@1.3.1` was already irreversibly on the registry**, leaving the release half-shipped with no unpublish available after 72 hours. The check that exists is real and the wrong shape: `npm-release.yml:213` computes "promised" as `[pkg.main, ...bin]`, and **`exports` is not in that list**. PROVEN FALSIFIABLE, OBSERVED GREEN: a two-file package declaring `"./ingest"` with only `dist/index.js` on disk passes that assertion run verbatim — `exit=0`, while `require pkg/ingest` answers `MODULE_NOT_FOUND`. The clean-room smoke does not cover it either, and that is not its defect: it resolves what a package IMPORTS, so a producer's phantom is invisible until a consumer's turn, which is after the irreversible step. Exposure: 3 of 8 publishable packages declare **23 export subpaths**, none asserted, two of them also declaring an unbounded `./*`. **FIXED 2026-08-16 — `.harness/scripts/ci/67-validate-declared-exports.mjs`, run at PR time over every publishable workspace AND per package inside the release loop, immediately before `npm publish`.** It collects every string leaf of the condition tree, so `types` counts as much as `default`, and folds in `main`/`bin`, making it a superset of the assertion it replaces. **The row's own claim about the registry was refuted by the guard on its first run:** "22 of 22 resolve, 0 phantom" excluded wildcard keys by its own filter, and one is DEAD — `core-domain` declares `./infrastructure/adapters/*` with **no `adapters` directory at all**, 0 matches in a 796-file packlist, `MODULE_NOT_FOUND` on the published 1.3.1, and **no commit in this repository ever carried that path**. Deleted, not widened: there was never anything behind it. Falsifiability observed on both sides — red on the `./ingest` fixture, on `core-domain` for real, and on a file present on disk but excluded by `files`; green on the same fixture once it ships and on the whole tree, **68 declared targets across 9 packages**. | A package can promise an import path it never shipped, and the next package to publish is the one that finds out. | The release refuses to publish a manifest that lies, before anything becomes irreversible. | `Infra` | Cross | P1 | S | `DONE` | -**Progress:** 677 / 706 done · 3 in progress · 0 pending · 26 deferred +**Progress:** 678 / 708 done · 3 in progress · 1 pending · 26 deferred **Wave 2026-06-23 (Winston deep audit III):** Added 14 new gaps `GT-212`…`GT-225` from the Winston Audit Playbook covering: ADR status hygiene (GT-212), topology manifest metadata + operational budgets + guidance corpus (GT-213, GT-217, GT-219), REST controller observability + OpenAPI (GT-214, GT-215), OPA input-schema parity + per-topology test density (GT-216, GT-222), SDLC Phase 05 rollback + on-call templates (GT-218), CLI branch coverage + envelope format coverage + skip-list cleanup (GT-220, GT-224, GT-225), MCP HTTP audit logging (GT-221), and cross-surface parity e2e tests (GT-223). diff --git a/reference/core/control-center/maturity-reports/executive-summary.es.md b/reference/core/control-center/maturity-reports/executive-summary.es.md index bca7817a..744f3ed6 100644 --- a/reference/core/control-center/maturity-reports/executive-summary.es.md +++ b/reference/core/control-center/maturity-reports/executive-summary.es.md @@ -27,8 +27,8 @@ La forma correcta de usar este resumen es simple: si necesitas contexto, abre so |---:|---|---|---| | 1 | Bloqueadores P0 | Impiden afirmar readiness productivo o release mayor. | [GT-435](../gaps/gap-reference-catalog.es.md#gt-435) | | 2 | Área de mayor riesgo | `Governance` tiene la mayor carga ponderada abierta. | [GT-670](../gaps/gap-reference-catalog.es.md#gt-670), [GT-585](../gaps/gap-reference-catalog.es.md#gt-585), [GT-669](../gaps/gap-reference-catalog.es.md#gt-669), [GT-672](../gaps/gap-reference-catalog.es.md#gt-672), [GT-689](../gaps/gap-reference-catalog.es.md#gt-689), [GT-588](../gaps/gap-reference-catalog.es.md#gt-588), +2 | -| 3 | Ganancias rápidas | Alta criticidad con complejidad XS/S. | [GT-684](../gaps/gap-reference-catalog.es.md#gt-684) | -| 4 | Ola P1 | Endurecimiento siguiente después de limpiar P0. | [GT-684](../gaps/gap-reference-catalog.es.md#gt-684), [GT-324](../gaps/gap-reference-catalog.es.md#gt-324), [GT-670](../gaps/gap-reference-catalog.es.md#gt-670), [GT-680](../gaps/gap-reference-catalog.es.md#gt-680), [GT-681](../gaps/gap-reference-catalog.es.md#gt-681), [GT-585](../gaps/gap-reference-catalog.es.md#gt-585), [GT-669](../gaps/gap-reference-catalog.es.md#gt-669), [GT-448](../gaps/gap-reference-catalog.es.md#gt-448) | +| 3 | Ganancias rápidas | Alta criticidad con complejidad XS/S. | [GT-684](../gaps/gap-reference-catalog.es.md#gt-684), [GT-710](../gaps/gap-reference-catalog.es.md#gt-710) | +| 4 | Ola P1 | Endurecimiento siguiente después de limpiar P0. | [GT-684](../gaps/gap-reference-catalog.es.md#gt-684), [GT-710](../gaps/gap-reference-catalog.es.md#gt-710), [GT-324](../gaps/gap-reference-catalog.es.md#gt-324), [GT-670](../gaps/gap-reference-catalog.es.md#gt-670), [GT-680](../gaps/gap-reference-catalog.es.md#gt-680), [GT-681](../gaps/gap-reference-catalog.es.md#gt-681), [GT-585](../gaps/gap-reference-catalog.es.md#gt-585), [GT-669](../gaps/gap-reference-catalog.es.md#gt-669), +1 | | 5 | P2/P3 | Solo después de estabilizar seguridad, CI, reglas y contratos. | [GT-444](../gaps/gap-reference-catalog.es.md#gt-444), [GT-464](../gaps/gap-reference-catalog.es.md#gt-464), [GT-674](../gaps/gap-reference-catalog.es.md#gt-674), [GT-685](../gaps/gap-reference-catalog.es.md#gt-685), [GT-686](../gaps/gap-reference-catalog.es.md#gt-686), [GT-687](../gaps/gap-reference-catalog.es.md#gt-687), +10 | ## Bloqueadores Actuales @@ -41,23 +41,23 @@ La forma correcta de usar este resumen es simple: si necesitas contexto, abre so | Indicador | Valor | |---|---:| -| Fecha canónica del tablero | 2026-08-18 | -| Gaps totales | 706 | -| Gaps cerrados | 677 | -| Gaps pendientes | 29 | +| Fecha canónica del tablero | 2026-09-05 | +| Gaps totales | 708 | +| Gaps cerrados | 678 | +| Gaps pendientes | 30 | | P0 abiertos | 1 | -| P1 abiertos | 8 | +| P1 abiertos | 9 | | P2 abiertos | 16 | -| Cierre total | 95.9% | -| Registros de evidencia de cierre | 659 | +| Cierre total | 95.8% | +| Registros de evidencia de cierre | 660 | | Readiness registrado | 4 PASS | | Área | Pendientes | P0 | P1 | Primeros IDs | |---|---:|---:|---:|---| | `Governance` | 8 | 0 | 3 | [GT-670](../gaps/gap-reference-catalog.es.md#gt-670), [GT-585](../gaps/gap-reference-catalog.es.md#gt-585), [GT-669](../gaps/gap-reference-catalog.es.md#gt-669), [GT-672](../gaps/gap-reference-catalog.es.md#gt-672), +4 | | `Cross` | 3 | 1 | 1 | [GT-435](../gaps/gap-reference-catalog.es.md#gt-435), [GT-448](../gaps/gap-reference-catalog.es.md#gt-448), [GT-651](../gaps/gap-reference-catalog.es.md#gt-651) | +| `Infra` | 5 | 0 | 2 | [GT-710](../gaps/gap-reference-catalog.es.md#gt-710), [GT-324](../gaps/gap-reference-catalog.es.md#gt-324), [GT-464](../gaps/gap-reference-catalog.es.md#gt-464), [GT-685](../gaps/gap-reference-catalog.es.md#gt-685), +1 | | `MCP Server` | 3 | 0 | 3 | [GT-684](../gaps/gap-reference-catalog.es.md#gt-684), [GT-680](../gaps/gap-reference-catalog.es.md#gt-680), [GT-681](../gaps/gap-reference-catalog.es.md#gt-681) | -| `Infra` | 4 | 0 | 1 | [GT-324](../gaps/gap-reference-catalog.es.md#gt-324), [GT-464](../gaps/gap-reference-catalog.es.md#gt-464), [GT-685](../gaps/gap-reference-catalog.es.md#gt-685), [GT-692](../gaps/gap-reference-catalog.es.md#gt-692) | | `Core Domain` | 3 | 0 | 0 | [GT-674](../gaps/gap-reference-catalog.es.md#gt-674), [GT-687](../gaps/gap-reference-catalog.es.md#gt-687), [GT-678](../gaps/gap-reference-catalog.es.md#gt-678) | ## Fuente y Regla de Actualización diff --git a/reference/core/control-center/maturity-reports/executive-summary.md b/reference/core/control-center/maturity-reports/executive-summary.md index 9ec9256a..a68804bb 100644 --- a/reference/core/control-center/maturity-reports/executive-summary.md +++ b/reference/core/control-center/maturity-reports/executive-summary.md @@ -27,8 +27,8 @@ Use this summary with a simple rule: if you need context, open only the linked I |---:|---|---|---| | 1 | P0 blockers | They prevent production-readiness or major-release confidence. | [GT-435](../gaps/gap-reference-catalog.md#gt-435) | | 2 | Highest-risk area | `Governance` has the largest weighted open load. | [GT-670](../gaps/gap-reference-catalog.md#gt-670), [GT-585](../gaps/gap-reference-catalog.md#gt-585), [GT-669](../gaps/gap-reference-catalog.md#gt-669), [GT-672](../gaps/gap-reference-catalog.md#gt-672), [GT-689](../gaps/gap-reference-catalog.md#gt-689), [GT-588](../gaps/gap-reference-catalog.md#gt-588), +2 | -| 3 | Quick wins | High criticality with XS/S complexity. | [GT-684](../gaps/gap-reference-catalog.md#gt-684) | -| 4 | P1 wave | Next hardening after P0 is cleared. | [GT-684](../gaps/gap-reference-catalog.md#gt-684), [GT-324](../gaps/gap-reference-catalog.md#gt-324), [GT-670](../gaps/gap-reference-catalog.md#gt-670), [GT-680](../gaps/gap-reference-catalog.md#gt-680), [GT-681](../gaps/gap-reference-catalog.md#gt-681), [GT-585](../gaps/gap-reference-catalog.md#gt-585), [GT-669](../gaps/gap-reference-catalog.md#gt-669), [GT-448](../gaps/gap-reference-catalog.md#gt-448) | +| 3 | Quick wins | High criticality with XS/S complexity. | [GT-684](../gaps/gap-reference-catalog.md#gt-684), [GT-710](../gaps/gap-reference-catalog.md#gt-710) | +| 4 | P1 wave | Next hardening after P0 is cleared. | [GT-684](../gaps/gap-reference-catalog.md#gt-684), [GT-710](../gaps/gap-reference-catalog.md#gt-710), [GT-324](../gaps/gap-reference-catalog.md#gt-324), [GT-670](../gaps/gap-reference-catalog.md#gt-670), [GT-680](../gaps/gap-reference-catalog.md#gt-680), [GT-681](../gaps/gap-reference-catalog.md#gt-681), [GT-585](../gaps/gap-reference-catalog.md#gt-585), [GT-669](../gaps/gap-reference-catalog.md#gt-669), +1 | | 5 | P2/P3 | Only after security, CI, rules, and contracts stabilize. | [GT-444](../gaps/gap-reference-catalog.md#gt-444), [GT-464](../gaps/gap-reference-catalog.md#gt-464), [GT-674](../gaps/gap-reference-catalog.md#gt-674), [GT-685](../gaps/gap-reference-catalog.md#gt-685), [GT-686](../gaps/gap-reference-catalog.md#gt-686), [GT-687](../gaps/gap-reference-catalog.md#gt-687), +10 | ## Current Blockers @@ -41,23 +41,23 @@ Use this summary with a simple rule: if you need context, open only the linked I | Indicator | Value | |---|---:| -| Canonical board date | 2026-08-18 | -| Total gaps | 706 | -| Closed gaps | 677 | -| Open gaps | 29 | +| Canonical board date | 2026-09-05 | +| Total gaps | 708 | +| Closed gaps | 678 | +| Open gaps | 30 | | Open P0 | 1 | -| Open P1 | 8 | +| Open P1 | 9 | | Open P2 | 16 | -| Total closure | 95.9% | -| Closure evidence records | 659 | +| Total closure | 95.8% | +| Closure evidence records | 660 | | Recorded readiness | 4 PASS | | Area | Open | P0 | P1 | First IDs | |---|---:|---:|---:|---| | `Governance` | 8 | 0 | 3 | [GT-670](../gaps/gap-reference-catalog.md#gt-670), [GT-585](../gaps/gap-reference-catalog.md#gt-585), [GT-669](../gaps/gap-reference-catalog.md#gt-669), [GT-672](../gaps/gap-reference-catalog.md#gt-672), +4 | | `Cross` | 3 | 1 | 1 | [GT-435](../gaps/gap-reference-catalog.md#gt-435), [GT-448](../gaps/gap-reference-catalog.md#gt-448), [GT-651](../gaps/gap-reference-catalog.md#gt-651) | +| `Infra` | 5 | 0 | 2 | [GT-710](../gaps/gap-reference-catalog.md#gt-710), [GT-324](../gaps/gap-reference-catalog.md#gt-324), [GT-464](../gaps/gap-reference-catalog.md#gt-464), [GT-685](../gaps/gap-reference-catalog.md#gt-685), +1 | | `MCP Server` | 3 | 0 | 3 | [GT-684](../gaps/gap-reference-catalog.md#gt-684), [GT-680](../gaps/gap-reference-catalog.md#gt-680), [GT-681](../gaps/gap-reference-catalog.md#gt-681) | -| `Infra` | 4 | 0 | 1 | [GT-324](../gaps/gap-reference-catalog.md#gt-324), [GT-464](../gaps/gap-reference-catalog.md#gt-464), [GT-685](../gaps/gap-reference-catalog.md#gt-685), [GT-692](../gaps/gap-reference-catalog.md#gt-692) | | `Core Domain` | 3 | 0 | 0 | [GT-674](../gaps/gap-reference-catalog.md#gt-674), [GT-687](../gaps/gap-reference-catalog.md#gt-687), [GT-678](../gaps/gap-reference-catalog.md#gt-678) | ## Source and Refresh Rule diff --git a/reference/core/control-center/maturity-reports/maturity-evidence.json b/reference/core/control-center/maturity-reports/maturity-evidence.json index 6e56b236..3b9e25b5 100644 --- a/reference/core/control-center/maturity-reports/maturity-evidence.json +++ b/reference/core/control-center/maturity-reports/maturity-evidence.json @@ -1,6 +1,6 @@ { "schemaVersion": "1.0.0", - "asOf": "2026-08-18", + "asOf": "2026-09-05", "checks": [ { "id": "cli-baseline", diff --git a/reference/core/control-center/maturity-reports/maturity-reconciliation.json b/reference/core/control-center/maturity-reports/maturity-reconciliation.json index b53c8fd8..1bdc8ede 100644 --- a/reference/core/control-center/maturity-reports/maturity-reconciliation.json +++ b/reference/core/control-center/maturity-reports/maturity-reconciliation.json @@ -1,16 +1,16 @@ { "schemaVersion": "1.0.0", "scope": "evolith-core", - "asOf": "2026-08-18", + "asOf": "2026-09-05", "gaps": { - "total": 706, - "done": 677, - "pending": 0, + "total": 708, + "done": 678, + "pending": 1, "inProgress": 3, "deferred": 26 }, "evidence": { - "closureRecords": 659, + "closureRecords": 660, "cliPackage": "@beyondnet/evolith-cli@1.3.2", "adrCount": 144, "rulesetCount": 184,