# OpenQodex 0.11.0 a prueba: por qué Git push puede pasar con una revisión incompleta

> Canonical: https://vxi.me/es/posts/openqodex-prepush-review-fail-open/
> Locale: es-es
> Published: 2026-10-11
> Updated: 2026-10-11
> Markdown: https://vxi.me/es/posts/openqodex-prepush-review-fail-open.md

Poner una revisión de código con IA delante de `git push` parece una medida sencilla. Antes de enviar los cambios al remoto, una herramienta los comprueba; si no se han revisado o existe un problema grave, el hook detiene el envío.

Probé [OpenQodex](https://github.com/openqodex/openqodex) 0.11.0 varias veces en repositorios Git desechables. ShellCheck sí encontró un error que había introducido deliberadamente en un script. El hook de Git también logró bloquear un push sin revisión cuando estaba configurado para ello. La sorpresa llegó con otra situación: si el revisor de IA independiente no termina su trabajo, el push no tiene por qué quedar bloqueado.

Eso **no demuestra que la revisión con IA de OpenQodex sea mala**: esa parte no llegó a superar la prueba. Lo que conviene separar son tres piezas distintas: el escaneo estático, el Reviewer independiente y la puerta de salida de Git que consulta recibos de revisión previos (Receipts). Instalar un hook no hace que las tres se ejecuten automáticamente.

## ShellCheck detectó el fallo, pero solo cuando llegó a ejecutarse

Preparé un repositorio de Git que podía borrar al terminar. Contenía dos versiones de un mismo script Shell. En la versión de partida una variable iba entre comillas dobles; en la modificación quité esas comillas a propósito para provocar la regla `SC2086` de ShellCheck.

La ejecución usó OpenQodex 0.11.0 en modo escáner, sin llamar a un modelo. Estos eran los parámetros esenciales:

```console
openqodex scan --uncommitted --only shellcheck --block-on-severity nitpick
```

He simplificado la línea para que sea legible. El script real también indicaba `--cwd`, `--report-dir` y la ruta fija del paquete verificado. **No afirmo que se ejecutara exactamente la línea anterior, carácter por carácter.** Para reproducirla hay que crear un diff auténtico en un repositorio aislado y usar la misma versión de la CLI. La [documentación del commit utilizado](https://github.com/openqodex/openqodex/blob/2f2fa9e48c1f62a6123997eb14111140d8e22a74/docs/plumbing.md) describe los argumentos.

Con la misma modificación sintética, los resultados cambiaron según la disponibilidad del escáner:

| Condiciones | Estado de ShellCheck | Hallazgos finales | Código de salida |
| --- | --- | --- | --- |
| Sin conexión y sin permitir instalaciones | `not_installed` | 0 | 0 |
| Con instalación y ejecución permitidas | `ran` | 1 (`SC2086`) | 1 |

Son observaciones depuradas de la prueba del 10 de octubre de 2026, con la versión fijada. La ejecución en línea registró `rawCount=1` y `keptCount=1`, además de `SC2086` en el informe final. En cambio, la ejecución sin instalación no llegó a utilizar ShellCheck. Sus `0 findings` no significan que el código esté limpio.

También influye la gravedad. En esta correspondencia, `SC2086` se clasifica como `nitpick`, mientras que el valor predeterminado de `review.severity_threshold` en OpenQodex es `minor`. Si solo miras los hallazgos visibles por defecto, puedes interpretar que el escáner no encontró nada. Ya habíamos tropezado con ese filtro, así que para la prueba indicamos `--block-on-severity nitpick` de forma explícita y conservamos el error introducido en el informe.

A partir de ahí, un código de salida 0 dejó de ser suficiente para mí. Primero compruebo **si el escáner se ha ejecutado de verdad**. Después miro qué queda de los hallazgos originales tras los filtros de gravedad y líneas modificadas. Una herramienta que ni siquiera está instalada no debería confundirse con una revisión satisfactoria.

## El hook real de pre-push no inicia una IA en cada envío

En la segunda prueba utilicé un push de Git de verdad, pero dirigido a un repositorio bare local. No tocó un proyecto de producción ni un remoto hospedado.

Instalé el hook pre-push real de OpenQodex y confirmé un cambio sin revisar. Con los ajustes predeterminados, el hook mostró una advertencia por la falta de revisión, pero `git push` terminó correctamente y avanzó la rama del bare remoto.

A continuación añadí:

```yaml
version: 1
review:
  block_on_severity: major
```

Al crear otro commit sin revisar e intentar enviarlo, el push fue rechazado. La prueba comprobó, además, que el `main` del repositorio bare seguía apuntando al commit anterior. Ambos resultados proceden del hook instalado por OpenQodex, no de un script falso programado para devolver siempre 1.

| Caso sintético | Resultado observado |
| --- | --- |
| Configuración por defecto, sin Receipt | Advertencia, pero push permitido |
| `block_on_severity: major`, todavía sin revisión | Push rechazado; HEAD remoto sin cambios |

La distinción importa: aquí solo se verificó el tratamiento de **cambios sin recibo de revisión**. No se inició Codex como Reviewer independiente y no se creó ningún Receipt válido de una revisión de IA completada.

El [código fuente fijado del hook](https://github.com/openqodex/openqodex/blob/2f2fa9e48c1f62a6123997eb14111140d8e22a74/packages/cli/src/commands/hook.ts) permite ver la separación. Durante el push identifica el rango de Git que se va a enviar, busca en el OpenQodex HOME del usuario un recibo correspondiente a ese cambio y toma una decisión. No vuelve a ejecutar todos los escáneres ni abre un nuevo proceso de IA automáticamente al escribir `git push`.

Por tanto, «se ejecutó `scan`», «terminó `review`» y «`hook` dejó pasar el envío» son afirmaciones distintas que exigen comprobaciones independientes. Ninguna demuestra las otras.

## ¿Por qué puede dejar pasar un push cuando falla el Reviewer?

En la tercera parte apareció el caso más incómodo. En un intento desde Mac, el proceso de Codex Reviewer independiente llegó a iniciar una solicitud, pero recibió HTTP 401. OpenQodex anotó la revisión como `incomplete` y no se obtuvo un recibo válido de revisión finalizada. **Esa prueba no permite evaluar la capacidad del modelo para encontrar errores, ni el ciclo completo de un Receipt válido o caducado.**

El resumen del ensayo en Mac registraba otro detalle llamativo: primero se rechazó un push sin revisión; después de un resultado `incomplete`, otro push devolvió código 0 y creó una rama en el remoto. Pero los dos envíos no apuntaban exactamente a la misma referencia Git, y los informes originales de Mac no se habían incorporado como evidencia comprobable de forma independiente. No es correcto presentar esa secuencia como una demostración causal completa de que el 401 provocó el push exitoso.

Lo que sí se puede comprobar de manera directa es el **código fuente de OpenQodex 0.11.0**.

En el archivo fijado [`push-gate.ts`](https://github.com/openqodex/openqodex/blob/2f2fa9e48c1f62a6123997eb14111140d8e22a74/packages/core/src/push-gate.ts), `checkPush()` devuelve `abstain` cuando encuentra un Receipt que coincide con el cambio actual y cuyo `kind` es `incomplete`. Es decir, decide no bloquear el envío. Configurar `review.block_on_severity: major` no convierte esa rama de ejecución en un rechazo.

Hay otra regla en el [hook generado por `hook.ts`](https://github.com/openqodex/openqodex/blob/2f2fa9e48c1f62a6123997eb14111140d8e22a74/packages/cli/src/commands/hook.ts): el script externo solo devuelve 1, y por tanto impide el push, si su subcomando devuelve 1. Otros fallos de la herramienta, incluida la imposibilidad de iniciarla, se traducen en código 0. Así se evita dejar al desarrollador sin posibilidad de usar Git cuando falla el sistema de revisión. La contrapartida es clara: **establecer un umbral de gravedad no equivale a exigir que la revisión termine correctamente.**

Se trata de una diferencia en la política ante fallos, no de una vulnerabilidad arbitraria en producción demostrada por el experimento. El autor de una herramienta puede dar prioridad a no interrumpir el trabajo; un equipo puede preferir bloquear cualquier cambio que no tenga revisión completada. El problema aparece cuando alguien supone que la primera política garantiza la segunda.

## Qué haría si la revisión tuviera que ser obligatoria

Por ahora usaría OpenQodex como herramienta de feedback para desarrolladores, no como única puerta ineludible para fusionar cambios. Su escáner da reglas y ubicaciones precisas; el hook avisa cuando falta revisión. Pero dejaría la decisión de fusionar en manos de una comprobación CI independiente, observable y sometida a pruebas, que distinga como mínimo:

- Si los escáneres necesarios se ejecutaron, o quedaron en `not_installed`, omitidos o con error.
- Si el Reviewer de IA independiente terminó, en vez de quedarse en `unavailable` o `incomplete`.
- Si el Receipt corresponde al commit / diff actual y a los umbrales vigentes.
- Si la protección de la rama exige el éxito de la comprobación obligatoria incluso cuando falla CI o el servicio de revisión.

**Es una recomendación de despliegue, no una integración de protección de ramas de GitHub que yo haya probado en este experimento.** Si el equipo necesita una revisión independiente finalizada para cada merge, deberá comprobar que su propia puerta CI se comporta realmente como fail-closed. La configuración local de pre-push no basta para demostrarlo.

En un repositorio personal tampoco tengo claro que merezca la pena llamar a otro modelo antes de cada push. Ejecutar `review` cuando aporte valor, utilizar los escáneres como feedback rápido y reservar la CI comprobada para ramas importantes puede ser un equilibrio mejor entre coste y ritmo de trabajo. Eso sí: no hay que confundir «el hook está instalado» con «una IA independiente ya ha revisado este cambio».

La evidencia de esta serie sigue siendo bastante concreta: OpenQodex 0.11.0 encontró `SC2086` en una modificación Shell sintética. Un remoto Git desechable mostró la advertencia por defecto y el rechazo configurable de un push sin revisar. El código fijado incluye caminos explícitos por los que un Receipt `incomplete` o ciertos fallos del hook no bloquean. **El Reviewer independiente no llegó a completar una revisión con éxito**, así que no tenemos datos fiables de precisión, falsos positivos, latencia ni del ciclo completo de un Receipt válido.

Precisamente porque esa parte quedó pendiente, no utilizaría solo el hook local de OpenQodex como puerta obligatoria de revisión con IA en un repositorio de producción. Como punto de entrada para escanear y revisar sigue teniendo utilidad. Antes de darle poder para decidir si se permite un push, prefiero averiguar qué ocurre cuando el revisor no consigue terminar.
