diff --git a/docs/known-limitations.es.md b/docs/known-limitations.es.md index 8ca72dc50..60bc865ca 100644 --- a/docs/known-limitations.es.md +++ b/docs/known-limitations.es.md @@ -39,7 +39,7 @@ Medido el 2026-09-20 con `@beyondnet/evolith-cli@1.3.2` en un contenedor limpio: ## El Tracker le envió un repositorio al Core, y el Core respondió 500 -Medido el 2026-09-20 contra la imagen del Core construida desde `main@142b8324`, la que corre el entorno UAT: la llamada de conformidad del repositorio del Tracker (`POST /products/{id}/evaluate-architecture` → Core `POST /api/v1/evaluate` con el repositorio inline, 150 ficheros, ~1 MB) volvió como `500 INTERNAL_ERROR "An unexpected error occurred"`, sin una sola línea de log en el Core. Reproducido en local sobre la misma imagen: 14 ficheros (99,8 KB) → `200`, 15 ficheros (101,6 KB) → `500`. El techo de 100 KB por omisión de Express para JSON, nunca subido, nunca nombrado. Arreglado como GT-715 —un techo configurable (`EVOLITH_MAX_BODY_BYTES`, 2 MiB por defecto), un `413 PAYLOAD_TOO_LARGE` que dice los dos tamaños y una línea de log por cada 5xx enmascarado—, pero la imagen que corre el UAT es anterior al arreglo hasta que `main` se promueva y se redespliegue. La captura del Tracker en la portada es, por eso, la compuerta de fase alrededor de una iniciativa, que nunca pasa por este camino; no es el veredicto sobre el repositorio. +Medido el 2026-09-20 contra la imagen del Core construida desde `main@142b8324`, la que corre el entorno UAT: la llamada de conformidad del repositorio del Tracker (`POST /products/{id}/evaluate-architecture` → Core `POST /api/v1/evaluate` con el repositorio inline, 150 ficheros, ~1 MB) volvió como `500 INTERNAL_ERROR "An unexpected error occurred"`, sin una sola línea de log en el Core. Reproducido en local sobre la misma imagen: 14 ficheros (99,8 KB) → `200`, 15 ficheros (101,6 KB) → `500`. El techo de 100 KB por omisión de Express para JSON, nunca subido, nunca nombrado. Arreglado como GT-715 —un techo configurable (`EVOLITH_MAX_BODY_BYTES`, 2 MiB por defecto), un `413 PAYLOAD_TOO_LARGE` que dice los dos tamaños y una línea de log por cada 5xx enmascarado—, y promovido el mismo día: Promovido en #778 (`9c5deedf`) y redesplegado por el job `Deploy UAT (Coolify)` de la corrida 35490911533 el 2026-09-20; medido justo después: la misma llamada `evaluate-architecture` responde `200`, `provenance: core`, `status: COMPLETED`, `resultDecision: FAILED` — un veredicto real sobre 150 ficheros (gates f1–f5 fallidos por artefactos de fase ausentes), 174 ms en el Core. La captura del Tracker en la portada es la compuerta de fase alrededor de una iniciativa —tomada antes del redespliegue, y conservada porque enseña lo que la CLI no tiene—, no el veredicto sobre el repositorio, que es la medición de arriba. ## Lo que no está construido diff --git a/docs/known-limitations.md b/docs/known-limitations.md index 14db02115..384e98e85 100644 --- a/docs/known-limitations.md +++ b/docs/known-limitations.md @@ -39,7 +39,7 @@ Measured on 2026-09-20 with `@beyondnet/evolith-cli@1.3.2` in a clean container: ## The Tracker sent the Core a repository, and the Core answered 500 -Measured on 2026-09-20 against the Core image built from `main@142b8324`, the one the UAT environment runs: the Tracker's repository-conformance call (`POST /products/{id}/evaluate-architecture` → Core `POST /api/v1/evaluate` with the repository inline, 150 files, ~1 MB) came back `500 INTERNAL_ERROR "An unexpected error occurred"`, with no log line on the Core. Reproduced locally on the same image: 14 files (99.8 KB) → `200`, 15 files (101.6 KB) → `500`. Express's default 100 KB JSON ceiling, never raised, never named. Fixed as GT-715 — a configurable ceiling (`EVOLITH_MAX_BODY_BYTES`, 2 MiB by default), a `413 PAYLOAD_TOO_LARGE` that states both sizes, and a log line for every masked 5xx — but the image the UAT runs predates the fix until `main` is promoted and redeployed. The Tracker screenshot on the front page is therefore the phase gate around an initiative, which never touches this path; it is not the repository verdict. +Measured on 2026-09-20 against the Core image built from `main@142b8324`, the one the UAT environment runs: the Tracker's repository-conformance call (`POST /products/{id}/evaluate-architecture` → Core `POST /api/v1/evaluate` with the repository inline, 150 files, ~1 MB) came back `500 INTERNAL_ERROR "An unexpected error occurred"`, with no log line on the Core. Reproduced locally on the same image: 14 files (99.8 KB) → `200`, 15 files (101.6 KB) → `500`. Express's default 100 KB JSON ceiling, never raised, never named. Fixed as GT-715 — a configurable ceiling (`EVOLITH_MAX_BODY_BYTES`, 2 MiB by default), a `413 PAYLOAD_TOO_LARGE` that states both sizes, and a log line for every masked 5xx — and promoted the same day: Promoted in #778 (`9c5deedf`) and redeployed by the `Deploy UAT (Coolify)` job of run 35490911533 on 2026-09-20; measured right after: the same `evaluate-architecture` call answers `200`, `provenance: core`, `status: COMPLETED`, `resultDecision: FAILED` — a real verdict on 150 files (gates f1–f5 failed for missing phase artifacts), 174 ms in the Core. The Tracker screenshot on the front page is the phase gate around an initiative — taken before the redeploy, and kept because it shows what the CLI does not have — not the repository verdict, which is the measurement above. ## What is not built diff --git a/product/products/evolith-tracker/README.es.md b/product/products/evolith-tracker/README.es.md index a8ac91886..5d5ec4549 100644 --- a/product/products/evolith-tracker/README.es.md +++ b/product/products/evolith-tracker/README.es.md @@ -8,7 +8,7 @@ **Suite Padre:** [Evolith Product Suite](../../suite/README.es.md) **Core Gobernante:** [Evolith Core](../../../reference/core/README.es.md) -> **Estado de implementación (2026-09-20).** El Tracker existe y corre: iniciativas a través de las cinco fases, criterios de gate derivados del tipo, cadenas de aprobación del product owner, evaluación de compuerta de fase con veredicto del Core, evidencia, auditoría — la [portada](../../../README.es.md#la-compuerta-de-fase-en-la-cli-y-en-el-tracker) lleva una captura de su entorno UAT gobernando este mismo repositorio. Lo que **no** es cierto todavía: un lanzamiento, un repositorio público y una llamada de conformidad del repositorio que pase en UAT (el techo de 100 KB del cuerpo del Core la rechazó — GT-715, arreglado en el Core, a la espera de la siguiente promoción). El [Diseño de Interfaces Técnicas de Tracker](./sdlc-tracker-technical-interfaces.es.md) de abajo es anterior a la implementación y sigue marcado como *Diseño Propuesto*; no describe el producto entregado. +> **Estado de implementación (2026-09-20).** El Tracker existe y corre: iniciativas a través de las cinco fases, criterios de gate derivados del tipo, cadenas de aprobación del product owner, evaluación de compuerta de fase con veredicto del Core, evidencia, auditoría — la [portada](../../../README.es.md#la-compuerta-de-fase-en-la-cli-y-en-el-tracker) lleva una captura de su entorno UAT gobernando este mismo repositorio. Lo que **no** es cierto todavía: un lanzamiento y un repositorio público. La llamada de conformidad del repositorio en UAT la rechazó el techo de 100 KB del cuerpo del Core al primer intento (GT-715); arreglado, promovido y redesplegado el mismo día, hoy devuelve un veredicto real sobre este repositorio. El [Diseño de Interfaces Técnicas de Tracker](./sdlc-tracker-technical-interfaces.es.md) de abajo es anterior a la implementación y sigue marcado como *Diseño Propuesto*; no describe el producto entregado. --- diff --git a/product/products/evolith-tracker/README.md b/product/products/evolith-tracker/README.md index fa209d646..e7c02e4f2 100644 --- a/product/products/evolith-tracker/README.md +++ b/product/products/evolith-tracker/README.md @@ -8,7 +8,7 @@ **Parent Suite:** [Evolith Product Suite](../../suite/README.md) **Governing Core:** [Evolith Core](../../../reference/core/README.md) -> **Implementation status (2026-09-20).** The Tracker exists and runs: initiatives through the five phases, type-derived gate criteria, product-owner approval chains, phase-gate evaluation with a Core verdict, evidence, audit — the [front page](../../../README.md#the-phase-gate-in-the-cli-and-in-the-tracker) carries a capture of its UAT environment governing this very repository. What is **not** true yet: a launch, a public repository, and a passing repository-conformance call in UAT (the Core's 100 KB body ceiling rejected it — GT-715, fixed in the Core, awaiting the next promotion). The [Tracker Technical Interface Design](./sdlc-tracker-technical-interfaces.md) below predates the implementation and is still marked *Proposed Design*; it is not a description of the shipped product. +> **Implementation status (2026-09-20).** The Tracker exists and runs: initiatives through the five phases, type-derived gate criteria, product-owner approval chains, phase-gate evaluation with a Core verdict, evidence, audit — the [front page](../../../README.md#the-phase-gate-in-the-cli-and-in-the-tracker) carries a capture of its UAT environment governing this very repository. What is **not** true yet: a launch and a public repository. The repository-conformance call in UAT was rejected by the Core's 100 KB body ceiling on the first try (GT-715); fixed, promoted and redeployed the same day, it now returns a real verdict on this repository. The [Tracker Technical Interface Design](./sdlc-tracker-technical-interfaces.md) below predates the implementation and is still marked *Proposed Design*; it is not a description of the shipped product. --- diff --git a/reference/core/control-center/evidence/gap-closure-evidence.json b/reference/core/control-center/evidence/gap-closure-evidence.json index 4c2e4ac7a..15e7925f4 100644 --- a/reference/core/control-center/evidence/gap-closure-evidence.json +++ b/reference/core/control-center/evidence/gap-closure-evidence.json @@ -10899,7 +10899,7 @@ "END TO END ON THE BUILT core-api (npm run build in src/apps/core-api; NODE_ENV=production PORT=3011 EVOLITH_API_KEY=… CORE_PATH= node dist/main.js): the identical 150-file / 1,118,034-byte payload -> 200 with overallVerdict FAIL and gate-f1's six missing artifacts; a 3,146,294-byte body -> 413 PAYLOAD_TOO_LARGE 'Request body too large: 3146294 bytes received, the limit is 2097152 bytes (EVOLITH_MAX_BODY_BYTES).' with X-Problem-Format rfc9457.", "grep -n 'EVOLITH_MAX_BODY_BYTES' src/apps/core-api/src/infrastructure/config/env.validation.ts src/apps/core-api/src/main.ts src/apps/core-api/README.md", "EXPECTED OF THE PREVIOUS COMMAND: the variable declared once with a 2 MiB default, read once at bootstrap into useBodyParser('json') and useBodyParser('urlencoded'), and documented in the README's variable table", - "NOT CLAIMED: the UAT Tracker's evaluate-architecture returning a real verdict — the deployed image predates this commit until main is promoted and Coolify redeploys (the deploy job is off); the front page therefore shows the Tracker's phase gate, which never touches this path." + "MEASURED IN UAT AFTER THE PROMOTION, 2026-09-20 05:23 UTC (main 9c5deedf, run 35490911533, job 'Deploy UAT (Coolify)' green): POST https://tracker.beyondnet.cloud/api/v1/products/10c8006a-d2dc-43bf-9edd-18aec2c93391/evaluate-architecture -> HTTP 200, evaluation.status COMPLETED, resultDecision FAILED, provenance core, coreEndpoint /api/v1/evaluate, durationMs 174, fileCount 150 (branch main, 35 binaries and 1 oversize file skipped by the Tracker's own caps), responseResult.gates f1..f5 all 'failed'. The same call was HTTP 502 with provenance synthetic six hours earlier. The 'CD is off' half of the original row was wrong: VPS_DEPLOY_ENABLED has been true since 2026-08-22." ] } ] 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 6cf1dba95..c2a170936 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.es.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.es.md @@ -10285,7 +10285,7 @@ Los dos se arreglaron de forma estructural y no como correcciones: el rethrow no | un cuerpo de 6 ficheros (10 KB) | `200`, gate `gate-f1` `FAIL` con los seis artefactos ausentes — el camino inline en sí funciona | - **Lo que lo cierra, en `3b276c9a`:** `EVOLITH_MAX_BODY_BYTES` (2 MiB por defecto, validado en `env.validation.ts`) registra los parsers `json` y `urlencoded` explícitamente con `bodyParser: false`; el filtro reconoce los errores del body parser por su `type`/`status` antes de los matchers por mensaje y conserva el estado que eligió el parser (413, 400, 415…), el 413 dice los bytes recibidos, el techo y la variable; y todo 5xx se registra con su traza antes de enmascararse. Rojo primero: 4 de las 5 specs nuevas fallan contra el filtro anterior. Después: el mismo cuerpo de 1,1 MB → `200` con el veredicto de `gate-f1`; un cuerpo de 3 MB → `413 PAYLOAD_TOO_LARGE "Request body too large: 3146294 bytes received, the limit is 2097152 bytes (EVOLITH_MAX_BODY_BYTES)."` -- **Lo que esto no cierra:** la imagen del UAT es anterior al arreglo hasta que `main` se promueva y Coolify redespliegue (el job `deploy` sigue apagado, ver el compose de UAT); y el Tracker registra un veredicto `synthetic BLOCKED` ante un error HTTP, que es una cuestión de honestidad propia del Tracker y vive en su repositorio. +- **Lo que esto no cierra:** el Tracker registra un veredicto `synthetic BLOCKED` ante un error HTTP, que es una cuestión de honestidad propia del Tracker y vive en su repositorio. (Se registró como «la imagen del UAT es anterior al arreglo, CD apagado» — la segunda mitad era falsa: `VPS_DEPLOY_ENABLED` está en `true` desde el 2026-08-22 y cada promoción a `main` redespliega. Promovido en #778 (`9c5deedf`) y redesplegado por el job `Deploy UAT (Coolify)` de la corrida 35490911533 el 2026-09-20; medido justo después: la misma llamada `evaluate-architecture` responde `200`, `provenance: core`, `status: COMPLETED`, `resultDecision: FAILED` — un veredicto real sobre 150 ficheros (gates f1–f5 fallidos por artefactos de fase ausentes), 174 ms en el Core.) - **Casos de uso:** - Un tenant enlaza un producto a su repositorio y pide el veredicto de arquitectura; el Core evalúa el repositorio que recibió en vez de fallar por su tamaño. - Un cuerpo que sí supera el techo recibe un 413 que nombra el techo, así que el operador sube `EVOLITH_MAX_BODY_BYTES` en vez de leer un 500. @@ -10301,5 +10301,5 @@ Los dos se arreglaron de forma estructural y no como correcciones: el rethrow no - [x] El techo es configurable y está documentado. **CUMPLIDO** — `EVOLITH_MAX_BODY_BYTES` en `env.validation.ts` y en la tabla de variables del README de core-api. - [x] Un 5xx enmascarado se registra con su traza; un 4xx no. **CUMPLIDO** — dos specs, `Logger.prototype.error` observado una vez y cero veces. - [x] **FALSABILIDAD:** las specs están en rojo contra el filtro anterior. **CUMPLIDO** — 4 fallidas / 3 pasadas con `git show HEAD:…filter.ts` puesto, 7 pasadas después. - - [~] **NO RECLAMADO:** que el `evaluate-architecture` del Tracker UAT devuelva un veredicto real. Eso necesita la imagen promovida y redesplegada; queda escrito aquí para que la portada no lo diga, y no es de lo que trata esta fila — la fila es el techo del Core y su silencio, ambos medidos cerrados. + - [x] El `evaluate-architecture` del Tracker UAT devuelve un veredicto real. **CUMPLIDO tras la promoción** — Promovido en #778 (`9c5deedf`) y redesplegado por el job `Deploy UAT (Coolify)` de la corrida 35490911533 el 2026-09-20; medido justo después: la misma llamada `evaluate-architecture` responde `200`, `provenance: core`, `status: COMPLETED`, `resultDecision: FAILED` — un veredicto real sobre 150 ficheros (gates f1–f5 fallidos por artefactos de fase ausentes), 174 ms en el Core. La portada conserva la captura de la compuerta de fase; este veredicto queda registrado aquí y en `known-limitations`. - **Estado:** `COMPLETADO` diff --git a/reference/core/control-center/gaps/gap-reference-catalog.md b/reference/core/control-center/gaps/gap-reference-catalog.md index 1be75c8d4..c986b111e 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.md @@ -10378,7 +10378,7 @@ Both were fixed structurally rather than corrected: the rethrow now names BOTH f | a body of 6 files (10 KB) | `200`, gate `gate-f1` `FAIL` with the six missing artifacts — the inline path itself works | - **What closes it, in `3b276c9a`:** `EVOLITH_MAX_BODY_BYTES` (default 2 MiB, validated in `env.validation.ts`) registers the `json` and `urlencoded` parsers explicitly with `bodyParser: false`; the filter recognises body-parser errors by their `type`/`status` before the message matchers and keeps the parser's status (413, 400, 415…), the 413 states the bytes received, the ceiling and the variable; and every 5xx is logged with its stack before it is masked. Red first: 4 of the 5 new specs fail against the previous filter. After: the same 1.1 MB payload → `200` with the `gate-f1` verdict; a 3 MB body → `413 PAYLOAD_TOO_LARGE "Request body too large: 3146294 bytes received, the limit is 2097152 bytes (EVOLITH_MAX_BODY_BYTES)."` -- **What this does not close:** the UAT image predates the fix until `main` is promoted and Coolify redeploys (the `deploy` job is still off, see the UAT compose); and the Tracker records a `synthetic BLOCKED` verdict on an HTTP error, which is the Tracker's own honesty question and lives in its repository. +- **What this does not close:** the Tracker records a `synthetic BLOCKED` verdict on an HTTP error, which is the Tracker's own honesty question and lives in its repository. (Registered as "the UAT image predates the fix, CD off" — the second half was wrong: `VPS_DEPLOY_ENABLED` has been `true` since 2026-08-22 and every promotion to `main` redeploys. Promoted in #778 (`9c5deedf`) and redeployed by the `Deploy UAT (Coolify)` job of run 35490911533 on 2026-09-20; measured right after: the same `evaluate-architecture` call answers `200`, `provenance: core`, `status: COMPLETED`, `resultDecision: FAILED` — a real verdict on 150 files (gates f1–f5 failed for missing phase artifacts), 174 ms in the Core.) - **Use cases:** - A tenant links a product to its repository and asks for the architecture verdict; the Core evaluates the repository it received instead of failing on its size. - A body that does exceed the ceiling gets a 413 that names the ceiling, so the operator raises `EVOLITH_MAX_BODY_BYTES` instead of reading a 500. @@ -10394,5 +10394,5 @@ Both were fixed structurally rather than corrected: the rethrow now names BOTH f - [x] The ceiling is configurable and documented. **MET** — `EVOLITH_MAX_BODY_BYTES` in `env.validation.ts` and the core-api README's variable table. - [x] A masked 5xx is logged with its stack; a 4xx is not. **MET** — two specs, `Logger.prototype.error` observed once and zero times. - [x] **FALSIFIABILITY:** the specs are red against the previous filter. **MET** — 4 failed / 3 passed with `git show HEAD:…filter.ts` in place, 7 passed after. - - [~] **NOT CLAIMED:** the UAT Tracker's `evaluate-architecture` returning a real verdict. That needs the promoted image redeployed; recorded here so the front page does not say it, and it is not what this row is about — the row is the Core's ceiling and its silence, both measured closed. + - [x] The UAT Tracker's `evaluate-architecture` returns a real verdict. **MET after the promotion** — Promoted in #778 (`9c5deedf`) and redeployed by the `Deploy UAT (Coolify)` job of run 35490911533 on 2026-09-20; measured right after: the same `evaluate-architecture` call answers `200`, `provenance: core`, `status: COMPLETED`, `resultDecision: FAILED` — a real verdict on 150 files (gates f1–f5 failed for missing phase artifacts), 174 ms in the Core. The front page keeps the phase-gate capture; this verdict is recorded here and in `known-limitations`. - **Status:** `DONE` diff --git a/reference/core/control-center/gaps/gap-tracking.es.md b/reference/core/control-center/gaps/gap-tracking.es.md index 1c9203587..caaab59dc 100644 --- a/reference/core/control-center/gaps/gap-tracking.es.md +++ b/reference/core/control-center/gaps/gap-tracking.es.md @@ -26,7 +26,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-715`](./gap-reference-catalog.es.md#gt-715) | **La Core API rechazaba cualquier contexto de evaluación inline de más de 100 KB con un 500 enmascarado y sin línea de log, así que la llamada de conformidad del repositorio del Tracker nunca funcionó contra un repositorio real.** Medido el 2026-09-20 desde el entorno UAT del Tracker: `POST /products/{id}/evaluate-architecture` sobre un producto que apunta a este repositorio leyó 150 ficheros (~1 MB) de GitHub y se los envió al Core, que respondió `500 INTERNAL_ERROR "An unexpected error occurred"`; el Tracker registró un `synthetic BLOCKED`. Reproducido sobre la misma imagen (`main@142b8324`): 14 ficheros / cuerpo de 99.797 bytes → `200`, 15 ficheros / 101.578 bytes → `500`; un cuerpo sintético de 92.956 bytes → `200`, de 126.556 → `500`. El límite de 100 KB por omisión de Express para json, lanzado como un `PayloadTooLargeError` que no es `HttpException`, clasificado por mensaje, enmascarado para el cable y escrito en ninguna parte. **CERRADO 2026-09-20** en `3b276c9a`: `EVOLITH_MAX_BODY_BYTES` (2 MiB por defecto) registra los parsers explícitamente, los errores del body parser conservan el estado del parser y el 413 nombra los dos tamaños y la variable, y todo 5xx enmascarado se registra con su traza. Rojo primero: 4 de las 5 specs nuevas fallan sobre el filtro viejo; después: el mismo cuerpo de 1,1 MB → `200` con el veredicto de `gate-f1`, 3 MB → `413`. La imagen del UAT es anterior al arreglo hasta que se redespliegue la siguiente promoción, y por eso la portada lleva la compuerta de fase y no el veredicto sobre el repositorio. | El Tracker no podía obtener un veredicto de arquitectura sobre ningún repositorio real: el Core rechazaba la petición por su tamaño y no le decía nada útil a nadie. | El Core evalúa un repositorio real enviado inline, rechaza con una razón que nombra cuando debe, y deja una traza que el operador puede leer. | `Core API` | Cross | P1 | S | `COMPLETADO` | +| [`GT-715`](./gap-reference-catalog.es.md#gt-715) | **La Core API rechazaba cualquier contexto de evaluación inline de más de 100 KB con un 500 enmascarado y sin línea de log, así que la llamada de conformidad del repositorio del Tracker nunca funcionó contra un repositorio real.** Medido el 2026-09-20 desde el entorno UAT del Tracker: `POST /products/{id}/evaluate-architecture` sobre un producto que apunta a este repositorio leyó 150 ficheros (~1 MB) de GitHub y se los envió al Core, que respondió `500 INTERNAL_ERROR "An unexpected error occurred"`; el Tracker registró un `synthetic BLOCKED`. Reproducido sobre la misma imagen (`main@142b8324`): 14 ficheros / cuerpo de 99.797 bytes → `200`, 15 ficheros / 101.578 bytes → `500`; un cuerpo sintético de 92.956 bytes → `200`, de 126.556 → `500`. El límite de 100 KB por omisión de Express para json, lanzado como un `PayloadTooLargeError` que no es `HttpException`, clasificado por mensaje, enmascarado para el cable y escrito en ninguna parte. **CERRADO 2026-09-20** en `3b276c9a`: `EVOLITH_MAX_BODY_BYTES` (2 MiB por defecto) registra los parsers explícitamente, los errores del body parser conservan el estado del parser y el 413 nombra los dos tamaños y la variable, y todo 5xx enmascarado se registra con su traza. Rojo primero: 4 de las 5 specs nuevas fallan sobre el filtro viejo; después: el mismo cuerpo de 1,1 MB → `200` con el veredicto de `gate-f1`, 3 MB → `413`. Promovido en #778 (`9c5deedf`) y redesplegado por el job `Deploy UAT (Coolify)` de la corrida 35490911533 el 2026-09-20; medido justo después: la misma llamada `evaluate-architecture` responde `200`, `provenance: core`, `status: COMPLETED`, `resultDecision: FAILED` — un veredicto real sobre 150 ficheros (gates f1–f5 fallidos por artefactos de fase ausentes), 174 ms en el Core. La portada conserva la captura de la compuerta de fase. | El Tracker no podía obtener un veredicto de arquitectura sobre ningún repositorio real: el Core rechazaba la petición por su tamaño y no le decía nada útil a nadie. | El Core evalúa un repositorio real enviado inline, rechaza con una razón que nombra cuando debe, y deja una traza que el operador puede leer. | `Core API` | Cross | P1 | S | `COMPLETADO` | | [`GT-714`](./gap-reference-catalog.es.md#gt-714) | **`gate evaluate` y `phase advance` en la CLI publicada necesitan un checkout de este repositorio en disco, porque el tarball trae las reglas pero no las definiciones de gate.** Medido el 2026-09-20 con `@beyondnet/evolith-cli@1.3.2` en un contenedor `node:20` limpio sobre un satélite recién salido de `init`: sin `--core` las dos órdenes salen con `1` y `ENOENT … reference/governance/sdlc/gates` (el tarball trae `rulesets/sdlc/phase-gates.rules.json` y el registro de artefactos en las rutas propias del paquete, pero el validador compone `/reference/governance/sdlc/gates` y `/src/rulesets/sdlc/artifact-registry.json`, y `findCorePath` cae en el propio satélite); con `--core ../evolith` las dos salen con `2` y el veredicto real del gate. GT-705 arregló el mismo defecto para el paquete MCP empaquetando los dos árboles e instalando un único resolutor; el paquete de la CLI no entró en ese cambio, y `sdlc gate-status` (sub-hallazgo de GT-461) no tiene `--core` en absoluto. Hasta que aterrice, la portada muestra las dos órdenes con `--core ../evolith` y lo explica en el pie. | Las dos órdenes que hacen de Evolith algo más que un linter no corren desde el paquete publicado sin un clon de este repositorio junto al proyecto. | `npx -y @beyondnet/evolith-cli gate evaluate --phase discovery` sobre un satélite nuevo da el veredicto, sin opción, y la portada pierde su salvedad. | `Evolith CLI` | Cross | P1 | S | `PENDIENTE` | | [`GT-713`](./gap-reference-catalog.es.md#gt-713) | **El análisis al que están ligadas las alertas de la pestaña Security solo lo produce la corrida `push` de `sdk-cli-ci.yml`, y su filtro de rutas saltaba casi todo el código.** El job `CodeQL SAST` es el único que sube el análisis `/language:javascript-typescript` para `refs/heads/main`; las corridas de pull request son diff-informed (se recortan al diff y nunca mueven las alertas de la rama) y la configuración por defecto "Code Quality" es otra suite. El trigger `push` estaba filtrado a `src/sdk/cli/**`, `.harness/**` y los lockfiles. Medido el 2026-09-19: la promoción `19d736da` (cambios solo bajo `src/packages` y `src/apps`) llegó a `main` sin análisis alguno, así que la pestaña siguió mostrando 10 alertas sobre código que ya no existía; `c5547114` hizo lo mismo 40 minutos después. Las dos necesitaron `gh workflow run sdk-cli-ci.yml --ref main` a mano. **CERRADO 2026-09-19** en `72aceb70`: el filtro cubre ahora `src/packages/**` y `src/apps/**` — todo lo que CodeQL escanea — y un push solo de documentación sigue sin gastar la corrida. | El escáner que decide qué muestra la pestaña Security no volvía a correr cuando cambiaba la mayor parte del código, así que la pestaña describía el commit anterior. | Una promoción de código a `main` re-analiza `main`; la pestaña está al día sin que nadie tenga que acordarse de lanzarla. | `Infra` | Cross | P2 | XS | `COMPLETADO` | | [`GT-712`](./gap-reference-catalog.es.md#gt-712) | **De las 95 alertas de la pestaña Security, 37 eran reales: una línea de shell construida con entrada del tool MCP, rutas de fichero tomadas tal cual de tres cuerpos HTTP, un escritor de configuración que llegaba a `Object.prototype`, ocho regex polinómicos y siete workflows con un token por defecto con escritura.** Triadas desde el SARIF del último análisis de CodeQL sobre `main` (sus `codeFlows` nombran el controlador o tool por el que entra cada taint), no desde la pestaña. CWE-78: `NxWorkspaceStrategy` interpolaba `apiName`/`hostName`/`remotes`/`domains` — argumentos sin validar del tool MCP `evolith-scaffold` — en `npx nx g … --name=… --directory=apps/…` a través de un shell; `git-log-reader` hacía lo mismo con `--since`. CWE-22: `POST /evaluate` (rama legacy) pasaba `body.satellitePath` y `body.corePath` al pipeline tal cual — la propia descripción del DTO decía "Path traversal validation required before use" — así que un llamador autenticado podía evaluar cualquier directorio del host; `POST /architecture/validate-satellite` dejaba que el manifest pisara el workspace resuelto; `POST /projects/initialize` construía `${cwd}/${name}` con un `name` sin validar. CWE-1321: `evolith-config-set` recorría una ruta de puntos sin guardar `__proto__`/`constructor`/`prototype`. Ocho regex `/\/+$/`, `/=+$/` y de recorte de separadores eran polinómicos — medidos en 15 s con 200k barras. Siete workflows no tenían `permissions:` de nivel superior (Scorecard Token-Permissions). **CERRADO 2026-09-19** en `cb2c8a1e` (#725), `8920b140` (#737) y `b200c5cb` (#748): la estrategia ejecuta sin shell vía `executeFileOrThrow` — nuevo en el puerto `ICommandExecutor`, implementado por el ejecutor del CLI y el del MCP — y cada nombre debe cumplir `^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$`; `WorkspaceReferenceResolverService.resolveLegacyPath`/`resolveCorePathOverride` contienen las rutas legacy bajo `WORKSPACE_ROOT`/`CORE_PATH` y una instancia sin resolver falla cerrada; las rutas del manifest se fijan a las resueltas; `name` es un solo segmento de directorio en el DTO y en el caso de uso; el escritor de configuración rechaza los tres segmentos de prototipo en la escritura; los regex son recortes carácter a carácter (la forma que ya usaba `codeowners.ts`); los siete workflows declaran `contents: read` arriba y cada escritura en su job. Las 58 alertas restantes quedan descartadas cada una con su motivo escrito (SHA-256 como normalización de longitud antes de `timingSafeEqual`, cadenas de autenticación con comparación en tiempo constante, fixtures de test, `.wasm` compilados del Rego contiguo con `opa-parity.yml`, `npm install -g` de nuestra propia versión exacta). Recuento final en `c5547114`: CodeQL 0, Scorecard 0, Dependabot 0, secret-scanning 0. | Un agente manejando el tool de scaffold del MCP, o un cliente de la API REST del Core con una clave válida, podía ejecutar comandos de shell o leer directorios del host del Core. | Las 37 alertas reales se arreglan en el origen y las verifica CodeQL sobre `main`; las 58 que no lo eran llevan el motivo en la pestaña para que nadie las vuelva a triar. | `Core` | Cross | P0 | M | `COMPLETADO` | diff --git a/reference/core/control-center/gaps/gap-tracking.md b/reference/core/control-center/gaps/gap-tracking.md index 791a25c21..425645b6f 100644 --- a/reference/core/control-center/gaps/gap-tracking.md +++ b/reference/core/control-center/gaps/gap-tracking.md @@ -26,7 +26,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-715`](./gap-reference-catalog.md#gt-715) | **The Core API rejected any inline evaluation context over 100 KB with a masked 500 and no log line, so the Tracker's repository-conformance call never worked against a real repository.** Measured 2026-09-20 from the Tracker's UAT environment: `POST /products/{id}/evaluate-architecture` on a product pointing at this repository read 150 files (~1 MB) from GitHub and posted them to the Core, which answered `500 INTERNAL_ERROR "An unexpected error occurred"`; the Tracker recorded a `synthetic BLOCKED`. Reproduced on the same image (`main@142b8324`): 14 files / 99,797-byte body → `200`, 15 files / 101,578 bytes → `500`; a synthetic 92,956-byte body → `200`, 126,556 → `500`. Express's 100 KB json default, thrown as a `PayloadTooLargeError` that is not an `HttpException`, classified by message, masked for the wire and written nowhere. **CLOSED 2026-09-20** in `3b276c9a`: `EVOLITH_MAX_BODY_BYTES` (2 MiB by default) registers the parsers explicitly, body-parser errors keep the parser's status and the 413 names both sizes and the variable, and every masked 5xx is logged with its stack. Red first: 4 of 5 new specs fail on the old filter; after: the same 1.1 MB payload → `200` with the `gate-f1` verdict, 3 MB → `413`. The UAT image predates the fix until the next promotion is redeployed, which is why the front page carries the phase gate and not the repository verdict. | The Tracker could not get an architecture verdict on any real repository: the Core refused the request for its size and said nothing useful to anyone. | The Core evaluates a real repository sent inline, refuses with a reason it names when it must, and leaves a trace the operator can read. | `Core API` | Cross | P1 | S | `DONE` | +| [`GT-715`](./gap-reference-catalog.md#gt-715) | **The Core API rejected any inline evaluation context over 100 KB with a masked 500 and no log line, so the Tracker's repository-conformance call never worked against a real repository.** Measured 2026-09-20 from the Tracker's UAT environment: `POST /products/{id}/evaluate-architecture` on a product pointing at this repository read 150 files (~1 MB) from GitHub and posted them to the Core, which answered `500 INTERNAL_ERROR "An unexpected error occurred"`; the Tracker recorded a `synthetic BLOCKED`. Reproduced on the same image (`main@142b8324`): 14 files / 99,797-byte body → `200`, 15 files / 101,578 bytes → `500`; a synthetic 92,956-byte body → `200`, 126,556 → `500`. Express's 100 KB json default, thrown as a `PayloadTooLargeError` that is not an `HttpException`, classified by message, masked for the wire and written nowhere. **CLOSED 2026-09-20** in `3b276c9a`: `EVOLITH_MAX_BODY_BYTES` (2 MiB by default) registers the parsers explicitly, body-parser errors keep the parser's status and the 413 names both sizes and the variable, and every masked 5xx is logged with its stack. Red first: 4 of 5 new specs fail on the old filter; after: the same 1.1 MB payload → `200` with the `gate-f1` verdict, 3 MB → `413`. Promoted in #778 (`9c5deedf`) and redeployed by the `Deploy UAT (Coolify)` job of run 35490911533 on 2026-09-20; measured right after: the same `evaluate-architecture` call answers `200`, `provenance: core`, `status: COMPLETED`, `resultDecision: FAILED` — a real verdict on 150 files (gates f1–f5 failed for missing phase artifacts), 174 ms in the Core. The front page keeps the phase-gate capture. | The Tracker could not get an architecture verdict on any real repository: the Core refused the request for its size and said nothing useful to anyone. | The Core evaluates a real repository sent inline, refuses with a reason it names when it must, and leaves a trace the operator can read. | `Core API` | Cross | P1 | S | `DONE` | | [`GT-714`](./gap-reference-catalog.md#gt-714) | **`gate evaluate` and `phase advance` in the published CLI need a checkout of this repository on disk, because the tarball carries the rules but not the gate definitions.** Measured 2026-09-20 with `@beyondnet/evolith-cli@1.3.2` in a clean `node:20` container on a satellite fresh from `init`: without `--core` both commands exit `1` with `ENOENT … reference/governance/sdlc/gates` (the tarball ships `rulesets/sdlc/phase-gates.rules.json` and the artifact registry at the package's own paths, but the validator composes `/reference/governance/sdlc/gates` and `/src/rulesets/sdlc/artifact-registry.json`, and `findCorePath` falls back to the satellite itself); with `--core ../evolith` both exit `2` with the real gate verdict. GT-705 fixed the same defect for the MCP package by bundling both trees and installing one resolver; the CLI package was not part of that change, and `sdlc gate-status` (GT-461 sub-finding) has no `--core` at all. Until it lands, the front page shows the two commands with `--core ../evolith` and says why in the caption. | The two commands that make Evolith more than a linter do not run from the published package without a clone of this repository next to the project. | `npx -y @beyondnet/evolith-cli gate evaluate --phase discovery` on a fresh satellite gives the verdict, no flag, and the front page loses its caveat. | `Evolith CLI` | Cross | P1 | S | `PENDING` | | [`GT-713`](./gap-reference-catalog.md#gt-713) | **The analysis that the Security tab's alerts are keyed to is produced only by the `push` run of `sdk-cli-ci.yml`, and its path filter skipped most of the code.** The `CodeQL SAST` job is the sole uploader of the `/language:javascript-typescript` analysis for `refs/heads/main`; pull-request runs are diff-informed (they prune to the diff and never move the branch's alerts) and the default "Code Quality" setup is a different suite. The `push` trigger was filtered to `src/sdk/cli/**`, `.harness/**` and the lockfiles. Measured 2026-09-19: promotion `19d736da` (changes under `src/packages` and `src/apps` only) reached `main` with no analysis at all, so the tab kept reporting 10 alerts on code that no longer existed; `c5547114` did the same 40 minutes later. Both needed `gh workflow run sdk-cli-ci.yml --ref main` by hand. **CLOSED 2026-09-19** in `72aceb70`: the filter now spans `src/packages/**` and `src/apps/**` — every tree CodeQL scans — while a docs-only push still skips the run. | The scanner that decides what the Security tab shows was not re-run when most of the code changed, so the tab described the previous commit. | A code promotion to `main` re-analyses `main`; the tab is current without anyone remembering to dispatch it. | `Infra` | Cross | P2 | XS | `DONE` | | [`GT-712`](./gap-reference-catalog.md#gt-712) | **Of the 95 alerts on the Security tab, 37 were real: a shell line built from MCP tool input, filesystem paths taken verbatim from three HTTP bodies, a config writer that reached `Object.prototype`, eight polynomial regexes and seven workflows with a write-capable default token.** Triaged from the SARIF of the last CodeQL analysis on `main` (its `codeFlows` name the controller or tool each taint enters from), not from the tab. CWE-78: `NxWorkspaceStrategy` interpolated `apiName`/`hostName`/`remotes`/`domains` — unvalidated arguments of the MCP tool `evolith-scaffold` — into `npx nx g … --name=… --directory=apps/…` through a shell; `git-log-reader` did the same with `--since`. CWE-22: `POST /evaluate` (legacy branch) passed `body.satellitePath` and `body.corePath` to the pipeline as-is — the DTO's own description said "Path traversal validation required before use" — so an authenticated caller could evaluate any directory on the host; `POST /architecture/validate-satellite` let the manifest override the resolved workspace; `POST /projects/initialize` built `${cwd}/${name}` from an unvalidated `name`. CWE-1321: `evolith-config-set` walked a dot-path with no guard on `__proto__`/`constructor`/`prototype`. Eight `/\/+$/`, `/=+$/` and separator-trim regexes were polynomial — measured at 15 s on 200k slashes. Seven workflows had no top-level `permissions:` (Scorecard Token-Permissions). **CLOSED 2026-09-19** in `cb2c8a1e` (#725), `8920b140` (#737) and `b200c5cb` (#748): the strategy runs shell-free through `executeFileOrThrow` — new on the `ICommandExecutor` port, implemented by the CLI executor and the MCP one — and every name must match `^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$`; `WorkspaceReferenceResolverService.resolveLegacyPath`/`resolveCorePathOverride` contain the legacy paths under `WORKSPACE_ROOT`/`CORE_PATH` and an instance without a resolver fails closed; the manifest's paths are pinned to the resolved ones; `name` is one directory segment in the DTO and in the use case; the config writer refuses the three prototype segments at the write; the regexes are char-based trims (the shape `codeowners.ts` already used); the seven workflows declare `contents: read` at the top and each write on its job. The 58 remaining alerts are dismissed with a written reason each (SHA-256 as length normalisation before `timingSafeEqual`, auth chains with constant-time comparison, test fixtures, `.wasm` compiled from adjacent Rego with `opa-parity.yml`, `npm install -g` of our own exact version). Final count on `c5547114`: CodeQL 0, Scorecard 0, Dependabot 0, secret-scanning 0. | An agent driving the MCP scaffold tool, or a client of the Core's REST API with a valid key, could run shell commands or read directories on the Core host. | The 37 real alerts are fixed at the source and verified by CodeQL on `main`; the 58 that were not real carry the reason in the tab so nobody re-triages them. | `Core` | Cross | P0 | M | `DONE` |