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 eecd176e..c5e26a3f 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.es.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.es.md @@ -10092,7 +10092,8 @@ Los dos se arreglaron de forma estructural y no como correcciones: el rethrow no | dato | valor | |---|---| | contextos requeridos en `main` y `develop` | 9 | - | ¿incluyen `Security Audit`? | **no** (tampoco `Trivy` ni `build-and-test`) | + | ¿incluyen `Security Audit`? | **no** cuando se registró; **sí desde el 2026-09-05** (10 contextos en ambas ramas, verificado) | + | ¿incluyen `Trivy Container Scan` o `build-and-test`? | **no** — decididos requeridos, pendiente de aplicar; `build-and-test` con una precondición (ver criterio 3) | | `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` | @@ -10111,5 +10112,5 @@ Los dos se arreglaron de forma estructural y no como correcciones: el rethrow no - **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. + - [~] La decisión sobre `Trivy` y `build-and-test` queda escrita — requeridos también, o registrado por qué no lo son. **DECIDIDO por el dueño el 2026-09-05: requeridos los dos.** Ejecutable solo la mitad, y la otra mitad no es pereza sino un deadlock medido: **`Trivy Container Scan` es seguro de requerir** porque vive en `sdk-cli-ci.yml`, que **no lleva filtro `paths`** —y su comentario explica que no debe llevarlo nunca, por el bloqueo que `CodeQL SAST` causó en el PR #218 al volverse requerido—, así que reporta en todo PR. **`build-and-test` NO puede requerirse tal como está:** vive en `sdk-cli-release.yml`, cuyo disparador `pull_request` sí filtra por `src/sdk/cli/**`, `src/packages/**`, `.github/workflows/sdk-cli-release.yml` y `.harness/**`. Un check requerido detrás de un filtro de rutas **nunca reporta** en un PR que no las toca, y GitHub lee «no reportó» como «no satisfecho»: el PR queda inmergeable para siempre con todo en verde. Es exactamente lo que le habría pasado a [#690](https://github.com/beyondnetcode/evolith_arch32/pull/690), que solo tocó `reference/`. **Precondición, no alternativa:** quitar el filtro `paths` del `pull_request` de `sdk-cli-release.yml` —el mismo arreglo que ya se aplicó a `sdk-cli-ci.yml`— y solo entonces añadirlo a los requeridos. **Nota sobre el nombre:** el check a requerir es `Trivy Container Scan`, el nombre del job; el check `Trivy` a secas que publica `aquasecurity/trivy-action` aparece en `main` pero no en la cabeza de `develop`, así que requerir ese nombre reintroduciría el mismo deadlock por otra vía. - **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 e8283a20..02b21a69 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.md @@ -10185,7 +10185,8 @@ Both were fixed structurally rather than corrected: the rethrow now names BOTH f | fact | value | |---|---| | required contexts on `main` and `develop` | 9 | - | do they include `Security Audit`? | **no** (nor `Trivy` nor `build-and-test`) | + | do they include `Security Audit`? | **no** when registered; **yes since 2026-09-05** (10 contexts on both branches, verified) | + | do they include `Trivy Container Scan` or `build-and-test`? | **no** — decided required, not yet applied; `build-and-test` carries a precondition (see criterion 3) | | `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` | @@ -10204,5 +10205,5 @@ Both were fixed structurally rather than corrected: the rethrow now names BOTH f - **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. + - [~] The decision on `Trivy` and `build-and-test` is written down — required too, or a recorded reason why not. **DECIDED by the owner 2026-09-05: both required.** Only half is executable, and the other half is not laziness but a measured deadlock: **`Trivy Container Scan` is safe to require** because it lives in `sdk-cli-ci.yml`, which carries **no `paths` filter** — and whose comment says it must never carry one, because of the block `CodeQL SAST` caused on PR #218 once it became required — so it reports on every PR. **`build-and-test` CANNOT be required as it stands:** it lives in `sdk-cli-release.yml`, whose `pull_request` trigger does filter on `src/sdk/cli/**`, `src/packages/**`, `.github/workflows/sdk-cli-release.yml` and `.harness/**`. A required check behind a path filter **never reports** on a PR that misses those paths, and GitHub reads "never reported" as "not satisfied": the PR is unmergeable forever with everything green. That is exactly what would have happened to [#690](https://github.com/beyondnetcode/evolith_arch32/pull/690), which touched only `reference/`. **A precondition, not an alternative:** drop the `paths` filter from `sdk-cli-release.yml`'s `pull_request` — the same fix already applied to `sdk-cli-ci.yml` — and only then add it to the required set. **On the name:** the check to require is `Trivy Container Scan`, the job name; the bare `Trivy` check published by `aquasecurity/trivy-action` shows on `main` but not on `develop`'s head, so requiring that name would reintroduce the same deadlock by another route. - **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 3aeda0be..4f19ed3f 100644 --- a/reference/core/control-center/gaps/gap-tracking.es.md +++ b/reference/core/control-center/gaps/gap-tracking.es.md @@ -21,7 +21,7 @@ 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-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. **Aplicado en parte el 2026-09-05:** `Security Audit` ya es requerido en `main` y `develop` (10 contextos, verificado), y el dueño decidió que `Trivy` y `build-and-test` lo sean también. Sigue abierta porque **el falsador de esta fila aún no se ha disparado** —no hay ninguna alta viva que observar quedando `BLOCKED` en vez de `UNSTABLE`— y porque `build-and-test` **no puede requerirse tal como está**: vive en `sdk-cli-release.yml`, cuyo `pull_request` filtra por rutas, y un check requerido detrás de un filtro nunca reporta y deja el PR inmergeable con todo en verde. 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` | diff --git a/reference/core/control-center/gaps/gap-tracking.md b/reference/core/control-center/gaps/gap-tracking.md index bab43606..6c18efc5 100644 --- a/reference/core/control-center/gaps/gap-tracking.md +++ b/reference/core/control-center/gaps/gap-tracking.md @@ -21,7 +21,7 @@ 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-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. **Partly applied 2026-09-05:** `Security Audit` is now required on `main` and `develop` (10 contexts, verified), and the owner decided `Trivy` and `build-and-test` should be too. It stays open because **this row's falsifier has not fired yet** — there is no live high advisory to observe coming out `BLOCKED` rather than `UNSTABLE` — and because `build-and-test` **cannot be required as it stands**: it lives in `sdk-cli-release.yml`, whose `pull_request` trigger is path-filtered, and a required check behind a filter never reports, leaving the PR unmergeable with everything green. 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` |