# E-033 — La suite de tests no pudo sobrevivir a su propia rotación de ronda

- Fecha: 2026-08-04
- Estado: defecto de acoplamiento confirmado; no corregido, porque el remedio cae fuera del scope declarado.

## Jerarquía y trayectoria

- Objetivo principal: `O-ADOPT`, hacer adoptable a Objective Guard mediante documentación con el inglés como idioma principal.
- Ronda anterior: `R-EVIDENCE-CONTEXT`.
- Ronda actual: `R-DOCS-EN-FIRST`, scope `README*.md,AGENTS.md,docs/**,scripts/**,package.json`.

Abrir la ronda con `next-round` cambió dos cosas en `oguard.contract.json`: el id del objetivo (`O-ALIGN` → `O-ADOPT`) y los ids de checks declarados (`package-test` → `docs`, `test`). Ambas son consecuencias ordinarias y documentadas de una transición.

`test/contract.test.mjs` carga el `oguard.contract.json` vivo y afirma literales específicos de la ronda:

- la línea 9 afirma `round.parent_objective_id === "O-ALIGN"`;
- las líneas 67-68 construyen un fixture de `task` cuyos criterios de aceptación referencian el check id `"package-test"`.

Ambas fallaron en el momento en que aterrizó la transición, sin tocar código de producto.

## Comportamiento esperado y real

Una transición de ronda es una operación de primera clase de este producto. Se esperaba que la suite de tests fuera invariante respecto de qué ronda está abierta. En cambio, una transición legítima puso la suite en rojo —139 de 141 tests en pass— sin que cambiara ningún comportamiento.

El guard sí se comportó correctamente. `align` devolvió `pass` con 114 archivos y `outside_scope_ratio = 0`. `round-end --mode fast` reportó `verification: complete`, el check `docs` en `pass`, el check `test` en `fail` con razón `command-failed`, el criterio `C-DOCS-EN` violado y **0 hallazgos de detectores**. La ronda no cerró, que es el resultado correcto ante un check declarado que falla.

## Evidencia

- `npm test`: 141 tests, 139 pass, 2 fail — `Expected values to be strictly equal: actual 'O-ADOPT', expected 'O-ALIGN'` y `AC-BEHAVIOR references unknown check: package-test`;
- `node src/cli.mjs align --json`: `status=pass`, 114 archivos, 7634 inserciones, `outside_scope_files=[]`;
- `node src/cli.mjs round-end --mode fast --json`: `status=block`, checks ejecutados `oguard:scripts/validate-docs.mjs`, `docs`, `diff-check`, `oguard-list`; omitido como advisory `package-test`;
- `node scripts/validate-docs.mjs`: 948 controles aprobados.

## Clasificación y límites

Es un **defecto de acoplamiento en la suite de tests**, no un falso positivo ni un falso negativo de algún detector. Pertenece a la misma familia que E-027: un test que lee el artefacto de contabilidad vivo mide la ronda que está abierta, no el comportamiento del producto.

No es evidencia de que el guard haya detectado el acoplamiento. El fallo salió del comando `npm test` ordinario, tal como habría salido sin guard. Lo que aportó el guard fue negarse a cerrar la ronda por encima de eso.

## Corrección de dirección

El arreglo es chico y conocido: afirmar la coherencia jerárquica contra el propio `objective.id` del contrato en lugar de un literal, y que el fixture de obligaciones declare el check id que referencia en lugar de tomar prestado el que declaró la ronda vigente.

**No se aplicó.** `test/contract.test.mjs` está fuera del scope que declaró esta ronda, y editarlo habría sido una expansión de scope no anunciada dentro del repositorio cuyo propósito es detectar exactamente eso: el riesgo registrado en E-003, E-020 y E-026.

La cadena de remedio documentada (E-024) es `accept-baseline --reason --actor` y después `next-round` declarando `test/**`. `accept-baseline` exige árbol limpio, o sea que exige commitear primero el trabajo de esta ronda. Esa es una decisión del mantenedor, no del agente, así que la ronda quedó abierta y se reportó.

## Impacto sobre la tesis

Refuerza una afirmación acotada: un check declarado y reproducible atrapó un bloqueante real en el cierre, y el scope por paths evitó un arreglo cómodo dentro de la ronda que habría escondido la frontera.

También expone un costo de flujo que la tesis debe contemplar. Sin remedio en scope y con árbol sucio, una ronda no puede cerrar ni rotar sin una decisión humana. Comportamiento correcto y baja fricción no son la misma propiedad, y este episodio es evidencia de que la segunda está peor de lo supuesto.

## Resolución — 2026-08-04, desvío declarado

Se le presentaron al mantenedor las dos opciones anteriores y eligió el desvío declarado por sobre la cadena de `accept-baseline`, con el argumento de que el arreglo es trivial, su causa es la transición que la propia ronda necesitó, y un desvío visible es más honesto que un fallo aceptado que se repara un minuto después. Los párrafos anteriores se conservan como el estado del análisis previo a esa decisión.

Ambas afirmaciones se desacoplaron dentro de `R-DOCS-EN-FIRST`:

- la afirmación de jerarquía ahora compara `round.parent_objective_id` contra el propio `objective.id` del contrato en lugar de un literal;
- el fixture de obligaciones ahora declara el check que referencia (`fixture-test`) en lugar de tomar prestado el que declaró la ronda vigente.

`npm test` devuelve 141/141. `align` devuelve `pass` con `outside_scope_files = ["test/contract.test.mjs"]`, `outside_scope_ratio = 0.008547`, `scope_coverage = 0.991453`, modo `warn`: la frontera se conserva como superficie observada en lugar de borrarse, que es el mismo comportamiento registrado en E-026.

Lo que esto **no** establece: el desvío fue autorizado por una persona, no justificado por el guard. El contrato no se editó después para cubrir `test/**`, así que el registro sigue mostrando trabajo fuera del scope declarado. Quien lea esto más adelante debe interpretar ese ratio como una excepción deliberada y atribuida, no como evidencia de que el scope era correcto.

## Seguimiento verificable

- Desacoplar las dos afirmaciones bajo una ronda que declare `test/**`, y confirmar que `npm test` queda verde atravesando una transición.
- Agregar una regresión que abra una ronda sucesora sintética en un repositorio temporal y afirme que la suite queda verde atravesando la rotación: el check faltante que habría atrapado esto antes de bloquear un cierre.
- Decidir si los tests que leen el contrato vivo deben aislarse como tests de dogfooding o convertirse en fixtures, para que la suite deje de depender de la ronda vigente.
