Cómo revisar código generado por IA: las pruebas verdes no bastan
Vibe Coding acelera la producción de código, pero no reduce automáticamente el trabajo de revisión. Un LLM puede entregar un diff ordenado con rapidez. Una explicación convincente o una suite verde no demuestra por sí sola que la implementación sea segura para producción.
El caso es un validador previo a la compilación para los SVG de terminal animados de este programa del blog. Comprueba reglas XML/CSS limitadas, rutas y SHA-256 del manifest, y que los artículos de ocho idiomas referencien imágenes visibles. Un SVG canonical fijo sirve como caso positivo. Los cuatro defectos se encontraron en pruebas aisladas antes de la fusión; la evidencia se limita al validador offline.
La suite inicial pasó 8/8, pero una revisión independiente halló dos aceptaciones inseguras. Tras el primer arreglo pasó 10/10, y después se encontraron dos defectos más. La versión final fijada pasó 12/12 pruebas principales; cuatro sondas de ataque adicionales fueron rechazadas por separado. Tres archivos JavaScript también pasaron node —check.
El método tiene cinco pasos: fijar alcance e invariantes; construir contraejemplos mínimos; registrar RED solo si existe una ejecución fallida real; verificar de forma independiente los casos positivos y negativos sobre el commit exacto; decidir según el riesgo. Las pruebas lógicas no sustituyen la compilación ni las comprobaciones de plataforma y ejecución. Para cambios de riesgo, una ficha breve puede registrar alcance, commits Base/Head, invariantes, caso positivo, contraejemplo, comando y entorno, límites sin verificar y conclusión. El validador es un componente de build del blog, no un CLI genérico empaquetado por separado. No se ha medido ahorro de tiempo de revisión.
El repositorio público contiene únicamente contraejemplos sintéticos saneados y pruebas educativas ejecutables; no incluye el validador de producción de este programa de blog, sus arreglos ni su arquitectura interna. La secuencia 8/8 → 10/10 → 12/12 procede de la revisión offline original; las pruebas públicas reproducen los cuatro patrones de fallo, no la implementación de producción.
Ejemplos sintéticos saneados y pruebas reproducibles
¿Por qué 8/8 pruebas aceptaron entradas incorrectas?
Una suite verde solo muestra que las entradas actuales cumplen las aserciones actuales. No prueba que todas las entradas inválidas se rechacen.
El primer contraejemplo colocó una imagen permitida y una referencia no autorizada a otro artículo en la misma línea. La lógica antigua encontraba una imagen permitida y aceptaba la línea completa. La revisión creó un SVG real bajo otro slug y añadió la referencia Markdown junto a la imagen válida en los ocho artículos localizados. El archivo existía para descartar un fallo falso por destino ausente. La implementación antigua seguía devolviendo éxito con posts_checked=8.
<img src="/assets/terminal-demo.svg" alt="Terminal demo" width="356" height="137" /> 
El segundo contraejemplo fue una carrera del sistema de archivos. El test sustituyó un directorio padre al abrir el archivo; fuera del checkout había un SVG canonical con SHA-256 correcto. Tras convertir el directorio padre en un symlink hacia fuera, la implementación antigua leyó y aceptó el archivo externo. Se eludió la procedencia, no el hash.
La ruta Linux final abre la raíz confiable del checkout y resuelve componentes respecto a descriptores de directorio ya abiertos. Los componentes intermedios deben ser directorios y usan O_NOFOLLOW; el archivo final también rechaza symlinks. Se comprueban tipo, enlaces y tamaño, se comparan device, inode y tamaño antes/después de leer, y luego se verifica el digest. Usa Linux /proc/self/fd y falla de forma cerrada sin procfs. El alcance presupone un checkout confiable y procfs disponible, no atacantes arbitrarios con el mismo UID o privilegios de host. Consulta Linux openat2(2).
Segunda ronda: el hash es correcto, pero el contenido no se ve
Tras el primer arreglo, 10/10 pruebas pasaron. El tercer contraejemplo se refiere a reduced motion: los fotogramas SVG empiezan transparentes y la animación los revela. Si la regla solo detiene la animación sin mostrar el último fotograma, el resultado queda en blanco.
.f { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
#f0 { animation: none; opacity: 1; }
#f3 { animation: none; } /* el último fotograma sigue transparente */
}
El test recalculó SHA-256, tamaño, nombre basado en contenido y manifest canonical para mantener válidas las comprobaciones periféricas. Verificó el contrato “el resultado final debe ser visible”. Consulta MDN sobre prefers-reduced-motion.
El cuarto contraejemplo vino de Markdown: la regex antigua contaba imágenes dentro de comentarios HTML. Al comentar la única imagen, la etiqueta seguía en el código fuente pero no aparecía en la página. Según CommonMark 0.31.2, una etiqueta comentada no es una imagen visible.
<!-- <img src="/assets/demo.svg" alt="Terminal demo" /> -->
Vincular resultados a versiones exactas
| Versión | Suite principal | Revisión independiente posterior |
|---|---|---|
| Versión inicial | 8/8 superadas | Aceptaciones inseguras SEC-01 y SEC-02 |
| Versión tras el primer arreglo | 10/10 superadas | Aceptaciones inseguras SEC-03 y SEC-04 |
| Versión final revisada offline | 12/12 pruebas principales superadas | Cuatro sondas adicionales rechazadas |
La suite final incluye regresiones SEC-01 a SEC-04. Las cuatro sondas adicionales no cuentan para 12/12 y no deben sumarse como 16/16. Tres archivos JavaScript pasaron node —check, una comprobación de sintaxis, no de comportamiento.
Las dos últimas pruebas de regresión se confirmaron antes de corregir el código, pero la suite completa no se ejecutó en el commit que solo añadía pruebas. Por eso no existe un registro de 10/12 RED completo. Sí se reprodujeron aceptaciones incorrectas en el validador antiguo y la versión final pasó las pruebas principales y las sondas separadas.
Tras estabilizar la evidencia de comportamiento, reviso la complejidad: qué invariante protege cada helper, rama o configuración, si duplica lógica y si tiene consumidores reales. Menos código no significa automáticamente más simple; eliminar una protección de ruta puede reintroducir una carrera.
Panel interactivo de evidencias
Selecciona un contraejemplo sintético para revisar entrada, invariante, fallo reproducido y límites de la evidencia.
Caso seleccionado: SEC-01 · Presencia no implica exclusividad
SEC-01 · Presencia no implica exclusividad
- Condición de entrada
- Una imagen permitida y una referencia no autorizada a otro artículo aparecen juntas.
- Invariante que preservar
- Cada referencia relevante debe cumplir la regla; una permitida no vuelve segura toda la entrada.
- Fallo reproducido por el ejemplo sintético
- La lógica antigua aceptaba la línea completa al encontrar una imagen permitida y omitía la referencia extra.
- Comportamiento correcto esperado
- Comprobar todas las referencias y rechazar si alguna cruza el límite.
- Ronda de revisión original
- Hallado en la primera revisión independiente
- Lección de revisión transferible
- Crear un contraejemplo mínimo con contenido válido e inválido y revisar toda la entrada.
SEC-02 · Carrera entre comprobación y uso
- Condición de entrada
- Tras validar una ruta, se cambia un directorio padre o enlace simbólico antes de abrir.
- Invariante que preservar
- El objeto validado debe seguir siendo el utilizado dentro del límite previsto del sistema de archivos.
- Fallo reproducido por el ejemplo sintético
- Comprobar una ruta y reabrirla después permite cambiar el destino entre ambos pasos.
- Comportamiento correcto esperado
- Validación y acceso deben conservar el límite; el ejemplo sintético no afirma implementar una corrección de producción.
- Ronda de revisión original
- Hallado en la primera revisión independiente
- Lección de revisión transferible
- Buscar estado modificable por otro escritor entre validación y uso.
SEC-03 · El movimiento reducido debe mostrar el último fotograma
- Condición de entrada
- Los fotogramas SVG empiezan transparentes; al reducir movimiento, la animación se detiene.
- Invariante que preservar
- Detener la animación no debe ocultar contenido final importante.
- Fallo reproducido por el ejemplo sintético
- Si se detiene la animación sin mostrar el último fotograma, el resultado puede quedar en blanco.
- Comportamiento correcto esperado
- El modo reducido debe mostrar directamente el estado final completo y legible.
- Ronda de revisión original
- Hallado en la segunda revisión independiente
- Lección de revisión transferible
- Tratar la preferencia de accesibilidad como entrada independiente y comprobar visibilidad.
SEC-04 · Los comentarios no son contenido visible
- Condición de entrada
- Un comentario HTML contiene texto que parece una imagen válida.
- Invariante que preservar
- El texto fuente no equivale a elementos que realmente renderiza el navegador.
- Fallo reproducido por el ejemplo sintético
- Contar etiquetas puede contar como visible una imagen dentro de un comentario.
- Comportamiento correcto esperado
- Analizar la estructura, excluir comentarios y evaluar solo elementos renderizados.
- Ronda de revisión original
- Hallado en la segunda revisión independiente
- Lección de revisión transferible
- Colocar una etiqueta falsa en un comentario para probar validación estructural.
Evidencia educativa sintética pública
El repositorio público solo reproduce patrones sintéticos. Una prueba superada reproduce el fallo, no demuestra una corrección de producción.
Abrir el repositorio educativo sintético fijadoResumen offline de la ingeniería original
Registro original: 8/8 → 10/10 → 12/12. Cuatro sondas independientes se ejecutaron por separado y no forman parte de 12/12.
Límite de verificación
El alcance es el validador offline en Node.js v22.16.0 / Linux x64. macOS nativo ante escrituras concurrentes maliciosas y el runtime completo del sitio siguen sin verificarse.
Lecturas relacionadas: Mutation Testing, el límite fail-open de revisión pre-push de OpenQodex y los anclajes de comentarios de Diffle.
¿Qué falta entre pruebas aprobadas y autorización de producción?
La revisión final cubrió el validador offline en Node.js v22.16.0, Linux x64: 12/12 pruebas principales pasaron, cuatro sondas adicionales fueron rechazadas, los casos positivos de SVG/manifest y las ocho asociaciones de idioma pasaron, y tres archivos JavaScript pasaron node —check.
Esto respalda una conclusión limitada: las cuatro aceptaciones incorrectas conocidas se corrigieron en ese entorno y bajo esas reglas. No demuestra que todo el blog esté listo para producción. La resistencia nativa de macOS ante escritores concurrentes maliciosos sigue sin verificarse; la compilación completa del sitio y la ejecución final de la página quedaron fuera de las pruebas offline.
La aprobación debe dividirse por riesgo: pruebas unitarias para el comportamiento cubierto, controles estáticos para patrones, build e integración para la composición, y comprobaciones de plataforma/runtime para los límites de despliegue. Si una frontera crítica sigue sin verificar, un éxito local no es preparación completa para producción.
Los contratos y contraejemplos identifican el comportamiento sospechoso; las pruebas de la versión exacta y la evidencia independiente deciden dónde profundizar. No elimina el juicio humano: convierte “parece correcto” en una decisión de revisión con evidencia, alcance y posibilidad de verificación.