Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 69 additions & 0 deletions reference/core/control-center/gaps/gap-reference-catalog.es.md

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions reference/core/control-center/gaps/gap-reference-catalog.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions reference/core/control-center/gaps/gap-tracking.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -720,3 +720,5 @@ Este tablero es la única fuente de verdad para deuda técnica, gaps, oportunida

---
[Volver al Índice de Visión](../../README.es.md)
| [`GT-648`](./gap-reference-catalog.es.md#gt-648) | **El Core desplegado vuelve a leer y revalidar todo su corpus de rulesets en cada `POST /api/v1/evaluate`, bloqueando el event loop de Node mientras lo hace.** `RuleEvaluationEngine.discoverAndEvaluate` llama a `rulesetRepo.loadAllRulesets(corePath)` en cada evaluación, y el `IRulesetRepository` de core-api es un `DiskRulesetRepository` pelado sin caché — así que un recorrido de directorios sobre 176 ficheros `*.rules.json` más una pasada de Ajv que produce 393 reglas normalizadas corre por petición. Ambas mitades son trabajo de CPU síncrono dentro de llamadas `await`, así que nada más en el loop avanza mientras corren. **Observado en la corrida de CI `30631939687` (workflow Reliability, artefactos `k6-smoke.json` + `core-api.log`):** el bloque de WARN de carga del corpus se repite una vez por iteración de k6 (~1/s) — una carga en arranque lo imprimiría una sola vez — y **un `GET /health` que cayó durante ese trabajo tardó 498,5 ms extremo a extremo mientras core-api registraba su propio handler en `durationMs=0`**, con `http_req_waiting` 498,4 ms contra `http_req_connecting` 0,17 ms. La petición nunca fue lenta; estaba encolada tras un loop bloqueado. **Reproducido localmente el 2026-07-31 contra `origin/develop` en `3051e499`:** once evaluaciones produjeron exactamente once cargas del corpus (33 WARN de ruleset a 3 por carga, 11 WARN de fases a 1 por carga), y una llamada a `loadAllRulesets` se midió en 30,4–54,2 ms con un temporizador de 10 ms encolado esperando hasta 14 ms. Esa cifra de laptop es más o menos una décima de la del runner, y la distancia entre ambas es justamente el punto: el mismo defecto sobre E/S más lenta y disputada es lo que produjo 498 ms. **También por petición, y agravándolo:** `SdlcDataLoaderService.loadAllGates` relee el índice de fases una vez por fase (`loadPhase` → `loadAllPhases`), convirtiendo un escaneo en N+1. **No lo atiende el arreglo del umbral de k6** que asevera la mediana en vez de un p95 de 10 muestras — eso detiene el flake que bloqueaba CI y deja este defecto en pie a propósito; es la razón de que `/health` tenga una cola de cientos de ms con 1 VU. | `Core API` | Cross | P1 | S | `PENDIENTE` |
| [`GT-649`](./gap-reference-catalog.es.md#gt-649) | **El corpus de evaluación carga tres documentos que el loader reporta como rotos en cada carga, una etapa de gates de fase que no evalúa nada desde el 2026-07-04, y un logger que duplica cada línea que escribe.** Tres fallos afloraron juntos en el mismo `core-api.log` de la corrida de CI `30631939687`, y cada uno es real por separado. **(1) Tres ficheros `*.rules.json` fallan el schema estándar de ruleset con `must have required property 'rules' / 'principles'` y se saltan siempre** — `architecture/topology-recommendation.rules.json` (el catálogo de recomendación de ADR-0104, que tiene su propio schema y su propio consumidor en `TopologyRecommendationService`), y `infrastructure/helm-enforcement.rules.json` + `infrastructure/opa-sidecar-bundle.rules.json` (declaraciones de una sola regla, aplicadas por el guard `29-validate-opa-sidecar-bundles.mjs` y su Rego pareado, no por el motor de reglas). No tenían nada malo: son tipos de documento distintos que comparten sufijo, y el loader no tenía vocabulario para decirlo. Los tres además fallaban el schema que *declaran*, sin que nadie lo notara, porque nunca se los verificó contra él — `severity: "error"` no está en el enum de ese schema, `name` no es su campo (`title` sí), el bloque `validation` que nunca modeló, y una ruta `$schema` que la reorganización de `reference/` dejó obsoleta. **(2) `reference/governance/sdlc/phases/` no existe**, borrado por `f0d01911` (2026-07-04, *"remove leftover old directories under reference/"*) que eliminó todo el árbol `reference/governance/`; `GT-461` recreó después `gates/` en esa misma ruta y no `phases/`. Como `loadGatesForPhase` resuelve los gates *a través* del índice de fases, que `loadAllPhases()` devuelva `[]` significa que **los cinco ficheros de gate escritos han sido inalcanzables durante ~4 semanas** — la etapa de gates de fase de `SatelliteEvaluationPipeline` producía cero resultados sin reportar error alguno, de modo que una evaluación gobernada se saltaba en silencio la mitad del pipeline donde viven los gates del SDLC. **(3) `NestLoggerProvider` reenvía su `context` opcional sin condición**, y el `Logger` de Nest lee `...optionalParams` por posición, así que `warn(msg, undefined)` no es `warn(msg)`: cada log de un solo argumento del dominio emitía una línea `WARN undefined` pareada. Medido en la reproducción base: **44 líneas de esas en 11 peticiones**, duplicando el volumen de todo lo que el Core escribe. | `Core Domain` | Cross | P1 | S | `PENDIENTE` |
2 changes: 2 additions & 0 deletions reference/core/control-center/gaps/gap-tracking.md
Original file line number Diff line number Diff line change
Expand Up @@ -742,3 +742,5 @@ This board is the single source of truth for technical debt, gaps, opportunities

---
[Back to Vision Index](../README.md)
| [`GT-648`](./gap-reference-catalog.md#gt-648) | **The deployed Core re-reads and re-validates its entire ruleset corpus on every `POST /api/v1/evaluate`, blocking the Node event loop for the duration.** `RuleEvaluationEngine.discoverAndEvaluate` calls `rulesetRepo.loadAllRulesets(corePath)` on each evaluation, and core-api's `IRulesetRepository` is a bare `DiskRulesetRepository` with no cache — so a directory walk over 176 `*.rules.json` files plus an Ajv pass producing 393 normalized rules runs per request. Both halves are synchronous CPU work inside `await`ed calls, so nothing else on the loop advances while they run. **Observed in CI run `30631939687` (Reliability workflow, artifacts `k6-smoke.json` + `core-api.log`):** the corpus's load-time WARN block repeats once per k6 iteration (~1/s) — a boot-time load would print it once — and **a `GET /health` that landed during that work took 498.5 ms end-to-end while core-api logged its own handler at `durationMs=0`**, with `http_req_waiting` 498.4 ms against `http_req_connecting` 0.17 ms. The request was never slow; it was queued behind a blocked loop. **Reproduced locally 2026-07-31 against `origin/develop` at `3051e499`:** eleven evaluations produced exactly eleven corpus loads (33 ruleset WARNs at 3/load, 11 phase WARNs at 1/load), and one `loadAllRulesets` call was measured at 30.4–54.2 ms with a queued 10 ms timer waiting up to 14 ms. That laptop figure is roughly a tenth of the runner's, and the gap between the two is the point: the same defect on slower, contended I/O is what produced 498 ms. **Also per request, and compounding it:** `SdlcDataLoaderService.loadAllGates` re-reads the phase index once per phase (`loadPhase` → `loadAllPhases`), turning one scan into N+1. **Not addressed by the k6 threshold fix** that asserts the median instead of a 10-sample p95 — that stops the flake gating CI and deliberately leaves this defect in place; it is why `/health` has a multi-hundred-ms tail at 1 VU. | `Core API` | Cross | P1 | S | `PENDING` |
| [`GT-649`](./gap-reference-catalog.md#gt-649) | **The evaluation corpus carries three documents the loader reports as broken on every load, a phase-gate stage that has evaluated nothing since 2026-07-04, and a logger that doubles every line it writes.** Three faults surfaced together in the same `core-api.log` of CI run `30631939687`, and each is independently real. **(1) Three `*.rules.json` files fail the standard ruleset schema with `must have required property 'rules' / 'principles'` and are skipped every time** — `architecture/topology-recommendation.rules.json` (the ADR-0104 recommendation catalogue, which has its own schema and its own consumer in `TopologyRecommendationService`), and `infrastructure/helm-enforcement.rules.json` + `infrastructure/opa-sidecar-bundle.rules.json` (single-rule declarations enforced by guard `29-validate-opa-sidecar-bundles.mjs` and their paired Rego, not by the rule engine). Nothing was wrong with them: they are different document kinds sharing a suffix, and the loader had no vocabulary to say so. All three additionally failed the schema they *declare*, unnoticed, because nothing ever checked them against it — `severity: "error"` is not in that schema's enum, `name` is not its field (`title` is), the `validation` block it never modelled, and a `$schema` path left stale by the `reference/` reorganisation. **(2) `reference/governance/sdlc/phases/` does not exist**, deleted by `f0d01911` (2026-07-04, *"remove leftover old directories under reference/"*) which removed the whole `reference/governance/` tree; `GT-461` later re-created `gates/` at that same path and not `phases/`. Because `loadGatesForPhase` resolves gates *through* the phase index, `loadAllPhases()` returning `[]` means **every one of the five authored gate files has been unreachable for ~4 weeks** — the phase-gate stage of `SatelliteEvaluationPipeline` produced zero gate results while reporting no error, so a governed evaluation silently skipped the half of the pipeline the SDLC gates live in. **(3) `NestLoggerProvider` forwards its optional `context` unconditionally**, and Nest's `Logger` reads `...optionalParams` positionally, so `warn(msg, undefined)` is not `warn(msg)`: every single-argument log from the domain emitted a paired `WARN undefined` line. Measured on the baseline reproduction: **44 such lines across 11 requests**, doubling the volume of everything the Core writes. | `Core Domain` | Cross | P1 | S | `PENDING` |
Original file line number Diff line number Diff line change
Expand Up @@ -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 | `Cross` tiene la mayor carga ponderada abierta. | [GT-435](../gaps/gap-reference-catalog.es.md#gt-435), [GT-448](../gaps/gap-reference-catalog.es.md#gt-448) |
| 3 | Ganancias rápidas | Alta criticidad con complejidad XS/S. | - |
| 4 | Ola P1 | Endurecimiento siguiente después de limpiar P0. | [GT-324](../gaps/gap-reference-catalog.es.md#gt-324), [GT-578](../gaps/gap-reference-catalog.es.md#gt-578), [GT-605](../gaps/gap-reference-catalog.es.md#gt-605), [GT-583](../gaps/gap-reference-catalog.es.md#gt-583), [GT-585](../gaps/gap-reference-catalog.es.md#gt-585), [GT-448](../gaps/gap-reference-catalog.es.md#gt-448) |
| 3 | Ganancias rápidas | Alta criticidad con complejidad XS/S. | [GT-648](../gaps/gap-reference-catalog.es.md#gt-648), [GT-649](../gaps/gap-reference-catalog.es.md#gt-649) |
| 4 | Ola P1 | Endurecimiento siguiente después de limpiar P0. | [GT-648](../gaps/gap-reference-catalog.es.md#gt-648), [GT-649](../gaps/gap-reference-catalog.es.md#gt-649), [GT-324](../gaps/gap-reference-catalog.es.md#gt-324), [GT-578](../gaps/gap-reference-catalog.es.md#gt-578), [GT-605](../gaps/gap-reference-catalog.es.md#gt-605), [GT-583](../gaps/gap-reference-catalog.es.md#gt-583), [GT-585](../gaps/gap-reference-catalog.es.md#gt-585), [GT-448](../gaps/gap-reference-catalog.es.md#gt-448) |
| 5 | P2/P3 | Solo después de estabilizar seguridad, CI, reglas y contratos. | [GT-622](../gaps/gap-reference-catalog.es.md#gt-622), [GT-444](../gaps/gap-reference-catalog.es.md#gt-444), [GT-464](../gaps/gap-reference-catalog.es.md#gt-464), [GT-531](../gaps/gap-reference-catalog.es.md#gt-531), [GT-536](../gaps/gap-reference-catalog.es.md#gt-536), [GT-592](../gaps/gap-reference-catalog.es.md#gt-592), +8 |

## Bloqueadores Actuales
Expand All @@ -42,13 +42,13 @@ La forma correcta de usar este resumen es simple: si necesitas contexto, abre so
| Indicador | Valor |
|---|---:|
| Fecha canónica del tablero | 2026-07-26 |
| Gaps totales | 645 |
| Gaps totales | 647 |
| Gaps cerrados | 621 |
| Gaps pendientes | 24 |
| Gaps pendientes | 26 |
| P0 abiertos | 1 |
| P1 abiertos | 6 |
| P1 abiertos | 8 |
| P2 abiertos | 14 |
| Cierre total | 96.3% |
| Cierre total | 96% |
| Registros de evidencia de cierre | 603 |
| Readiness registrado | 4 PASS |

Expand All @@ -58,7 +58,7 @@ La forma correcta de usar este resumen es simple: si necesitas contexto, abre so
| `Governance` | 6 | 0 | 2 | [GT-578](../gaps/gap-reference-catalog.es.md#gt-578), [GT-585](../gaps/gap-reference-catalog.es.md#gt-585), [GT-599](../gaps/gap-reference-catalog.es.md#gt-599), [GT-639](../gaps/gap-reference-catalog.es.md#gt-639), +2 |
| `Evolith Core` | 4 | 0 | 1 | [GT-583](../gaps/gap-reference-catalog.es.md#gt-583), [GT-590](../gaps/gap-reference-catalog.es.md#gt-590), [GT-594](../gaps/gap-reference-catalog.es.md#gt-594), [GT-600](../gaps/gap-reference-catalog.es.md#gt-600) |
| `Infra` | 3 | 0 | 1 | [GT-324](../gaps/gap-reference-catalog.es.md#gt-324), [GT-622](../gaps/gap-reference-catalog.es.md#gt-622), [GT-464](../gaps/gap-reference-catalog.es.md#gt-464) |
| `Evolith Suite` | 1 | 0 | 1 | [GT-605](../gaps/gap-reference-catalog.es.md#gt-605) |
| `Core API` | 2 | 0 | 1 | [GT-648](../gaps/gap-reference-catalog.es.md#gt-648), [GT-522](../gaps/gap-reference-catalog.es.md#gt-522) |

## Fuente y Regla de Actualización

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 | `Cross` has the largest weighted open load. | [GT-435](../gaps/gap-reference-catalog.md#gt-435), [GT-448](../gaps/gap-reference-catalog.md#gt-448) |
| 3 | Quick wins | High criticality with XS/S complexity. | - |
| 4 | P1 wave | Next hardening after P0 is cleared. | [GT-324](../gaps/gap-reference-catalog.md#gt-324), [GT-578](../gaps/gap-reference-catalog.md#gt-578), [GT-605](../gaps/gap-reference-catalog.md#gt-605), [GT-583](../gaps/gap-reference-catalog.md#gt-583), [GT-585](../gaps/gap-reference-catalog.md#gt-585), [GT-448](../gaps/gap-reference-catalog.md#gt-448) |
| 3 | Quick wins | High criticality with XS/S complexity. | [GT-648](../gaps/gap-reference-catalog.md#gt-648), [GT-649](../gaps/gap-reference-catalog.md#gt-649) |
| 4 | P1 wave | Next hardening after P0 is cleared. | [GT-648](../gaps/gap-reference-catalog.md#gt-648), [GT-649](../gaps/gap-reference-catalog.md#gt-649), [GT-324](../gaps/gap-reference-catalog.md#gt-324), [GT-578](../gaps/gap-reference-catalog.md#gt-578), [GT-605](../gaps/gap-reference-catalog.md#gt-605), [GT-583](../gaps/gap-reference-catalog.md#gt-583), [GT-585](../gaps/gap-reference-catalog.md#gt-585), [GT-448](../gaps/gap-reference-catalog.md#gt-448) |
| 5 | P2/P3 | Only after security, CI, rules, and contracts stabilize. | [GT-622](../gaps/gap-reference-catalog.md#gt-622), [GT-444](../gaps/gap-reference-catalog.md#gt-444), [GT-464](../gaps/gap-reference-catalog.md#gt-464), [GT-531](../gaps/gap-reference-catalog.md#gt-531), [GT-536](../gaps/gap-reference-catalog.md#gt-536), [GT-592](../gaps/gap-reference-catalog.md#gt-592), +8 |

## Current Blockers
Expand All @@ -42,13 +42,13 @@ Use this summary with a simple rule: if you need context, open only the linked I
| Indicator | Value |
|---|---:|
| Canonical board date | 2026-07-26 |
| Total gaps | 645 |
| Total gaps | 647 |
| Closed gaps | 621 |
| Open gaps | 24 |
| Open gaps | 26 |
| Open P0 | 1 |
| Open P1 | 6 |
| Open P1 | 8 |
| Open P2 | 14 |
| Total closure | 96.3% |
| Total closure | 96% |
| Closure evidence records | 603 |
| Recorded readiness | 4 PASS |

Expand All @@ -58,7 +58,7 @@ Use this summary with a simple rule: if you need context, open only the linked I
| `Governance` | 6 | 0 | 2 | [GT-578](../gaps/gap-reference-catalog.md#gt-578), [GT-585](../gaps/gap-reference-catalog.md#gt-585), [GT-599](../gaps/gap-reference-catalog.md#gt-599), [GT-639](../gaps/gap-reference-catalog.md#gt-639), +2 |
| `Evolith Core` | 4 | 0 | 1 | [GT-583](../gaps/gap-reference-catalog.md#gt-583), [GT-590](../gaps/gap-reference-catalog.md#gt-590), [GT-594](../gaps/gap-reference-catalog.md#gt-594), [GT-600](../gaps/gap-reference-catalog.md#gt-600) |
| `Infra` | 3 | 0 | 1 | [GT-324](../gaps/gap-reference-catalog.md#gt-324), [GT-622](../gaps/gap-reference-catalog.md#gt-622), [GT-464](../gaps/gap-reference-catalog.md#gt-464) |
| `Evolith Suite` | 1 | 0 | 1 | [GT-605](../gaps/gap-reference-catalog.md#gt-605) |
| `Core API` | 2 | 0 | 1 | [GT-648](../gaps/gap-reference-catalog.md#gt-648), [GT-522](../gaps/gap-reference-catalog.md#gt-522) |

## Source and Refresh Rule

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"scope": "evolith-core",
"asOf": "2026-07-26",
"gaps": {
"total": 645,
"total": 647,
"done": 621,
"pending": 8,
"pending": 10,
"inProgress": 9,
"deferred": 7
},
Expand Down
Loading
Loading