From ff03a22362c194fe0a7571c48c444a47c3c22d41 Mon Sep 17 00:00:00 2001 From: aarroyo Date: Thu, 30 Jul 2026 09:07:40 -0500 Subject: [PATCH] =?UTF-8?q?docs(gaps):=20close=20GT-634=20=E2=80=94=201.2.?= =?UTF-8?q?2=20is=20published,=20and=20the=20row's=20security=20framing=20?= =?UTF-8?q?was=20wrong?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.2.2 shipped from main with provenance (run 30548042781). Verified against the REGISTRY rather than the workflow's tick: `npm view` returns 1.2.2 for both packages, both declare `@beyondnet/evolith-sdk: ^2.0.0`, both carry dist.attestations. The check that matters: installing @beyondnet/evolith-cli@latest into a directory that has never seen this workspace resolves sdk@2.0.0 and the binary boots. Before this release the same install pulled the 2026-07-18 build. CRITERION 2 REFUTED THE ROW'S OWN PREMISE, which is the finding worth keeping. "Verify the wave's fixes are present rather than assumed" was written to stop a version number standing in for evidence. Doing it: NONE of the seven security-wave commits (1b077ab6, 3a39dd03, 60bfc63a, 3f31fa64, 90eacdbc, 4f29e114, 8ad4e2de) touches src/packages/sdk-client — zero files each, checked one by one — and the wave's release commit 21205d24 names the three packages it affected: evolith-mcp (15 files), evolith-cli (7), evolith-agent-runtime (1). The SDK was republished alongside them, not fixed. So sdk@1.1.0 predates the wave chronologically and carries none of it. There was no security exposure through this dependency. What WAS real, and what the row should have said: a caret range under 1.x cannot cross a major, so both consumers were pinned a MAJOR behind on a contract dependency, and the lockfile nested the registry tarball inside each consumer where it shadowed the workspace link — the GT-625 class. The hedge written in when the row was opened (it measured dates and resolution, not contents) is what made the correction cheap instead of embarrassing. AND A CORRECTION TO MY OWN EARLIER WORK, made rather than left standing: the GT-624 deprecations of evolith-core-domain, evolith-core and evolith-infra-providers at 1.1.0 claimed "Security fixes landed in 1.2.0". The same evidence refutes it. All three registry messages were rewritten to say those versions were republished alongside the wave, not fixed by it. A false security claim on a public registry is worse than no claim. Criterion 3: sdk@1.1.0 deprecated, with the real reason (`.passed` -> `verdict`) and an explicit "not a security advisory". Verified after `npm cache clean --force` — the first read-back served the previous message from cache. Left open on purpose and recorded: cli@1.2.1 and mcp@1.2.1 are not deprecated (stale contract, not exposure, and one patch old is an owner's call), and contracts@1.1.0 still has no successor to point a deprecation at. Verified: 08-validate-tracking (635 gaps, 611/611 EN/ES, 578 closure records), 41 at 38 dead on a clean checkout against a budget of 40, 01-validate-docs (1491 files), 04 bilingual parity, 46 chain at a fixed point, 48-validate-security- publish-lag green. Counters 595/635 -> 596/635. Co-Authored-By: Claude Opus 5 --- .../evidence/gap-closure-evidence.json | 22 +++++++++++++++++++ .../gaps/gap-reference-catalog.es.md | 8 +++---- .../gaps/gap-reference-catalog.md | 8 +++---- .../control-center/gaps/gap-tracking.es.md | 4 ++-- .../core/control-center/gaps/gap-tracking.md | 4 ++-- .../maturity-reports/executive-summary.es.md | 16 +++++++------- .../maturity-reports/executive-summary.md | 16 +++++++------- .../maturity-reconciliation.json | 6 ++--- 8 files changed, 53 insertions(+), 31 deletions(-) diff --git a/reference/core/control-center/evidence/gap-closure-evidence.json b/reference/core/control-center/evidence/gap-closure-evidence.json index 66601741..38a7a6ba 100644 --- a/reference/core/control-center/evidence/gap-closure-evidence.json +++ b/reference/core/control-center/evidence/gap-closure-evidence.json @@ -8895,6 +8895,28 @@ "Registered by one session and closed by another within the hour. Recorded plainly: the value of the row was making a permanently red required-adjacent job visible, not owning the repair." ], "dependencyDisposition": "none" + }, + { + "id": "GT-634", + "closedAt": "2026-07-30", + "closureCommit": "f84fe739", + "evidence": [ + "src/sdk/cli/package.json", + "src/packages/mcp-server/package.json", + "package-lock.json", + "CHANGELOG.md", + "src/sdk/cli/scripts/check-install-smoke.mjs" + ], + "validationCommands": [ + "PUBLISHED AND VERIFIED AGAINST THE REGISTRY, not against a green workflow: run 30548042781 succeeded, `npm view @beyondnet/evolith-{cli,mcp} version --prefer-online` returns 1.2.2, both declare `@beyondnet/evolith-sdk: ^2.0.0`, and both carry dist.attestations with provenance.", + "THE CHECK THAT MATTERS: installing @beyondnet/evolith-cli@latest into a directory that has never seen this workspace resolves @beyondnet/evolith-sdk@2.0.0 and the binary boots printing 1.2.2. Before this release the same install pulled the 2026-07-18 SDK.", + "CRITERION 2 REFUTED THE ROW'S OWN PREMISE, and that is the finding. None of the seven security-wave commits (1b077ab6, 3a39dd03, 60bfc63a, 3f31fa64, 90eacdbc, 4f29e114, 8ad4e2de) touches src/packages/sdk-client -- zero files each, checked commit by commit. The wave's release commit 21205d24 names the three packages it affected: evolith-mcp (15 files), evolith-cli (7), evolith-agent-runtime (1). The SDK was republished alongside them, not fixed.", + "So sdk@1.1.0 predates the wave chronologically and carries none of it: there was no security exposure through this dependency. What was real: a caret range under 1.x cannot cross a major, so both consumers were pinned a MAJOR behind on a contract dependency (`.passed` against a payload that emits `verdict`), and package-lock.json nested the registry tarball inside each consumer where it shadowed the workspace link -- the GT-625 class.", + "CRITERION 3 -- npm deprecate @beyondnet/evolith-sdk@1.1.0, with a message that gives the real reason (superseded by 2.0.0, `.passed` -> `verdict`) and states explicitly that it is NOT a security advisory. Verified after `npm cache clean --force`, because a first read-back returned the previous message from cache.", + "A CORRECTION TO MY OWN EARLIER WORK, made rather than left standing: the GT-624 deprecations of evolith-core-domain, evolith-core and evolith-infra-providers at 1.1.0 said `Security fixes landed in 1.2.0`. The same commit-by-commit evidence refutes that -- the wave touched three packages and none of those three is among them. All three messages were rewritten to say they were republished alongside the wave, not fixed by it. A false security claim on a public registry is worse than no claim." + ], + "dependencyDisposition": "accepted-scope", + "dependencyRationale": "TWO things are left open on purpose. (1) evolith-cli@1.2.1 and evolith-mcp@1.2.1 are NOT deprecated: they resolve the superseded SDK major, which is a stale contract and not an exposure, and deprecating a version one patch old needs an owner's call rather than mine. (2) evolith-contracts@1.1.0 remains this package's only published version, so there is still no upgrade path to point a deprecation at; it is recorded here rather than forced." } ] } 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 72d4eb79..fdcfd1d4 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.es.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.es.md @@ -7873,13 +7873,13 @@ Serie histórica de gaps registrada en el antiguo `gap-analysis-core.es.md`, pre **Title:** El CLI y el MCP publicados resuelven una build del SDK anterior a la ola de seguridad, porque un rango caret bajo 1.x la fija - **Purpose:** Que `npm install @beyondnet/evolith-cli@latest` deje de arrastrar una dependencia publicada antes de los fixes que anuncia su propio CHANGELOG. -- **Evidence:** **`@beyondnet/evolith-cli@1.2.1` y `@beyondnet/evolith-mcp@1.2.1` declaran ambos `@beyondnet/evolith-sdk: ^1.1.0`, y eso resuelve EXACTAMENTE `1.1.0`.** El SDK publica `1.0.0`, `1.1.0` y `2.0.0` y nada en medio, así que un rango caret bajo 1.x no puede alcanzar 2.0.0 — un caret está acotado por el siguiente mayor. Las fechas son el hallazgo: `sdk@1.1.0` se publicó el **2026-07-18**, `sdk@2.0.0` el **2026-07-27** como parte de la release que cerró [`GT-570`](./gap-reference-catalog.es.md#gt-570), y `cli@1.2.1` / `mcp@1.2.1` el **2026-07-28** — *después* de que 2.0.0 existiera, y seguían apuntando a la línea 1.x. Así que la exposición que GT-570 declaró cerrada para "quien instale `latest`" no está cerrada para esta dependencia: el `latest` de ambas superficies instala un SDK construido antes de la ola del 2026-07-23. **Dicho con precisión, porque la afirmación fuerte no está verificada aquí:** lo medido son las fechas y la resolución, no el contenido — esta fila no afirma que `sdk@1.1.0` cargue una vulnerabilidad concreta, solo que es anterior a la ola y que la release que la trajo fue un mayor al que los consumidores no llegan. Verificar qué fixes hay en `sdk@2.0.0` es parte de cerrarla. **Encontrado porque bloqueaba otra cosa:** deprecar `sdk@1.1.0` para [`GT-624`](./gap-reference-catalog.es.md#gt-624) habría impreso un aviso en cada instalación limpia de la línea actual sin ningún sucesor 1.x que ofrecer, y eso es lo que dejó el rango a la vista. Arreglo: reapuntar ambos rangos a `^2.0.0` (un salto mayor de dependencia, así que exige revisar los breaking changes del SDK contra ambos call sites) o publicar un `sdk@1.2.x` con los fixes, y entonces deprecar `sdk@1.1.0` y cerrar de verdad el primer criterio de GT-624. **RANGOS REAPUNTADOS el 2026-07-29, y hacerlo demostró que la fila se quedaba corta.** `package-lock.json` no sólo registraba el rango — fijaba el tarball del registry de `sdk@1.1.0` en `src/sdk/cli/node_modules/@beyondnet/evolith-sdk` **y** en `src/packages/mcp-server/node_modules/@beyondnet/evolith-sdk`, con hashes de integridad. Así que un `npm ci` en checkout limpio descargaba el SDK anterior a la ola y lo anidaba dentro de cada consumidor, donde **tapa el enlace del workspace**: el symlink de primer nivel al 2.0.0 local es lo que lee todo el mundo, y el 1.1.0 anidado es lo que resuelve de verdad una instalación fresca. Es la misma clase que [`GT-625`](./gap-reference-catalog.es.md#gt-625) — el workspace tapando lo que hace una instalación real — y es la razón de que nada lo cazara. Ambas entradas anidadas desaparecieron al mover el rango a `^2.0.0`, porque entonces el propio 2.0.0 del workspace lo satisface; el diff del lockfile son exactamente dos cadenas de rango y esas dos eliminaciones, sin deriva colateral, y se usó `npm install --package-lock-only` para confirmar que escribe byte a byte lo mismo que la edición a mano en vez de fiarse de ella. La superficie del SDK que ambos consumidores usan de verdad es estrecha — sólo `EvolithRestClient` — así que el breaking change de 2.0.0 (tipos de payload re-exportados de `core-domain`, `.passed` → `.verdict`, severidad `'info'` retirada) no toca ninguno de los dos call sites. Verificado contra 2.0.0: CLI **1436/1436** en 99 suites, mcp-server **433/433** en 53. **1.2.2 está en `main` (PR #285) y NO publicada.** La fila sigue ABIERTA y ése es justo el punto: un rango en el repositorio no es un rango en el registry, y el criterio 1 sólo se cumple cuando npm lo sirve. Hasta entonces el `latest` publicado de ambas superficies sigue instalando el SDK del 2026-07-18. +- **Evidence:** **`@beyondnet/evolith-cli@1.2.1` y `@beyondnet/evolith-mcp@1.2.1` declaran ambos `@beyondnet/evolith-sdk: ^1.1.0`, y eso resuelve EXACTAMENTE `1.1.0`.** El SDK publica `1.0.0`, `1.1.0` y `2.0.0` y nada en medio, así que un rango caret bajo 1.x no puede alcanzar 2.0.0 — un caret está acotado por el siguiente mayor. Las fechas son el hallazgo: `sdk@1.1.0` se publicó el **2026-07-18**, `sdk@2.0.0` el **2026-07-27** como parte de la release que cerró [`GT-570`](./gap-reference-catalog.es.md#gt-570), y `cli@1.2.1` / `mcp@1.2.1` el **2026-07-28** — *después* de que 2.0.0 existiera, y seguían apuntando a la línea 1.x. Así que la exposición que GT-570 declaró cerrada para "quien instale `latest`" no está cerrada para esta dependencia: el `latest` de ambas superficies instala un SDK construido antes de la ola del 2026-07-23. **Dicho con precisión, porque la afirmación fuerte no está verificada aquí:** lo medido son las fechas y la resolución, no el contenido — esta fila no afirma que `sdk@1.1.0` cargue una vulnerabilidad concreta, solo que es anterior a la ola y que la release que la trajo fue un mayor al que los consumidores no llegan. Verificar qué fixes hay en `sdk@2.0.0` es parte de cerrarla. **Encontrado porque bloqueaba otra cosa:** deprecar `sdk@1.1.0` para [`GT-624`](./gap-reference-catalog.es.md#gt-624) habría impreso un aviso en cada instalación limpia de la línea actual sin ningún sucesor 1.x que ofrecer, y eso es lo que dejó el rango a la vista. Arreglo: reapuntar ambos rangos a `^2.0.0` (un salto mayor de dependencia, así que exige revisar los breaking changes del SDK contra ambos call sites) o publicar un `sdk@1.2.x` con los fixes, y entonces deprecar `sdk@1.1.0` y cerrar de verdad el primer criterio de GT-624. **RANGOS REAPUNTADOS el 2026-07-29, y hacerlo demostró que la fila se quedaba corta.** `package-lock.json` no sólo registraba el rango — fijaba el tarball del registry de `sdk@1.1.0` en `src/sdk/cli/node_modules/@beyondnet/evolith-sdk` **y** en `src/packages/mcp-server/node_modules/@beyondnet/evolith-sdk`, con hashes de integridad. Así que un `npm ci` en checkout limpio descargaba el SDK anterior a la ola y lo anidaba dentro de cada consumidor, donde **tapa el enlace del workspace**: el symlink de primer nivel al 2.0.0 local es lo que lee todo el mundo, y el 1.1.0 anidado es lo que resuelve de verdad una instalación fresca. Es la misma clase que [`GT-625`](./gap-reference-catalog.es.md#gt-625) — el workspace tapando lo que hace una instalación real — y es la razón de que nada lo cazara. Ambas entradas anidadas desaparecieron al mover el rango a `^2.0.0`, porque entonces el propio 2.0.0 del workspace lo satisface; el diff del lockfile son exactamente dos cadenas de rango y esas dos eliminaciones, sin deriva colateral, y se usó `npm install --package-lock-only` para confirmar que escribe byte a byte lo mismo que la edición a mano en vez de fiarse de ella. La superficie del SDK que ambos consumidores usan de verdad es estrecha — sólo `EvolithRestClient` — así que el breaking change de 2.0.0 (tipos de payload re-exportados de `core-domain`, `.passed` → `.verdict`, severidad `'info'` retirada) no toca ninguno de los dos call sites. Verificado contra 2.0.0: CLI **1436/1436** en 99 suites, mcp-server **433/433** en 53. **1.2.2 está en `main` (PR #285) y NO publicada.** La fila sigue ABIERTA y ése es justo el punto: un rango en el repositorio no es un rango en el registry, y el criterio 1 sólo se cumple cuando npm lo sirve. Hasta entonces el `latest` publicado de ambas superficies sigue instalando el SDK del 2026-07-18. **CERRADO el 2026-07-30, y el marco de seguridad de esta fila era ERRÓNEO — se registra en vez de retirarlo en silencio.** 1.2.2 está publicada con provenance y una instalación limpia de `@beyondnet/evolith-cli@latest` resuelve ya `@beyondnet/evolith-sdk@2.0.0`, con el binario arrancando. Pero el criterio 2 obligó a hacer la pregunta que esta fila había dejado abierta a propósito, y la respuesta refuta su premisa: **ninguno de los siete commits de la ola de seguridad (`1b077ab6`, `3a39dd03`, `60bfc63a`, `3f31fa64`, `90eacdbc`, `4f29e114`, `8ad4e2de`) toca `src/packages/sdk-client` — cero ficheros.** El propio commit de release de la ola, `21205d24`, nombra los tres paquetes afectados: `evolith-mcp` (15 ficheros), `evolith-cli` (7) y `evolith-agent-runtime` (1). El SDK se republicó con ellos, no se arregló. Así que `sdk@1.1.0` es cronológicamente anterior a la ola y no carga nada de ella; no había exposición por esta dependencia. **Lo que sí era real, y es lo que la fila debió decir:** un rango caret bajo 1.x no cruza un mayor, así que ambos consumidores quedaban fijados un MAYOR por detrás en una dependencia de contrato — `.passed` contra un payload que emite `verdict` — y el lockfile anidaba el tarball del registry dentro de cada consumidor, tapando el enlace del workspace. Un defecto de contrato rancio y de sombra en el lockfile (la clase de [`GT-625`](./gap-reference-catalog.es.md#gt-625)), no de seguridad. La cautela que se escribió en la fila al abrirla — que medía fechas y resolución, no contenidos — es lo que abarató la corrección. - **Component:** `Infra` · **Criticality:** P1 · **Complexity:** S - **Provenance:** Encontrado el 2026-07-29 ejecutando el criterio de deprecación de GT-624. Registrado en vez de sorteado: saltarse la deprecación en silencio habría dejado el rango — y la instalación — exactamente como están. - **Acceptance criteria:** - - [ ] `npm view @beyondnet/evolith-cli@latest dependencies` muestra un rango de SDK que resuelve a una versión publicada el 2026-07-27 o después, e igual `evolith-mcp`. - - [ ] Los fixes que reclama la ola de seguridad del 2026-07-23 se verifican presentes en la versión del SDK que el CLI y el MCP resuelven de verdad, en vez de asumirse por el número de versión. - - [ ] `@beyondnet/evolith-sdk@1.1.0` queda deprecada en cuanto exista un sucesor al que sus consumidores puedan llegar. + - [x] `npm view @beyondnet/evolith-cli@latest dependencies` muestra un rango de SDK que resuelve a una versión publicada el 2026-07-27 o después, e igual `evolith-mcp` — ambos publican `^2.0.0` en 1.2.2, y una instalación limpia resuelve `sdk@2.0.0` con el binario arrancando. + - [x] Los fixes que reclama la ola de seguridad del 2026-07-23 se verifican presentes en la versión del SDK que el CLI y el MCP resuelven de verdad, en vez de asumirse por el número de versión — y la verificación REFUTÓ la premisa: ninguno de los siete commits de la ola toca `src/packages/sdk-client`, así que no hay fixes de la ola en el SDK que puedan estar o faltar. + - [x] `@beyondnet/evolith-sdk@1.1.0` queda deprecada en cuanto exista un sucesor al que sus consumidores puedan llegar — hecho el 2026-07-30, con un mensaje que da el motivo real (superseded por 2.0.0, `.passed` → `verdict`) y dice explícitamente que no es un aviso de seguridad. #### GT-633 diff --git a/reference/core/control-center/gaps/gap-reference-catalog.md b/reference/core/control-center/gaps/gap-reference-catalog.md index c001a3ea..046fcb95 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.md @@ -7968,13 +7968,13 @@ Historical gap series tracked in the former `gap-analysis-core.md`, preserved fo **Title:** The published CLI and MCP resolve an SDK build that predates the security wave, because a caret range under 1.x pins it - **Purpose:** Make `npm install @beyondnet/evolith-cli@latest` stop pulling a dependency published before the fixes its own CHANGELOG announces. -- **Evidence:** **`@beyondnet/evolith-cli@1.2.1` and `@beyondnet/evolith-mcp@1.2.1` both declare `@beyondnet/evolith-sdk: ^1.1.0`, and that resolves to EXACTLY `1.1.0`.** The SDK publishes `1.0.0`, `1.1.0` and `2.0.0` and nothing in between, so a caret range under 1.x cannot reach 2.0.0 — a caret is bounded by the next major. The dates are the finding: `sdk@1.1.0` was published **2026-07-18**, `sdk@2.0.0` on **2026-07-27** as part of the release that closed [`GT-570`](./gap-reference-catalog.md#gt-570), and `cli@1.2.1` / `mcp@1.2.1` on **2026-07-28** — *after* 2.0.0 existed, still pointing at the 1.x line. So the exposure GT-570 declared closed for "anyone installing `latest`" is not closed for this dependency: `latest` of both surfaces installs an SDK built before the 2026-07-23 wave. **Stated precisely, because the stronger claim is not verified here:** what is measured is the dates and the resolution, not the contents — this row does not assert that `sdk@1.1.0` carries a specific vulnerability, only that it predates the wave and that the release which shipped the wave was a major the consumers cannot reach. Verifying which fixes are in `sdk@2.0.0` is part of closing it. **Found because it blocked something else:** deprecating `sdk@1.1.0` for [`GT-624`](./gap-reference-catalog.md#gt-624) would have printed a warning on every fresh install of the current line with no 1.x successor to offer, which is what exposed the range. Fix: retarget both ranges to `^2.0.0` (a major bump of a dependency, so it needs the SDK's breaking changes reviewed against both call sites) or publish an `sdk@1.2.x` carrying the fixes, then deprecate `sdk@1.1.0` and close GT-624's first criterion for real. **RANGES RETARGETED 2026-07-29, and doing it proved the row understated the defect.** `package-lock.json` did not merely record the range — it pinned the registry tarball of `sdk@1.1.0` at `src/sdk/cli/node_modules/@beyondnet/evolith-sdk` **and** `src/packages/mcp-server/node_modules/@beyondnet/evolith-sdk`, with integrity hashes. So `npm ci` on a clean checkout fetched the pre-wave SDK and nested it inside each consumer, where it **shadows the workspace link**: the top-level symlink to the local 2.0.0 is what every developer reads, and the nested 1.1.0 is what a fresh install actually resolves. That is the same class as [`GT-625`](./gap-reference-catalog.md#gt-625) — the workspace hiding what a real install does — and it is why nothing caught this. Both nested entries disappeared once the range moved to `^2.0.0`, because the workspace's own 2.0.0 then satisfies it; the lockfile diff is exactly two range strings and those two removals, with no collateral drift, and `npm install --package-lock-only` was used to confirm it writes byte-for-byte what the hand edit did rather than trusting the edit. The SDK surface both consumers actually use is narrow — `EvolithRestClient` only — so the 2.0.0 breaking change (payload types re-exported from `core-domain`, `.passed` → `.verdict`, `'info'` severity retired) touches neither call site. Verified against 2.0.0: CLI **1436/1436** in 99 suites, mcp-server **433/433** in 53. **1.2.2 is on `main` (PR #285) and NOT published.** The row stays OPEN and that is the point: a range in the repository is not a range on the registry, and criterion 1 is met only when npm serves it. Until then the published `latest` of both surfaces still installs the 2026-07-18 SDK. +- **Evidence:** **`@beyondnet/evolith-cli@1.2.1` and `@beyondnet/evolith-mcp@1.2.1` both declare `@beyondnet/evolith-sdk: ^1.1.0`, and that resolves to EXACTLY `1.1.0`.** The SDK publishes `1.0.0`, `1.1.0` and `2.0.0` and nothing in between, so a caret range under 1.x cannot reach 2.0.0 — a caret is bounded by the next major. The dates are the finding: `sdk@1.1.0` was published **2026-07-18**, `sdk@2.0.0` on **2026-07-27** as part of the release that closed [`GT-570`](./gap-reference-catalog.md#gt-570), and `cli@1.2.1` / `mcp@1.2.1` on **2026-07-28** — *after* 2.0.0 existed, still pointing at the 1.x line. So the exposure GT-570 declared closed for "anyone installing `latest`" is not closed for this dependency: `latest` of both surfaces installs an SDK built before the 2026-07-23 wave. **Stated precisely, because the stronger claim is not verified here:** what is measured is the dates and the resolution, not the contents — this row does not assert that `sdk@1.1.0` carries a specific vulnerability, only that it predates the wave and that the release which shipped the wave was a major the consumers cannot reach. Verifying which fixes are in `sdk@2.0.0` is part of closing it. **Found because it blocked something else:** deprecating `sdk@1.1.0` for [`GT-624`](./gap-reference-catalog.md#gt-624) would have printed a warning on every fresh install of the current line with no 1.x successor to offer, which is what exposed the range. Fix: retarget both ranges to `^2.0.0` (a major bump of a dependency, so it needs the SDK's breaking changes reviewed against both call sites) or publish an `sdk@1.2.x` carrying the fixes, then deprecate `sdk@1.1.0` and close GT-624's first criterion for real. **RANGES RETARGETED 2026-07-29, and doing it proved the row understated the defect.** `package-lock.json` did not merely record the range — it pinned the registry tarball of `sdk@1.1.0` at `src/sdk/cli/node_modules/@beyondnet/evolith-sdk` **and** `src/packages/mcp-server/node_modules/@beyondnet/evolith-sdk`, with integrity hashes. So `npm ci` on a clean checkout fetched the pre-wave SDK and nested it inside each consumer, where it **shadows the workspace link**: the top-level symlink to the local 2.0.0 is what every developer reads, and the nested 1.1.0 is what a fresh install actually resolves. That is the same class as [`GT-625`](./gap-reference-catalog.md#gt-625) — the workspace hiding what a real install does — and it is why nothing caught this. Both nested entries disappeared once the range moved to `^2.0.0`, because the workspace's own 2.0.0 then satisfies it; the lockfile diff is exactly two range strings and those two removals, with no collateral drift, and `npm install --package-lock-only` was used to confirm it writes byte-for-byte what the hand edit did rather than trusting the edit. The SDK surface both consumers actually use is narrow — `EvolithRestClient` only — so the 2.0.0 breaking change (payload types re-exported from `core-domain`, `.passed` → `.verdict`, `'info'` severity retired) touches neither call site. Verified against 2.0.0: CLI **1436/1436** in 99 suites, mcp-server **433/433** in 53. **1.2.2 is on `main` (PR #285) and NOT published.** The row stays OPEN and that is the point: a range in the repository is not a range on the registry, and criterion 1 is met only when npm serves it. Until then the published `latest` of both surfaces still installs the 2026-07-18 SDK. **CLOSED 2026-07-30, and the security framing of this row was WRONG — recorded rather than quietly dropped.** 1.2.2 is published with provenance and a clean install of `@beyondnet/evolith-cli@latest` now resolves `@beyondnet/evolith-sdk@2.0.0`, with the binary booting. But criterion 2 forced the question this row had deliberately left open, and the answer refutes its premise: **not one of the seven security-wave commits (`1b077ab6`, `3a39dd03`, `60bfc63a`, `3f31fa64`, `90eacdbc`, `4f29e114`, `8ad4e2de`) touches `src/packages/sdk-client` — zero files.** The wave's own release commit `21205d24` names the three packages it affected: `evolith-mcp` (15 files), `evolith-cli` (7) and `evolith-agent-runtime` (1). The SDK was republished alongside them, not fixed. So `sdk@1.1.0` predates the wave chronologically and carries none of it; there was no exposure through this dependency. **What was real, and is what the row should have said:** a caret range under 1.x cannot cross a major, so both consumers were pinned a MAJOR behind on a contract dependency — `.passed` against a payload that emits `verdict` — and the lockfile nested the registry tarball inside each consumer where it shadowed the workspace link. A stale-contract and lockfile-shadowing defect (the [`GT-625`](./gap-reference-catalog.md#gt-625) class), not a security one. The hedge written into this row when it was opened — that it measured dates and resolution, not contents — is what made the correction cheap. - **Component:** `Infra` · **Criticality:** P1 · **Complexity:** S - **Provenance:** Found on 2026-07-29 while executing GT-624's deprecation criterion. Registered rather than worked around: skipping the deprecation quietly would have left the range — and the install — exactly as they are. - **Acceptance criteria:** - - [ ] `npm view @beyondnet/evolith-cli@latest dependencies` shows an SDK range that resolves to a version published on or after 2026-07-27, likewise `evolith-mcp`. - - [ ] The fixes claimed by the 2026-07-23 security wave are verified present in the SDK version the CLI and MCP actually resolve, rather than assumed from the version number. - - [ ] `@beyondnet/evolith-sdk@1.1.0` is deprecated once a successor its consumers can reach exists. + - [x] `npm view @beyondnet/evolith-cli@latest dependencies` shows an SDK range that resolves to a version published on or after 2026-07-27, likewise `evolith-mcp` — both publish `^2.0.0` at 1.2.2, and a clean install resolves `sdk@2.0.0` with the binary booting. + - [x] The fixes claimed by the 2026-07-23 security wave are verified present in the SDK version the CLI and MCP actually resolve, rather than assumed from the version number — and the verification REFUTED the premise: none of the seven wave commits touches `src/packages/sdk-client`, so there are no wave fixes in the SDK to be present or absent. + - [x] `@beyondnet/evolith-sdk@1.1.0` is deprecated once a successor its consumers can reach exists — done 2026-07-30, with a message that states the real reason (superseded by 2.0.0, `.passed` → `verdict`) and says explicitly it is not a security advisory. #### GT-633 diff --git a/reference/core/control-center/gaps/gap-tracking.es.md b/reference/core/control-center/gaps/gap-tracking.es.md index 006eeecd..e50555ab 100644 --- a/reference/core/control-center/gaps/gap-tracking.es.md +++ b/reference/core/control-center/gaps/gap-tracking.es.md @@ -14,7 +14,7 @@ Este tablero es la única fuente de verdad para deuda técnica, gaps, oportunida | ID | Gap | Qué significa | Ejemplo | Componente | Fase | Criticidad | Complejidad | Estado | |---|---|---|---|:---:|:---:|:---:|:---:|:---:| | [`GT-635`](./gap-reference-catalog.es.md#gt-635) | **El ratchet de referencias muertas está por encima del presupuesto en main desde el 2026-07-28, así que el job de gobernanza está en rojo permanente.** `41-validate-evidence-commands --strict --max-dead 305` reporta **309 referencias muertas en un checkout limpio**, así que `Governance guards (GT-578)` sale 1 en cada corrida de `ci-cd.yml` — observado en las corridas `30482497995`, `30470648146` y `30466280865`, todas en `main` y sin nada por mergear. El presupuesto se fijó en `4a1b92d6` "donde se midió"; desde entonces diez commits han editado `gap-closure-evidence.json`, cada uno añadiendo `validationCommands`, y la cuenta se fue por encima. El propósito entero del ratchet es que las referencias muertas no CREZCAN, y está reportando exactamente eso mientras se lo ignora, porque un job en rojo permanente se lee como ruido de fondo — el mismo modo de fallo que [`GT-622`](./gap-reference-catalog.es.md#gt-622) registra para la configuración huérfana de CodeQL. El próximo cambio que añada de verdad una referencia muerta será indistinguible de las cuatro que ya están. **Lo que no hay que hacer:** subir el presupuesto a 309, que convierte un ratchet incumplido en uno ratificado. | | | `Governance` | Cross | P2 | S | `COMPLETADO` | -| [`GT-634`](./gap-reference-catalog.es.md#gt-634) | **El CLI y el MCP publicados resuelven una build del SDK anterior a la ola de seguridad, porque un rango caret bajo 1.x la fija.** `@beyondnet/evolith-cli@1.2.1` y `@beyondnet/evolith-mcp@1.2.1` declaran ambos `@beyondnet/evolith-sdk: ^1.1.0`, y eso resuelve **exactamente `1.1.0`** — el SDK publica `1.0.0`, `1.1.0` y `2.0.0` y nada en medio, y un caret no cruza un mayor. Las fechas son el hallazgo: `sdk@1.1.0` se publicó el 2026-07-18, `sdk@2.0.0` el 2026-07-27 como parte de la release que cerró [`GT-570`](./gap-reference-catalog.es.md#gt-570), y ambos consumidores el 2026-07-28 — **después** de que 2.0.0 existiera, y seguían en la línea 1.x. Así que la exposición que GT-570 declaró cerrada para "quien instale `latest`" no está cerrada para esta dependencia. Dicho con precisión, porque la afirmación fuerte no está verificada aquí: lo medido son las fechas y la resolución, no el contenido — esta fila no afirma que `sdk@1.1.0` cargue una vulnerabilidad concreta. Encontrado ejecutando el criterio de deprecación de [`GT-624`](./gap-reference-catalog.es.md#gt-624), que es lo que dejó el rango a la vista: deprecar `sdk@1.1.0` avisaría en cada instalación limpia de la línea actual sin ningún sucesor 1.x que ofrecer. | | | `Infra` | Cross | P1 | S | `PENDIENTE` | +| [`GT-634`](./gap-reference-catalog.es.md#gt-634) | **El CLI y el MCP publicados resuelven una build del SDK anterior a la ola de seguridad, porque un rango caret bajo 1.x la fija.** `@beyondnet/evolith-cli@1.2.1` y `@beyondnet/evolith-mcp@1.2.1` declaran ambos `@beyondnet/evolith-sdk: ^1.1.0`, y eso resuelve **exactamente `1.1.0`** — el SDK publica `1.0.0`, `1.1.0` y `2.0.0` y nada en medio, y un caret no cruza un mayor. Las fechas son el hallazgo: `sdk@1.1.0` se publicó el 2026-07-18, `sdk@2.0.0` el 2026-07-27 como parte de la release que cerró [`GT-570`](./gap-reference-catalog.es.md#gt-570), y ambos consumidores el 2026-07-28 — **después** de que 2.0.0 existiera, y seguían en la línea 1.x. Así que la exposición que GT-570 declaró cerrada para "quien instale `latest`" no está cerrada para esta dependencia. Dicho con precisión, porque la afirmación fuerte no está verificada aquí: lo medido son las fechas y la resolución, no el contenido — esta fila no afirma que `sdk@1.1.0` cargue una vulnerabilidad concreta. Encontrado ejecutando el criterio de deprecación de [`GT-624`](./gap-reference-catalog.es.md#gt-624), que es lo que dejó el rango a la vista: deprecar `sdk@1.1.0` avisaría en cada instalación limpia de la línea actual sin ningún sucesor 1.x que ofrecer. **CERRADO el 2026-07-30 — 1.2.2 está publicada, y el marco de seguridad de esta fila era ERRÓNEO.** Una instalación limpia de `@beyondnet/evolith-cli@latest` resuelve ya `@beyondnet/evolith-sdk@2.0.0` con el binario arrancando. Pero verificar el criterio 2 refutó la premisa: **ninguno de los siete commits de la ola toca `src/packages/sdk-client` — cero ficheros** — y el propio commit de release de la ola nombra los tres paquetes afectados (mcp, cli, agent-runtime). El SDK se republicó con ellos, no se arregló. Lo real es un MAYOR rancio fijado por un caret bajo 1.x más un lockfile que anidaba el tarball del registry tapando el enlace del workspace — la clase de `GT-625`, no una exposición de seguridad. La cautela escrita al abrir la fila es lo que abarató la corrección. | | | `Infra` | Cross | P1 | S | `COMPLETADO` | | [`GT-633`](./gap-reference-catalog.es.md#gt-633) | **El guard comparaba el snapshot contra seis números copiados del propio snapshot, así que solo podía pasar — y el archivo que no estaba vigilando se estampa en 388 filas de un artefacto mayor.** `native-evaluability-snapshot.json` declaraba en su propia cabecera que era "un SNAPSHOT CAPTURADO, no la fuente de verdad". **No existía ningún script de captura.** Se mantenía a mano y derivó: fijaba `documentation-only: 129` mientras Core fijaba 136, y seguía llamando `unimplemented-native` a reglas que ya tenían handler. Su guard en `iso-5055-mapping.test.mjs` asertaba los seis conteos de clase del snapshot contra seis números escritos a mano en el test — los mismos seis literales que el snapshot ya contenía. **El valor esperado y el valor real eran copias el uno del otro**, así que la aserción se sostenía mientras el archivo no cambiara, fijara Core lo que fijara; informaba de una concordancia que nunca comprobó, lo cual es peor que no tener guard, porque la fila que protege se lee como verificada. **La deriva no se queda donde empieza.** `build-iso-5055-mapping.mjs` estampa `nativeEvaluability` en TODAS las filas del mapeo ISO/IEC 5055 desde ese archivo — 388 tras la recaptura, 381 antes — así que una sola clase rancia se blanquea en un artefacto derivado cinco veces mayor que su entrada, y sobredimensiona el backlog de handlers que es el entregable entero de [`GT-598`](./gap-reference-catalog.es.md#gt-598). El orden recaptura→reconstrucción no estaba escrito en ningún sitio, y el guard del propio mapeo no corría en **ningún workflow**. **CORREGIDO el 2026-07-29:** un script de captura que ejecuta el triage REAL de Core vía `ts-node` en vez de reimplementar la clasificación, la medición extraída del spec de jest a `test/rule-corpus-triage.ts` para que un script pueda alcanzarla — esa inalcanzabilidad es la razón de que el archivo se mantuviera a mano —, aserciones snapshot-contra-triage-fresco en ambas direcciones, un guard del job de documentación que lee los conteos que Core fija LEYENDO el spec y **lanza excepción** cuando el literal falta o cambia de forma, y ambos pasos declarados como eslabones de la cadena de artefactos derivados de [`GT-630`](./gap-reference-catalog.es.md#gt-630). **Recapturado el 2026-07-29 tras mergear el fix en la línea actual, y la deriva había crecido mientras estaba en vuelo:** `native-handler` 139 → 151, `documentation-only` 129 → 136, el backlog de handlers 60 → **48**, corpus 379 → 386, mapeo 381 → 388 filas. Doce de esos handlers los cerraron [`GT-595`](./gap-reference-catalog.es.md#gt-595) y [`GT-632`](./gap-reference-catalog.es.md#gt-632) mientras el snapshot mantenido a mano seguía reportándolos como backlog — el defecto demostrándose una vez más, sobre números que nadie escribió dos veces. | | | `Governance` | Cross | P1 | S | `COMPLETADO` | | [`GT-632`](./gap-reference-catalog.es.md#gt-632) | **El MCP denegaba todas sus herramientas en producción por una ruta mal escrita, y la prueba de que funcionaba era estado local sin versionar.** `abac-evaluator.ts:322` resolvía la política compilada en `/sdk/cli/rulesets/opa/policy.wasm` — la ruta ANTERIOR al refactor `src/`. El fichero vive en `src/sdk/cli/…`. Cuando el stat falla, el evaluador deniega fail-closed en producción, así que en un checkout limpio con `NODE_ENV=production` la política compilada nunca se carga y TODA herramienta MCP se rechaza. Parecía verde porque `policy.wasm` está gitignorado: un fichero rancio dejado en la ruta vieja por una época anterior hacía que la carga funcionara en local, y el test que lo cubre pasaba contra un artefacto que ningún checkout fresco tendría. Misma forma que `GT-625` — un verde que dependía de estado no versionado — pero en el camino de autorización. Encontrado el 2026-07-29 investigando por qué `abac-rego-parity.spec.ts` se había puesto rojo; el rego era correcto (`opa eval` devuelve `allow: true` para un arquitecto en producción). **`40-validate-path-literals` no puede ver esta clase:** barre literales de cadena, y esto es una construcción `path.join(corePath, 'sdk', 'cli', …)`. Sobreviven doce joins así en `src/**` — los otros once están en `opa-input-builder.ts` y `cli-release-rule.handler.ts`, donde una ruta equivocada produce un falso NEGATIVO en un evaluador en vez de una denegación, que es más silencioso y no menos incorrecto. **CORREGIDO para el camino de autorización el 2026-07-29:** el evaluador prueba ahora ambas ubicaciones, la nueva primero — no es dejadez, es lo que mantiene funcionando una imagen ya desplegada mientras se corrige la ruta. Revertir el arreglo pone `abac-rego-parity.spec.ts` en rojo (1 de 11); con él, mcp-server va 434/434. Quedan los otros once joins y el punto ciego del guard. **Criterio 2 CERRADO el 2026-07-29 — y dos de las once estaban mal de una forma que el prefijo por sí solo no arreglaba.** Ocho eran supervivientes simples del movimiento a `src/` y admitieron el prefijo. Las otras tres eran peores: dos apuntaban a `sdk/cli/src/core/mcp/server.ts`, un fichero que salió del CLI por completo cuando mcp-server pasó a ser su propio paquete (hoy `src/packages/mcp-server/src/mcp/mcp-server.service.ts`); y una exigía un `package-lock.json` por paquete, cosa que los WORKSPACES de npm impiden — hay exactamente un lockfile, en la raíz, así que esa comprobación solo podía ser falsa. Una regla que nadie puede satisfacer no es una regla más estricta. **Las specs y las fixtures de paridad también codificaban el layout viejo** (`cli-release-readiness.fixture.json`, `mcp.fixture.json`), y por eso llevaban tiempo en verde sobre rutas que no existen: la fixture y el código coincidían entre sí y ninguno coincidía con el repositorio. Ambas corregidas. core-domain 1367/1367, `28-native-evaluator-parity` exit 0. **Criterio 3 CERRADO el 2026-07-29 — la primera corrida del guard estuvo casi toda equivocada, que es el hallazgo.** `47-validate-joined-paths` reportó 19 rutas rotas; solo 3 eran defectos. Cada una de las otras 16 habría empeorado el código: seis tenían base `root`, un PARÁMETRO que nombra el workspace del cliente, así que corregirlas rompería la evaluación de todos los satélites; nueve eran miembros de CADENAS DE FALLBACK correctas cuando uno de cuatro layouts resuelve, y la forma natural de callarlas es borrar los fallbacks, rompiendo imágenes desplegadas; una era una PROHIBICIÓN cuya ausencia ES el estado que aprueba, así que corregirla habría invertido la regla. Las cadenas se detectan ahora estructuralmente (hermanos de array, o las ramas de un ternario); las prohibiciones no pueden, y llevan razón escrita. Los tres defectos reales: `opa-evaluator.ts` resolvía `policy.wasm` y los esquemas de entrada sin el prefijo `src/`, misma clase fail-closed que el P0 de arriba; `satellite-upgrade-diff.ts` sondeaba `rulesets` detrás de un return temprano, reportando cero cambios de rulesets en cada upgrade de satélite; `artifact-path-resolver.ts` nombraba un `adr-matrix.json` que no existe en ningún layout. 11 fixtures negativas, los dos suelos anti-vacuos, y `43-validate-guard-negative-fixtures` lo OBSERVÓ en rojo sobre 36 guards. Una indulgencia conocida (dos joins como argumentos hermanos) queda anotada en su cabecera en vez de escondida. | | | `MCP Server` | Cross | P0 | M | `COMPLETADO` | | [`GT-631`](./gap-reference-catalog.es.md#gt-631) | **Extraído de [`GT-573`](./gap-reference-catalog.es.md#gt-573) para que su mitad cross-repo se siga en vez de absorberse en un cierre.** La mitad del Core del tercer criterio de GT-573 está hecha: `evaluation-context` y `evaluation-result` están fijados en `MACHINE_CONTRACT_SET` junto a `gate-evidence` y `output-envelope`, así que un cambio en cualquiera de esas formas ya rompe `10-validate-contract-conformance` en vez de llegar en silencio a los consumidores. Lo que queda está en otro repositorio y no se puede hacer desde aquí: el **Tracker** debe re-fijar esos dos esquemas en su propio `contracts/evolith-core-contracts.json` con su sha256, enlazar las tres fixtures publicadas (`EVALUATION_RESULT_PASS/FAIL/OPA_GATE_FAIL`) en un test de binding de DTOs, quitar o documentar `resolvedTopology` (que el resultado canónico nunca llevó), renombrar su `phase` de gate a `phaseId`, y eliminar su caída a `SKIPPED` para un payload que trae un `overallVerdict` no vacío. Hasta entonces, un cambio de forma del lado Core se caza aquí y sigue sorprendiendo al consumidor. | | | `Evolith Tracker` | Cross | P1 | S | `PENDIENTE` | @@ -650,7 +650,7 @@ Este tablero es la única fuente de verdad para deuda técnica, gaps, oportunida | [`GT-246`](./gap-reference-catalog.es.md#gt-246) | Implementar experimentos Chaos Mesh/Litmus | | | `QA` | Cross | P3 | L | `COMPLETADO` | -**Progreso:** 595 / 635 completados · 14 en progreso · 22 pendientes · 4 diferidos +**Progreso:** 596 / 635 completados · 14 en progreso · 21 pendientes · 4 diferidos **Oleada 2026-06-23 (auditoría profunda de Winston III):** Añadidos 14 gaps nuevos `GT-212`…`GT-225` del Winston Audit Playbook que cubren: higiene de estado ADR (GT-212), metadata + presupuestos operativos + corpus de guías por topología (GT-213, GT-217, GT-219), observabilidad + OpenAPI en controladores REST (GT-214, GT-215), paridad de input-schemas OPA + densidad de tests por topología (GT-216, GT-222), plantillas de rollback + on-call de Fase 05 (GT-218), cobertura de ramas CLI + paridad de envelope --format + limpieza de skip-list (GT-220, GT-224, GT-225), audit logging HTTP de MCP (GT-221), y tests e2e de paridad cross-surface (GT-223). diff --git a/reference/core/control-center/gaps/gap-tracking.md b/reference/core/control-center/gaps/gap-tracking.md index 01d95fdf..56cb81e8 100644 --- a/reference/core/control-center/gaps/gap-tracking.md +++ b/reference/core/control-center/gaps/gap-tracking.md @@ -14,7 +14,7 @@ This board is the single source of truth for technical debt, gaps, opportunities | ID | Gap | What it means | Example | Component | Phase | Criticality | Complexity | Status | |---|---|---|---|:---:|:---:|:---:|:---:|:---:| | [`GT-635`](./gap-reference-catalog.md#gt-635) | **The dead-reference ratchet has been over budget on main since 2026-07-28, so the governance job is permanently red.** `41-validate-evidence-commands --strict --max-dead 305` reports **309 dead references on a clean checkout**, so `Governance guards (GT-578)` exits 1 on every run of `ci-cd.yml` — observed on runs `30482497995`, `30470648146` and `30466280865`, all on `main` with nothing unmerged. The budget was set in `4a1b92d6` "where it was measured"; ten commits have edited `gap-closure-evidence.json` since, each adding `validationCommands`, and the count drifted past it. The ratchet's whole purpose is that dead references must not GROW, and it is reporting exactly that while being ignored, because a permanently red job reads as background noise — the same failure mode [`GT-622`](./gap-reference-catalog.md#gt-622) records for the orphaned CodeQL configuration. The next change that genuinely adds a dead reference will be indistinguishable from the four already there. **What not to do:** raise the budget to 309, which converts a breached ratchet into a ratified one. | | | `Governance` | Cross | P2 | S | `DONE` | -| [`GT-634`](./gap-reference-catalog.md#gt-634) | **The published CLI and MCP resolve an SDK build that predates the security wave, because a caret range under 1.x pins it.** `@beyondnet/evolith-cli@1.2.1` and `@beyondnet/evolith-mcp@1.2.1` both declare `@beyondnet/evolith-sdk: ^1.1.0`, and that resolves to **exactly `1.1.0`** — the SDK publishes `1.0.0`, `1.1.0` and `2.0.0` and nothing in between, and a caret cannot cross a major. The dates are the finding: `sdk@1.1.0` was published 2026-07-18, `sdk@2.0.0` on 2026-07-27 as part of the release that closed [`GT-570`](./gap-reference-catalog.md#gt-570), and both consumers on 2026-07-28 — **after** 2.0.0 existed, still on the 1.x line. So the exposure GT-570 declared closed for "anyone installing `latest`" is not closed for this dependency. Stated precisely, because the stronger claim is unverified here: what is measured is the dates and the resolution, not the contents — this row does not assert that `sdk@1.1.0` carries a specific vulnerability. Found while executing [`GT-624`](./gap-reference-catalog.md#gt-624)'s deprecation criterion, which is what exposed the range: deprecating `sdk@1.1.0` would warn on every fresh install of the current line with no 1.x successor to offer. | | | `Infra` | Cross | P1 | S | `PENDING` | +| [`GT-634`](./gap-reference-catalog.md#gt-634) | **The published CLI and MCP resolve an SDK build that predates the security wave, because a caret range under 1.x pins it.** `@beyondnet/evolith-cli@1.2.1` and `@beyondnet/evolith-mcp@1.2.1` both declare `@beyondnet/evolith-sdk: ^1.1.0`, and that resolves to **exactly `1.1.0`** — the SDK publishes `1.0.0`, `1.1.0` and `2.0.0` and nothing in between, and a caret cannot cross a major. The dates are the finding: `sdk@1.1.0` was published 2026-07-18, `sdk@2.0.0` on 2026-07-27 as part of the release that closed [`GT-570`](./gap-reference-catalog.md#gt-570), and both consumers on 2026-07-28 — **after** 2.0.0 existed, still on the 1.x line. So the exposure GT-570 declared closed for "anyone installing `latest`" is not closed for this dependency. Stated precisely, because the stronger claim is unverified here: what is measured is the dates and the resolution, not the contents — this row does not assert that `sdk@1.1.0` carries a specific vulnerability. Found while executing [`GT-624`](./gap-reference-catalog.md#gt-624)'s deprecation criterion, which is what exposed the range: deprecating `sdk@1.1.0` would warn on every fresh install of the current line with no 1.x successor to offer. **CLOSED 2026-07-30 — 1.2.2 is published, and the security framing of this row was WRONG.** A clean install of `@beyondnet/evolith-cli@latest` now resolves `@beyondnet/evolith-sdk@2.0.0` with the binary booting. But verifying criterion 2 refuted the premise: **none of the seven security-wave commits touches `src/packages/sdk-client` — zero files** — and the wave's own release commit names the three packages it affected (mcp, cli, agent-runtime). The SDK was republished alongside them, not fixed. What was real is a stale MAJOR pinned by a caret under 1.x plus a lockfile that nested the registry tarball where it shadowed the workspace link — the `GT-625` class, not a security exposure. The hedge written in when the row was opened is what made the correction cheap. | | | `Infra` | Cross | P1 | S | `DONE` | | [`GT-633`](./gap-reference-catalog.md#gt-633) | **The guard compared the snapshot against six numbers copied out of the snapshot, so it could only ever pass — and the file it was failing to guard is stamped onto 388 rows of a larger artifact.** `native-evaluability-snapshot.json` declared in its own header that it was "a CAPTURED SNAPSHOT, not the source of truth". **No capture script existed.** It was hand-maintained and it drifted: it pinned `documentation-only: 129` while Core pinned 136, and went on calling rules `unimplemented-native` after they had handlers. Its guard in `iso-5055-mapping.test.mjs` asserted the snapshot's six class counts against six numbers typed into the test — the same six literals the snapshot already contained. **Expected value and actual value were copies of each other**, so the assertion held for as long as the file was unchanged, whatever Core actually pinned; it reported an agreement it never checked, which is worse than no guard, because the row it protects reads as verified. **The drift does not stay where it starts.** `build-iso-5055-mapping.mjs` stamps `nativeEvaluability` onto EVERY row of the ISO/IEC 5055 mapping from that file — 388 rows after the recapture, 381 before — so one stale class is laundered into a derived artifact five times the size of its input, and overstates the handler backlog that is [`GT-598`](./gap-reference-catalog.md#gt-598)'s entire deliverable. The recapture→rebuild order was written down nowhere, and the mapping's own guard ran in **no workflow at all**. **FIXED 2026-07-29:** a capture script that runs Core's REAL triage through `ts-node` instead of reimplementing the classification, the measurement extracted out of the jest spec into `test/rule-corpus-triage.ts` so a script can reach it — that unreachability is why the file was maintained by hand — snapshot-vs-fresh-triage assertions in both directions, a documentation-job guard that reads Core's pinned counts OUT of the spec and **throws** when the literal is missing or reshaped, and both steps declared as links in the [`GT-630`](./gap-reference-catalog.md#gt-630) derived-artifact chain. **Recaptured on 2026-07-29 after merging the fix into the current line, and the drift had grown while it was in flight:** `native-handler` 139 → 151, `documentation-only` 129 → 136, the handler backlog 60 → **48**, corpus 379 → 386, mapping 381 → 388 rows. Twelve of those handlers were closed by [`GT-595`](./gap-reference-catalog.md#gt-595) and [`GT-632`](./gap-reference-catalog.md#gt-632) while the hand-maintained snapshot went on reporting them as backlog — the defect demonstrating itself one more time, on numbers nobody typed twice. | | | `Governance` | Cross | P1 | S | `DONE` | | [`GT-632`](./gap-reference-catalog.md#gt-632) | **The MCP denied every tool in production from a path typo, and the evidence that it worked was untracked local state.** `abac-evaluator.ts:322` resolved the compiled policy at `/sdk/cli/rulesets/opa/policy.wasm` — the PRE-`src/`-refactor path. The file lives at `src/sdk/cli/…`. When the stat fails the evaluator denies fail-closed in production, so on a clean checkout with `NODE_ENV=production` the compiled policy never loads and EVERY MCP tool is refused. It looked green because `policy.wasm` is gitignored: a stale file left at the old path by an earlier era made the load succeed locally, and the test that covers it passed against an artifact no fresh checkout would have. Same shape as `GT-625` — a green that depended on untracked state — but on the authorization path. Found on 2026-07-29 while investigating why `abac-rego-parity.spec.ts` had gone red; the rego itself was correct (`opa eval` returns `allow: true` for an architect in production). **`40-validate-path-literals` cannot see this class:** it scans string literals, and this is a `path.join(corePath, 'sdk', 'cli', …)` construction. Twelve such joins survive the refactor across `src/**` — the other eleven are in `opa-input-builder.ts` and `cli-release-rule.handler.ts`, where a wrong path produces a false NEGATIVE in an evaluator rather than a denial, which is quieter and no less wrong. **FIXED for the authorization path on 2026-07-29:** the evaluator now tries both locations, newest first — not sloppiness, but what keeps an already-deployed image working while the path is corrected. Reverting the fix turns `abac-rego-parity.spec.ts` red (1 of 11); with it, mcp-server is 434/434. The other eleven joins and the guard blind spot remain. **Criterion 2 CLOSED 2026-07-29 — and two of the eleven were wrong in a way the prefix alone would not have fixed.** Eight were plain survivors of the `src/` move and took the prefix. The other three were worse: two pointed at `sdk/cli/src/core/mcp/server.ts`, a file that left the CLI entirely when mcp-server became its own package (now `src/packages/mcp-server/src/mcp/mcp-server.service.ts`); and one asserted a per-package `package-lock.json`, which npm WORKSPACES forbids — there is exactly one lockfile, at the root, so that check could only ever be false. A rule nobody can satisfy is not a stricter rule. **The specs and parity fixtures encoded the old layout too** (`cli-release-readiness.fixture.json`, `mcp.fixture.json`), which is why they had stayed green over paths that do not exist: the fixture and the code agreed with each other and neither agreed with the repository. Both are corrected. core-domain 1367/1367, `28-native-evaluator-parity` exit 0. **Criterion 3 CLOSED 2026-07-29 — the guard's first run was mostly wrong, which is the finding.** `47-validate-joined-paths` reported 19 broken paths; only 3 were defects. The other 16 would each have been made WORSE by a fix: six had base `root`, a PARAMETER naming the customer workspace under evaluation, so correcting them would break evaluation for every satellite; nine were members of FALLBACK CHAINS that are correct when one of four layouts resolves, and the natural way to silence those is to delete the fallbacks, breaking deployed images; one was a PROHIBITION whose absence IS the passing state, so correcting it would have inverted the rule. Chains are now detected structurally (array siblings, or the arms of a ternary); prohibitions cannot be, so they carry a written reason. The three real defects: `opa-evaluator.ts` resolved `policy.wasm` and the input schemas without the `src/` prefix, same fail-closed class as the P0 above; `satellite-upgrade-diff.ts` probed `rulesets` behind an early return, reporting zero ruleset changes on every satellite upgrade; `artifact-path-resolver.ts` named an `adr-matrix.json` present in no layout. 11 negative fixtures, both anti-vacuous floors, and `43-validate-guard-negative-fixtures` OBSERVED it red across 36 guards. A known leniency (two joins as sibling arguments) is recorded in its header rather than hidden. | | | `MCP Server` | Cross | P0 | M | `DONE` | | [`GT-631`](./gap-reference-catalog.md#gt-631) | **Carved out of [`GT-573`](./gap-reference-catalog.md#gt-573) so its cross-repo half is tracked rather than absorbed into a closure.** The Core half of GT-573's third criterion is done: `evaluation-context` and `evaluation-result` are pinned in `MACHINE_CONTRACT_SET` alongside `gate-evidence` and `output-envelope`, so a change to either shape now fails `10-validate-contract-conformance` instead of silently reaching consumers. What remains is in another repository and cannot be done from here: the **Tracker** must re-pin those two schemas in its own `contracts/evolith-core-contracts.json` with their sha256, bind the three published fixtures (`EVALUATION_RESULT_PASS/FAIL/OPA_GATE_FAIL`) in a DTO binding test, drop or document `resolvedTopology` (which the canonical result never carried), rename its gate `phase` to `phaseId`, and delete its fall-through to `SKIPPED` for a payload carrying a non-blank `overallVerdict`. Until it does, a Core-side shape change is caught here and still surprises the consumer. | | | `Evolith Tracker` | Cross | P1 | S | `PENDING` | @@ -650,7 +650,7 @@ This board is the single source of truth for technical debt, gaps, opportunities | [`GT-246`](./gap-reference-catalog.md#gt-246) | Implement Chaos Mesh/Litmus experiments | | | `QA` | Cross | P3 | L | `DONE` | -**Progress:** 595 / 635 done · 14 in progress · 22 pending · 4 deferred +**Progress:** 596 / 635 done · 14 in progress · 21 pending · 4 deferred **Wave 2026-06-23 (Winston deep audit III):** Added 14 new gaps `GT-212`…`GT-225` from the Winston Audit Playbook covering: ADR status hygiene (GT-212), topology manifest metadata + operational budgets + guidance corpus (GT-213, GT-217, GT-219), REST controller observability + OpenAPI (GT-214, GT-215), OPA input-schema parity + per-topology test density (GT-216, GT-222), SDLC Phase 05 rollback + on-call templates (GT-218), CLI branch coverage + envelope format coverage + skip-list cleanup (GT-220, GT-224, GT-225), MCP HTTP audit logging (GT-221), and cross-surface parity e2e tests (GT-223). diff --git a/reference/core/control-center/maturity-reports/executive-summary.es.md b/reference/core/control-center/maturity-reports/executive-summary.es.md index 318a8f71..88120ed5 100644 --- a/reference/core/control-center/maturity-reports/executive-summary.es.md +++ b/reference/core/control-center/maturity-reports/executive-summary.es.md @@ -27,8 +27,8 @@ La forma correcta de usar este resumen es simple: si necesitas contexto, abre so |---:|---|---|---| | 1 | Bloqueadores P0 | Impiden afirmar readiness productivo o release mayor. | [GT-602](../gaps/gap-reference-catalog.es.md#gt-602), [GT-603](../gaps/gap-reference-catalog.es.md#gt-603), [GT-604](../gaps/gap-reference-catalog.es.md#gt-604), [GT-435](../gaps/gap-reference-catalog.es.md#gt-435) | | 2 | Área de mayor riesgo | `Evolith Core` tiene la mayor carga ponderada abierta. | [GT-583](../gaps/gap-reference-catalog.es.md#gt-583), [GT-589](../gaps/gap-reference-catalog.es.md#gt-589), [GT-614](../gaps/gap-reference-catalog.es.md#gt-614), [GT-587](../gaps/gap-reference-catalog.es.md#gt-587), [GT-591](../gaps/gap-reference-catalog.es.md#gt-591), [GT-590](../gaps/gap-reference-catalog.es.md#gt-590), +2 | -| 3 | Ganancias rápidas | Alta criticidad con complejidad XS/S. | [GT-597](../gaps/gap-reference-catalog.es.md#gt-597), [GT-631](../gaps/gap-reference-catalog.es.md#gt-631), [GT-634](../gaps/gap-reference-catalog.es.md#gt-634) | -| 4 | Ola P1 | Endurecimiento siguiente después de limpiar P0. | [GT-597](../gaps/gap-reference-catalog.es.md#gt-597), [GT-631](../gaps/gap-reference-catalog.es.md#gt-631), [GT-634](../gaps/gap-reference-catalog.es.md#gt-634), [GT-324](../gaps/gap-reference-catalog.es.md#gt-324), [GT-578](../gaps/gap-reference-catalog.es.md#gt-578), [GT-580](../gaps/gap-reference-catalog.es.md#gt-580), [GT-584](../gaps/gap-reference-catalog.es.md#gt-584), [GT-605](../gaps/gap-reference-catalog.es.md#gt-605), +7 | +| 3 | Ganancias rápidas | Alta criticidad con complejidad XS/S. | [GT-597](../gaps/gap-reference-catalog.es.md#gt-597), [GT-631](../gaps/gap-reference-catalog.es.md#gt-631) | +| 4 | Ola P1 | Endurecimiento siguiente después de limpiar P0. | [GT-597](../gaps/gap-reference-catalog.es.md#gt-597), [GT-631](../gaps/gap-reference-catalog.es.md#gt-631), [GT-324](../gaps/gap-reference-catalog.es.md#gt-324), [GT-578](../gaps/gap-reference-catalog.es.md#gt-578), [GT-580](../gaps/gap-reference-catalog.es.md#gt-580), [GT-584](../gaps/gap-reference-catalog.es.md#gt-584), [GT-605](../gaps/gap-reference-catalog.es.md#gt-605), [GT-606](../gaps/gap-reference-catalog.es.md#gt-606), +6 | | 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-614](../gaps/gap-reference-catalog.es.md#gt-614), [GT-616](../gaps/gap-reference-catalog.es.md#gt-616), [GT-617](../gaps/gap-reference-catalog.es.md#gt-617), +12 | ## Bloqueadores Actuales @@ -46,22 +46,22 @@ La forma correcta de usar este resumen es simple: si necesitas contexto, abre so |---|---:| | Fecha canónica del tablero | 2026-07-26 | | Gaps totales | 635 | -| Gaps cerrados | 595 | -| Gaps pendientes | 40 | +| Gaps cerrados | 596 | +| Gaps pendientes | 39 | | P0 abiertos | 4 | -| P1 abiertos | 15 | +| P1 abiertos | 14 | | P2 abiertos | 18 | -| Cierre total | 93.7% | -| Registros de evidencia de cierre | 577 | +| Cierre total | 93.9% | +| Registros de evidencia de cierre | 578 | | Readiness registrado | 4 PASS | | Área | Pendientes | P0 | P1 | Primeros IDs | |---|---:|---:|---:|---| | `Evolith Core` | 8 | 0 | 2 | [GT-583](../gaps/gap-reference-catalog.es.md#gt-583), [GT-589](../gaps/gap-reference-catalog.es.md#gt-589), [GT-614](../gaps/gap-reference-catalog.es.md#gt-614), [GT-587](../gaps/gap-reference-catalog.es.md#gt-587), +4 | -| `Infra` | 5 | 0 | 3 | [GT-597](../gaps/gap-reference-catalog.es.md#gt-597), [GT-634](../gaps/gap-reference-catalog.es.md#gt-634), [GT-324](../gaps/gap-reference-catalog.es.md#gt-324), [GT-622](../gaps/gap-reference-catalog.es.md#gt-622), +1 | | `Cross` | 2 | 1 | 1 | [GT-435](../gaps/gap-reference-catalog.es.md#gt-435), [GT-448](../gaps/gap-reference-catalog.es.md#gt-448) | | `Evolith MCP` | 2 | 1 | 1 | [GT-602](../gaps/gap-reference-catalog.es.md#gt-602), [GT-606](../gaps/gap-reference-catalog.es.md#gt-606) | | `Evolith Suite` | 2 | 1 | 1 | [GT-604](../gaps/gap-reference-catalog.es.md#gt-604), [GT-605](../gaps/gap-reference-catalog.es.md#gt-605) | +| `Evolith Tracker` | 2 | 1 | 1 | [GT-603](../gaps/gap-reference-catalog.es.md#gt-603), [GT-631](../gaps/gap-reference-catalog.es.md#gt-631) | ## Fuente y Regla de Actualización diff --git a/reference/core/control-center/maturity-reports/executive-summary.md b/reference/core/control-center/maturity-reports/executive-summary.md index f73b17da..24455690 100644 --- a/reference/core/control-center/maturity-reports/executive-summary.md +++ b/reference/core/control-center/maturity-reports/executive-summary.md @@ -27,8 +27,8 @@ Use this summary with a simple rule: if you need context, open only the linked I |---:|---|---|---| | 1 | P0 blockers | They prevent production-readiness or major-release confidence. | [GT-602](../gaps/gap-reference-catalog.md#gt-602), [GT-603](../gaps/gap-reference-catalog.md#gt-603), [GT-604](../gaps/gap-reference-catalog.md#gt-604), [GT-435](../gaps/gap-reference-catalog.md#gt-435) | | 2 | Highest-risk area | `Evolith Core` has the largest weighted open load. | [GT-583](../gaps/gap-reference-catalog.md#gt-583), [GT-589](../gaps/gap-reference-catalog.md#gt-589), [GT-614](../gaps/gap-reference-catalog.md#gt-614), [GT-587](../gaps/gap-reference-catalog.md#gt-587), [GT-591](../gaps/gap-reference-catalog.md#gt-591), [GT-590](../gaps/gap-reference-catalog.md#gt-590), +2 | -| 3 | Quick wins | High criticality with XS/S complexity. | [GT-597](../gaps/gap-reference-catalog.md#gt-597), [GT-631](../gaps/gap-reference-catalog.md#gt-631), [GT-634](../gaps/gap-reference-catalog.md#gt-634) | -| 4 | P1 wave | Next hardening after P0 is cleared. | [GT-597](../gaps/gap-reference-catalog.md#gt-597), [GT-631](../gaps/gap-reference-catalog.md#gt-631), [GT-634](../gaps/gap-reference-catalog.md#gt-634), [GT-324](../gaps/gap-reference-catalog.md#gt-324), [GT-578](../gaps/gap-reference-catalog.md#gt-578), [GT-580](../gaps/gap-reference-catalog.md#gt-580), [GT-584](../gaps/gap-reference-catalog.md#gt-584), [GT-605](../gaps/gap-reference-catalog.md#gt-605), +7 | +| 3 | Quick wins | High criticality with XS/S complexity. | [GT-597](../gaps/gap-reference-catalog.md#gt-597), [GT-631](../gaps/gap-reference-catalog.md#gt-631) | +| 4 | P1 wave | Next hardening after P0 is cleared. | [GT-597](../gaps/gap-reference-catalog.md#gt-597), [GT-631](../gaps/gap-reference-catalog.md#gt-631), [GT-324](../gaps/gap-reference-catalog.md#gt-324), [GT-578](../gaps/gap-reference-catalog.md#gt-578), [GT-580](../gaps/gap-reference-catalog.md#gt-580), [GT-584](../gaps/gap-reference-catalog.md#gt-584), [GT-605](../gaps/gap-reference-catalog.md#gt-605), [GT-606](../gaps/gap-reference-catalog.md#gt-606), +6 | | 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-614](../gaps/gap-reference-catalog.md#gt-614), [GT-616](../gaps/gap-reference-catalog.md#gt-616), [GT-617](../gaps/gap-reference-catalog.md#gt-617), +12 | ## Current Blockers @@ -46,22 +46,22 @@ Use this summary with a simple rule: if you need context, open only the linked I |---|---:| | Canonical board date | 2026-07-26 | | Total gaps | 635 | -| Closed gaps | 595 | -| Open gaps | 40 | +| Closed gaps | 596 | +| Open gaps | 39 | | Open P0 | 4 | -| Open P1 | 15 | +| Open P1 | 14 | | Open P2 | 18 | -| Total closure | 93.7% | -| Closure evidence records | 577 | +| Total closure | 93.9% | +| Closure evidence records | 578 | | Recorded readiness | 4 PASS | | Area | Open | P0 | P1 | First IDs | |---|---:|---:|---:|---| | `Evolith Core` | 8 | 0 | 2 | [GT-583](../gaps/gap-reference-catalog.md#gt-583), [GT-589](../gaps/gap-reference-catalog.md#gt-589), [GT-614](../gaps/gap-reference-catalog.md#gt-614), [GT-587](../gaps/gap-reference-catalog.md#gt-587), +4 | -| `Infra` | 5 | 0 | 3 | [GT-597](../gaps/gap-reference-catalog.md#gt-597), [GT-634](../gaps/gap-reference-catalog.md#gt-634), [GT-324](../gaps/gap-reference-catalog.md#gt-324), [GT-622](../gaps/gap-reference-catalog.md#gt-622), +1 | | `Cross` | 2 | 1 | 1 | [GT-435](../gaps/gap-reference-catalog.md#gt-435), [GT-448](../gaps/gap-reference-catalog.md#gt-448) | | `Evolith MCP` | 2 | 1 | 1 | [GT-602](../gaps/gap-reference-catalog.md#gt-602), [GT-606](../gaps/gap-reference-catalog.md#gt-606) | | `Evolith Suite` | 2 | 1 | 1 | [GT-604](../gaps/gap-reference-catalog.md#gt-604), [GT-605](../gaps/gap-reference-catalog.md#gt-605) | +| `Evolith Tracker` | 2 | 1 | 1 | [GT-603](../gaps/gap-reference-catalog.md#gt-603), [GT-631](../gaps/gap-reference-catalog.md#gt-631) | ## Source and Refresh Rule diff --git a/reference/core/control-center/maturity-reports/maturity-reconciliation.json b/reference/core/control-center/maturity-reports/maturity-reconciliation.json index d0ea1559..f29872a3 100644 --- a/reference/core/control-center/maturity-reports/maturity-reconciliation.json +++ b/reference/core/control-center/maturity-reports/maturity-reconciliation.json @@ -4,13 +4,13 @@ "asOf": "2026-07-26", "gaps": { "total": 635, - "done": 595, - "pending": 22, + "done": 596, + "pending": 21, "inProgress": 14, "deferred": 4 }, "evidence": { - "closureRecords": 577, + "closureRecords": 578, "cliPackage": "@beyondnet/evolith-cli@1.2.2", "adrCount": 139, "rulesetCount": 175,