diff --git a/reference/core/control-center/evidence/gap-closure-evidence.json b/reference/core/control-center/evidence/gap-closure-evidence.json index c8aee44d..46588cd4 100644 --- a/reference/core/control-center/evidence/gap-closure-evidence.json +++ b/reference/core/control-center/evidence/gap-closure-evidence.json @@ -8853,6 +8853,33 @@ ], "dependencyDisposition": "accepted-scope", "dependencyRationale": "What this gate does NOT do is stated rather than left to be discovered. It detects a security-marked commit that no published version contains; it cannot detect a security fix nobody marked, which is a convention problem and belongs to GT-623 (release-please derives no bump from the non-conventional `security(...)` type this repository has used twice). It also reports, but does not fail on, a package whose published version is absent from this history -- an INEXACT boundary after a history rewrite -- because scanning the whole history conservatively is better than skipping the package silently. Finally, the gate needs the network: without a registry answer the package is skipped and named, and a run where EVERY package is skipped is a failure rather than a pass." + }, + { + "id": "GT-633", + "closedAt": "2026-07-30", + "closureCommit": "29e940d4", + "evidence": [ + "src/rulesets/standards/capture-native-evaluability-snapshot.mjs", + "src/rulesets/standards/iso-5055-mapping.test.mjs", + "src/packages/core-domain/test/rule-corpus-triage.ts", + "src/packages/core-domain/src/application/validators/rule-corpus-triage.spec.ts", + ".harness/scripts/ci/41-validate-evidence-commands.mjs", + ".harness/scripts/ci/46-validate-derived-artifact-order.mjs", + ".github/workflows/docs.yml", + ".github/workflows/ci-cd.yml" + ], + "validationCommands": [ + "node src/rulesets/standards/capture-native-evaluability-snapshot.mjs --check -> the snapshot is a faithful capture of the Core triage (389 rules). Before the fix there was no capture script at all, so this command could not exist.", + "node --test src/rulesets/standards/iso-5055-mapping.test.mjs -> 9/9. The guard now reads PINNED_CLASS_COUNTS out of rule-corpus-triage.spec.ts and THROWS when it cannot find it; the six hardcoded literals it used to compare against are gone.", + "npm run test --workspace src/packages/core-domain -> 1368 passed / 125 suites, including the GT-598 block that compares the committed snapshot against a freshly computed triage in both directions.", + "node .harness/scripts/ci/46-validate-derived-artifact-order.mjs --verbose -> 5 links, 7 artifacts, chain current AND at a fixed point. Swapping the capture/mapping pair is rejected: \"ISO/IEC 5055 corpus mapping consumes native-evaluability-snapshot.json, which a LATER link writes.\"", + "node --test .harness/scripts/ci/46-validate-derived-artifact-order.test.mjs -> 8/8, with link positions derived from CHAIN rather than typed as literals.", + "node --test .harness/scripts/ci/41-validate-evidence-commands.test.mjs -> 40/40, and 5 of the 6 new tests observed RED against the pre-fix parser.", + "NEGATIVE, both failure modes: reverting documentation-only to 129 turns the mapping guard red, and building the mapping from a stale snapshot turns the stamp test red. The laundering path is caught.", + "RATCHET, measured on the runner rather than locally: 309 -> 304 -> 40 -> 38 dead references, the budget lowered at each step only from the count the CI job printed. 38 remain and are not path rot." + ], + "dependencyDisposition": "accepted-scope", + "dependencyRationale": "What this closure does NOT do is stated rather than left to be discovered. 38 dead references survive the migration and are deliberately left dead, because each would require INVENTING evidence to clear: 15 name the vendored, gitignored .harness/bin/opa (environment-sensitive, not a wrong path); 4 name a script ABSORBED into 04-check-bilingual-parity rather than moved, so repointing them would claim a command nobody ran; 6 name artifacts that were deleted; 5 are root-level npm test recorded without the workspace they ran in, which cannot be inferred; the rest are build output and prose the extractor reads as commands. A recorded command may be repaired when its artifact MOVED, never when it was deleted, replaced or absorbed. Separately, the two guard PARSER defects found here were fixed rather than tolerated (0aee3eb0), so they are not part of this remainder. The budget rule stands: --max-dead may be lowered only from a count the CI job itself printed, never from a local measurement." } ] } 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 8680e550..1d554674 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.es.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.es.md @@ -7855,6 +7855,31 @@ Serie histórica de gaps registrada en el antiguo `gap-analysis-core.es.md`, pre - [x] El tipo `security` está declarado en la config de commitlint con un mapeo explícito de salto de versión, o se deja de usar. - [x] Ningún hook de `.husky/` imprime un mensaje de "skipping" y sale con cero — un hook que no puede correr se borra, no se silencia. +#### GT-633 + +**Título:** Una guarda cuyo valor esperado era una copia de su valor real + +- **Propósito:** Quitar un check que solo podía pasar, y convertir el snapshot que vigila en una captura real en vez de una copia mantenida a mano. +- **Evidencia:** `iso-5055-mapping.test.mjs` afirmaba `evaluability.counts` contra seis literales idénticos a los que ya había leído dentro de `native-evaluability-snapshot.json`. En `ac8594df` Core fijaba `documentation-only: 136` y el snapshot decía 129; el test pasó. En `integration/gt-572-595-604` Core fijaba `native-handler: 151` / `unimplemented-native: 48` frente a 139/60 del snapshot; el test volvió a pasar. Las doce reglas cerradas por esa integración seguían registradas como `unimplemented-native`. No existía script de captura: `grep` del nombre devolvía el consumidor, dos READMEs y el test. +- **Componente:** `Governance` · **Criticidad:** P1 · **Complejidad:** M +- **Procedencia:** Encontrado el 2026-07-29 al leer la afirmación de la propia guarda de que leía `rule-corpus-triage.spec.ts`. Cerrado en `c856752b`, `beb6f50f`, `104fd7a1`, `0aee3eb0` y `29e940d4`. +- **Criterios de aceptación:** + - [x] La guarda deriva sus cuentas esperadas de la fuente de verdad de Core, y LANZA en vez de pasar cuando no las encuentra. + - [x] Existe un script de captura, ejecuta el triage real de Core, y el snapshot se recaptura desde él — y después se reconstruye el mapeo, en ese orden. + - [x] La suite de Core compara el snapshot comiteado contra un triage recalculado, cuentas y clase por regla, en ambos sentidos. + - [x] El orden `captura → mapeo` está declarado como dato y una declaración intercambiada se rechaza. + - [x] La guarda del mapeo corre en un workflow; antes no corría en ninguno. + - [x] El ratchet de referencias muertas solo baja desde un conteo que imprimió el propio job de CI, nunca desde una medición local. +- **Modos de fallo transferibles:** + - Una guarda cuyo valor esperado es copia de su valor real solo puede pasar. La comparación debe alcanzar una fuente que el artefacto bajo prueba no controle. + - Un artefacto derivado estampado desde otro blanquea una entrada obsoleta dentro de algo mayor mientras el `--check` de cada uno sigue pasando — por eso el orden va en una cadena, no en prosa. + - Una medición alcanzable solo por su propio test no se puede capturar, así que la copia consumidora se mantiene a mano y deriva. Extraerla fue el arreglo. + - Un `dead` falso en una guarda de evidencia se lee como evidencia obsoleta e invita a "reparar" registros que ya eran correctos; 27 se reordenaron alrededor de un bug de parser antes de arreglarlo. + - Posiciones de test escritas como literales (`link 2 of 3`) hacen que una adición ajena parezca una regresión en otro sitio; derívalas. + - Pillado en el acto: dos tests escritos para probar un arreglo de parser pasaban también contra el parser VIEJO, porque la raíz de la fixture no declaraba `scripts` y la rama bajo prueba se saltaba. Con la fixture viva, los seis tests fallaron 5 de 6 contra el parser viejo. +- **Remanente abierto:** sobreviven 38 referencias muertas que no son rot de rutas — el desglose está en la fila. Ninguna es reparable sin inventar evidencia. La regla vigente registrada sobre el flag `--max-dead`: el presupuesto solo baja desde un conteo que imprimió el job de CI. +- **Referencias:** [`GT-598`](#gt-598) (origen), [`GT-578`](#gt-578) (el ratchet), [`GT-595`](#gt-595), [`GT-630`](#gt-630) (la cadena), [`GT-632`](#gt-632). + #### GT-632 **Title:** La política ABAC compilada se resuelve en una ruta previa al refactor, denegando toda herramienta MCP en producción diff --git a/reference/core/control-center/gaps/gap-reference-catalog.md b/reference/core/control-center/gaps/gap-reference-catalog.md index 1a10a43b..ceca2b81 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.md @@ -7950,6 +7950,31 @@ Historical gap series tracked in the former `gap-analysis-core.md`, preserved fo - [x] The `security` type is either declared in the commitlint config with an explicit version-bump mapping, or removed from use. - [x] No hook in `.husky/` prints a "skipping" message and exits zero — a hook that cannot run is deleted, not silenced. +#### GT-633 + +**Title:** A guard whose expected value was a copy of its actual value + +- **Purpose:** Remove a check that could only ever pass, and make the snapshot it guards a real capture rather than a hand-maintained copy. +- **Evidence:** `iso-5055-mapping.test.mjs` asserted `evaluability.counts` against six literals identical to those inside `native-evaluability-snapshot.json`, which it had already read. On `ac8594df` Core pinned `documentation-only: 136` and the snapshot said 129; the test passed. On `integration/gt-572-595-604` Core pinned `native-handler: 151` / `unimplemented-native: 48` against the snapshot's 139/60; the test passed. All twelve rules closed by that integration were still recorded as `unimplemented-native`. No capture script existed: `grep` for the filename returned the consumer, two READMEs and the test. +- **Component:** `Governance` · **Criticality:** P1 · **Complexity:** M +- **Provenance:** Found 2026-07-29 while reading the guard's own claim that it read `rule-corpus-triage.spec.ts`. Closed across `c856752b`, `beb6f50f`, `104fd7a1`, `0aee3eb0` and `29e940d4`. +- **Acceptance criteria:** + - [x] The guard derives its expected counts from the Core source of truth, and THROWS rather than passing when it cannot find them. + - [x] A capture script exists, runs Core's real triage, and the snapshot is recaptured from it — then the mapping rebuilt, in that order. + - [x] Core's own suite compares the committed snapshot against a freshly computed triage, counts and per-rule class, in both directions. + - [x] The `capture → mapping` order is declared as data and a swapped declaration is rejected. + - [x] The mapping guard runs in a workflow; it previously ran in none. + - [x] The dead-reference ratchet is lowered only from a count the CI job itself printed, never from a local measurement. +- **Transferable failure modes:** + - A guard whose expected value is a copy of its actual value can only ever pass. The comparison must reach a source the artifact under test does not control. + - A derived artifact stamped from another launders a stale input into something larger while each artifact's own `--check` still passes — which is why order belongs in a chain, not in prose. + - A measurement reachable only by its own test cannot be captured, so the consumer copy gets hand-maintained and drifts. Extracting it was the fix. + - A false `dead` in an evidence guard reads as stale evidence and invites "repairing" records that were already correct; 27 were reordered around one parser bug before it was fixed. + - Test positions typed as literals (`link 2 of 3`) make an unrelated addition look like a regression elsewhere; derive them. + - Caught in the act: two tests written to prove a parser fix passed against the OLD parser too, because the fixture root declared no `scripts` and the branch under test was skipped. With the fixture made live the six tests failed 5 of 6 against the old parser. +- **Open remainder:** 38 dead references survive and are not path rot — see the row for the breakdown. None is repairable without inventing evidence. The standing rule recorded above the `--max-dead` flag: the budget may be lowered only from a count the CI job printed. +- **References:** [`GT-598`](#gt-598) (origin), [`GT-578`](#gt-578) (the ratchet), [`GT-595`](#gt-595), [`GT-630`](#gt-630) (the chain), [`GT-632`](#gt-632). + #### GT-632 **Title:** The compiled ABAC policy is resolved at a pre-refactor path, denying every MCP tool in production diff --git a/reference/core/control-center/gaps/gap-tracking.es.md b/reference/core/control-center/gaps/gap-tracking.es.md index 65235911..5ee65001 100644 --- a/reference/core/control-center/gaps/gap-tracking.es.md +++ b/reference/core/control-center/gaps/gap-tracking.es.md @@ -13,6 +13,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-633`](./gap-reference-catalog.es.md#gt-633) | **Una guarda cuyo valor esperado era una copia de su valor real, y los cuatro modos de fallo encontrados al quitarla.** `native-evaluability-snapshot.json` decía en su propia cabecera que era una CAPTURA. Nada lo capturaba — no existía script de captura en el repositorio — así que se mantenía a mano y derivó: declaraba `documentation-only: 129` mucho después de que Core fijara 136, y seguía llamando `unimplemented-native` a doce reglas después de que [`GT-595`](./gap-reference-catalog.es.md#gt-595) y [`GT-632`](./gap-reference-catalog.es.md#gt-632) les dieran handlers. Su guarda no podía verlo: `iso-5055-mapping.test.mjs` comparaba las cuentas del snapshot contra seis números escritos en el propio test — **los mismos seis que son literales dentro del snapshot** — así que esperado y real eran copias, y pasaba mientras el fichero no se tocara, fijara Core lo que fijara. Su comentario afirmaba leer `rule-corpus-triage.spec.ts`; nunca abrió ese fichero. **La deriva no quedó contenida:** `build-iso-5055-mapping.mjs` estampa `nativeEvaluability` en cada fila de un artefacto de 391 filas DESDE el snapshot, así que reconstruir antes de recapturar blanquea una clase obsoleta dentro de algo mayor mientras ambos artefactos siguen pasando su propio `--check`. **CERRADO en cinco merges.** La medición salió del spec a `core-domain/test/rule-corpus-triage.ts` — que solo jest pudiera alcanzarla es POR LO QUE la copia consumidora se mantenía a mano —, se escribió un script de captura, la suite de Core compara ahora el snapshot comiteado contra un triage recalculado, y la guarda sin dependencias lee `PINNED_CLASS_COUNTS` del spec y LANZA si no lo encuentra, porque no hallar la fuente de verdad jamás debe leerse como estar de acuerdo con ella. La guarda del mapeo no corría en ningún workflow; ahora sí. El orden `captura → mapeo` está declarado en el CHAIN de `46-validate-derived-artifact-order`, e intercambiar el par se rechaza por nombre. De paso, el ratchet de evidencia pasó de **309 → 38**: cinco erratas de transcripción, luego 264 referencias que eran un solo evento (el refactor a `src/`), y luego dos defectos de parser en `41-validate-evidence-commands` que reportaban `dead` para comandos válidos — un `dead` falso se lee como evidencia obsoleta y ya había provocado que se reordenaran 27 registros a su alrededor. **Las 38 restantes NO son rot de rutas y no deben "repararse" hasta el silencio:** 15 nombran el binario vendorizado y gitignorado `.harness/bin/opa`, 4 nombran un script ABSORBIDO por `04-check-bilingual-parity` en vez de movido, 6 nombran artefactos borrados, 5 son `npm test` de raíz registrados sin el workspace en que corrieron, y el resto es salida de build y prosa que el extractor lee como comandos. | | | `Governance` | Cross | P1 | M | `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` | | [`GT-630`](./gap-reference-catalog.es.md#gt-630) | **Los artefactos derivados tienen un ORDEN de dependencia y nada lo exigía — costó tres checks requeridos en rojo solo el 2026-07-28.** `generate-executive-summary.mjs` lee `maturity-reconciliation.json`, que a su vez se deriva del tablero de gaps. Generar el resumen ANTES de reconciliar captura un valor que el reconciliador está a punto de mover. El `--check` de cada artefacto pasa en ese momento — el resumen coincide de verdad con lo que se acaba de escribir — así que `ci-runner governance` sale verde en local y `Validate documentation` sale rojo en el runner, donde los pasos corren en el orden declarado. No es un fallo de artefacto rancio, que los `--check` individuales ya cazan; es un fallo de ORDEN, invisible para cualquier comprobación que mire un artefacto cada vez. La secuencia correcta (reconciliar → suite → generar → validar) no estaba escrita en ningún sitio del repositorio, que es la razón de que el mismo error se cometiera tres veces en una sesión por alguien a quien ya le había mordido dos. **CERRADO al registrarse.** La cadena es ahora DATOS en `46-validate-derived-artifact-order.mjs` — productor, qué consume, qué escribe — recorrida en orden de dependencia y cableada en `Validate documentation`. Informa del PRIMER eslabón rancio con el comando que lo arregla y las entradas que deben estar al día antes que él, porque un artefacto construido sobre entrada rancia no está mal por sí mismo. Escribir los tests acotó cuánto vale la pasada de punto fijo, y ese límite queda recogido en el guard: con generadores deterministas, la comprobación de orden ya la subsume; se gana su sitio ante un generador cuya salida no depende solo de sus entradas declaradas, que es el caso que ejercita el autotest. 8 autotests, cuatro de ellos anti-vacuos. Registrado en `guard-classification.mjs`; 58 guards clasificados, 35 vistos fallar. | | | `Governance` | Cross | P2 | S | `COMPLETADO` | @@ -647,7 +648,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:** 593 / 632 completados · 14 en progreso · 21 pendientes · 4 diferidos +**Progreso:** 594 / 633 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 e2205664..5e5da5c2 100644 --- a/reference/core/control-center/gaps/gap-tracking.md +++ b/reference/core/control-center/gaps/gap-tracking.md @@ -13,6 +13,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-633`](./gap-reference-catalog.md#gt-633) | **A guard whose expected value was a copy of its actual value, and the four failure modes found while removing it.** `native-evaluability-snapshot.json` said in its own header that it was a CAPTURE. Nothing captured it — no capture script existed anywhere in the repository — so it was hand-maintained and it drifted: it declared `documentation-only: 129` long after Core pinned 136, and it went on calling twelve rules `unimplemented-native` after [`GT-595`](./gap-reference-catalog.md#gt-595) and [`GT-632`](./gap-reference-catalog.md#gt-632) gave them handlers. Its guard could not see any of it: `iso-5055-mapping.test.mjs` compared the snapshot's counts against six numbers typed into the test — **the same six that are literals inside the snapshot** — so expected and actual were copies of each other and it passed for as long as the file went untouched, whatever Core actually pinned. Its comment claimed it read `rule-corpus-triage.spec.ts`; it never opened that file. **The drift did not stay contained:** `build-iso-5055-mapping.mjs` stamps `nativeEvaluability` onto every row of a 391-row artifact FROM the snapshot, so rebuilding before recapturing launders a stale class into something larger while both artifacts still pass their own `--check`. **CLOSED across five merges.** The measurement moved out of the spec into `core-domain/test/rule-corpus-triage.ts` — being reachable only by jest is WHY the consumer copy was hand-maintained — a capture script was written, Core's suite now compares the committed snapshot against a freshly computed triage, and the dependency-free guard reads `PINNED_CLASS_COUNTS` out of the spec and THROWS if it cannot find it, because failing to find the source of truth must never read as agreement with it. The mapping guard had been running in no workflow at all; it runs now. The `capture → mapping` order is declared in `46-validate-derived-artifact-order`'s CHAIN, and swapping the pair is rejected by name. Along the way the evidence ratchet went **309 → 38**: five transcription slips, then 264 references that were one event (the `src/` refactor), then two parser defects in `41-validate-evidence-commands` that reported `dead` for valid commands — a false `dead` reads as stale evidence and had already caused 27 records to be reordered around it. **Remaining 38 are NOT path rot and must not be "repaired" into silence:** 15 name the vendored gitignored `.harness/bin/opa`, 4 name a script ABSORBED into `04-check-bilingual-parity` rather than moved, 6 name deleted artifacts, 5 are root-level `npm test` recorded without the workspace they ran in, and the rest are build output and prose the extractor reads as commands. | | | `Governance` | Cross | P1 | M | `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` | | [`GT-630`](./gap-reference-catalog.md#gt-630) | **Derived artifacts have a dependency ORDER and nothing enforced it — it cost three red required checks on 2026-07-28 alone.** `generate-executive-summary.mjs` reads `maturity-reconciliation.json`, which is itself derived from the gap board. Generate the summary BEFORE reconciling and it captures a value the reconciler is about to move. Each artifact's own `--check` then passes at that moment — the summary genuinely matches what was just written — so `ci-runner governance` goes green locally and `Validate documentation` goes red on the runner, where the steps run in the declared order. That is not a stale-artifact bug, which the individual `--check` modes already catch; it is an ORDER bug, invisible to any check that looks at one artifact at a time. The correct sequence (reconcile → suite → generate → validate) was written down nowhere in the repository, which is why the same mistake was made three times in one session by someone who had already been bitten by it twice. **CLOSED on registration.** The chain is now DATA in `46-validate-derived-artifact-order.mjs` — producer, what it consumes, what it writes — walked in dependency order, wired into `Validate documentation`. It reports the FIRST stale link with the command that fixes it and the inputs that must be current before it, because an artifact built on stale input is not independently wrong. Writing the tests bounded what the fixed-point pass is worth, and the bound is recorded in the guard: with deterministic generators, currency-in-order already subsumes it; it earns its place on a generator whose output does not depend only on its declared inputs, which is the case the self-test exercises. 8 self-tests, four of them anti-vacuous. Registered in `guard-classification.mjs`; 58 guards classified, 35 observed failing. | | | `Governance` | Cross | P2 | S | `DONE` | @@ -647,7 +648,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:** 593 / 632 done · 14 in progress · 21 pending · 4 deferred +**Progress:** 594 / 633 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 d372f522..c228861a 100644 --- a/reference/core/control-center/maturity-reports/executive-summary.es.md +++ b/reference/core/control-center/maturity-reports/executive-summary.es.md @@ -45,14 +45,14 @@ 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 | 632 | -| Gaps cerrados | 593 | +| Gaps totales | 633 | +| Gaps cerrados | 594 | | Gaps pendientes | 39 | | P0 abiertos | 4 | | P1 abiertos | 14 | | P2 abiertos | 18 | | Cierre total | 93.8% | -| Registros de evidencia de cierre | 575 | +| Registros de evidencia de cierre | 576 | | Readiness registrado | 4 PASS | | Área | Pendientes | P0 | P1 | Primeros IDs | diff --git a/reference/core/control-center/maturity-reports/executive-summary.md b/reference/core/control-center/maturity-reports/executive-summary.md index e8d146c8..f0e73b2e 100644 --- a/reference/core/control-center/maturity-reports/executive-summary.md +++ b/reference/core/control-center/maturity-reports/executive-summary.md @@ -45,14 +45,14 @@ 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 | 632 | -| Closed gaps | 593 | +| Total gaps | 633 | +| Closed gaps | 594 | | Open gaps | 39 | | Open P0 | 4 | | Open P1 | 14 | | Open P2 | 18 | | Total closure | 93.8% | -| Closure evidence records | 575 | +| Closure evidence records | 576 | | Recorded readiness | 4 PASS | | Area | Open | P0 | P1 | First IDs | diff --git a/reference/core/control-center/maturity-reports/maturity-reconciliation.json b/reference/core/control-center/maturity-reports/maturity-reconciliation.json index dc32aa02..cfdee865 100644 --- a/reference/core/control-center/maturity-reports/maturity-reconciliation.json +++ b/reference/core/control-center/maturity-reports/maturity-reconciliation.json @@ -3,14 +3,14 @@ "scope": "evolith-core", "asOf": "2026-07-26", "gaps": { - "total": 632, - "done": 593, + "total": 633, + "done": 594, "pending": 21, "inProgress": 14, "deferred": 4 }, "evidence": { - "closureRecords": 575, + "closureRecords": 576, "cliPackage": "@beyondnet/evolith-cli@1.2.2", "adrCount": 139, "rulesetCount": 175,