Notas de ingeniería — las técnicas detrás de Windup
Un resumen de los enfoques que hacen que las pruebas en lenguaje natural sean deterministas y baratas:
- Planifica una vez, reproduce gratis. El LLM se usa exactamente una vez por escenario (más la replanificación automática cuando la app cambia). Su salida es un plan de acciones JSON validado por esquema — datos, no código: sin scripts generados, sin condicionales, sin improvisación en tiempo de ejecución. Los replays ejecutan el plan en caché con cero llamadas al modelo.
- Ejecución determinista. Los planes se ejecutan sobre Playwright con comprobaciones nativas de actionability y eventos de entrada confiables. Cada acción lleva una postcondición explícita (
expect: elemento visible / glob de URL / valor de input) verificada sin LLM — la verificación cuesta una consulta al DOM, no tokens. - Caché autorreparable. Las trayectorias se cachean indexadas por escenario + ruta de la URL de inicio (portable entre hosts de dev/staging/CI). Una verificación fallida invalida el plan, preserva la entrada obsoleta como evidencia y dispara una replanificación con el fallo como contexto.
- Firmas estructurales de página. Las páginas se identifican por un SHA-256 de sus elementos interactivos normalizados — sin texto, sin datos — para que el ruido del entorno no divida identidades, y se detecta (con lenidad) la deriva de la página inicial en el replay.
- Conocimiento del sitio por capas. Un grafo de mapa del sitio alimenta al planificador con rutas y selectores reales, construido a partir de tres fuentes con precedencia estricta — observación en tiempo de ejecución > scan estático del código > asistencia LLM con tope. El conocimiento es caché, no verdad: cualquier cosa obsoleta degrada a descubrimiento en tiempo de ejecución.
- Disciplina de presupuesto del prompt. El prompt de planificación se mantiene de tamaño ≈ constante (~32k caracteres): el árbol de la página, la porción del mapa, el catálogo de fragmentos y el manifiesto tienen cada uno topes duros de caracteres. Los prompts largos degradan de forma medible a los modelos pequeños — los presupuestos son una característica de corrección, no una optimización.
- Normalización mecánica en vez de esperanza en el prompt. La salida del modelo se sanea de forma determinista: campos vacíos descartados, ids renumerados,
wait_for⇄expectnormalizados, acciones de eco de fragmentos deduplicadas, credenciales depuradas de los escenarios autorados. Las pruebas A/B entre proveedores mostraron que las instrucciones del prompt por sí solas no se sostienen entre modelos — el código tiene la última palabra. - Reintento de dos niveles. Los fallos semánticos (plan inválido) reciben un reintento corto que lleva los errores de validación; las patologías transitorias de la API (degeneración por bucle de tokens, red) reciben nuevas llamadas con semillas variadas. Se evitan los reintentos de prompt completo — vuelven a disparar la degeneración de forma fiable.
- Bloques de construcción componibles. Los fragmentos son sub-trayectorias curadas y versionadas (p. ej. login) que los planes referencian por id — actualizados una vez, propagados a todas partes, expandidos en tiempo de ejecución. El manifiesto del proyecto inyecta conocimiento del equipo (convenciones, cuentas, vocabulario) en cada plan.
- Secretos por referencia. Los valores de credenciales viven en
.env.local/secretos de CI; los archivos versionados llevan solo mapeos cuenta → nombre ENV. Los planes usanvalue_ref, resuelto en tiempo de ejecución — los secretos nunca llegan al LLM, la caché ni git. - Frontera de LLM agnóstica al proveedor. Una interfaz, implementaciones de Google y OpenAI (el cliente de OpenAI es REST puro — sin el peso de un SDK), seleccionable por ejecución. Cambiar el motor del navegador y añadir un proveedor fueron cada uno un cambio de un solo archivo — las fronteras son la arquitectura.
- Coste que puedes auditar. Cada punto de contacto con el LLM tiene un tope explícito y queda en un libro mayor por ejecución con tokens, modelo y proveedor;
windup costsrecalcula desde una tabla de precios con fecha, de modo que el historial sigue siendo preciso a medida que los precios cambian.