78879aef68
36 URLs en 500 desde el cutover: las mismas 9 cartas de otras semanas en fr/it/en/pt. En espanol iban bien, y contra CDMON las mismas URLs daban 200, o sea que era regresion nuestra. La reescritura de enlaces internos al idioma activo llamaba a url_to_postid() una vez por enlace, y estas cartas traen unos 40. Cuando la URL no encaja en ninguna regla de reescritura, la WP_Query que monta esa funcion se queda sin clausula que la acote y se trae las 32.311 entradas CON su contenido, dos veces por peticion. 256 MB agotados en class-wpdb.php y 500. Se sustituye por fea_href_a_post_id(): ultimo segmento del path y una consulta con LIMIT 1, que no puede degenerar, cacheada por peticion. El ORDER BY reproduce a quien sirve WordPress esa misma URL, y no es cosmetico: hay 4 slugs compartidos por una pagina de primer nivel y una entrada, donde gana la pagina, y 58 compartidos por dos entradas -duplicados del import de Joomla- donde gana la de post_date mas reciente. Mi primer intento ordenaba por ID ASC y en esos 58 habria traducido el enlace equivocado. Verificado: 116/116 cartas traducidas en 200, 200 entradas al azar en 200, E2E 13/13, y el resolutor nuevo coincide con url_to_postid() en 461 de 461 slugs de los casos donde url_to_postid() no revienta. La consulta gorda ha desaparecido del performance_schema. Van dos centinelas a la suite E2E, una carta italiana y una francesa de las que fallaban. Nota de metodo para el futuro: esto no se reproduce con wp-cli, porque Polylang no instancia su frontend en CLI y el filtro sale antes de tiempo. Se cazo mirando events_statements_history_long en MySQL mientras se pedia la pagina por HTTP. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tools/e2e — Suite E2E económica en tokens
Pipeline de revisión visual/funcional con coste mínimo en tokens de Claude. Diseño 3-tier:
| Tier | Quién | Coste tokens Claude |
|---|---|---|
| 1 — Programático | Playwright local | 0 |
| 2 — LLM local vision | Gemma 4 vision en LM Studio | 0 |
| 3 — Claude | Lee report.md (texto) |
Mínimo |
Issue de diseño: rafa/feadulta#37.
Setup
cd tools/e2e
npm install # instala playwright 1.58.2 (reaprovecha browsers ya en ~/.cache/ms-playwright/)
Uso — Tier 1 (sin LLM)
# Suite completa
node run.js feadulta
# Una sola URL por slug
node run.js feadulta home-es
# Subconjunto
node run.js feadulta --only=home-es,effa-hub,carta-semana
Genera por cada pasada:
out/<site>/<timestamp>/
report.json # datos crudos
report.md # tabla legible
<slug>.png # screenshot full-page por URL
out/<site>/latest -> <timestamp> # symlink al último run
Métricas por URL: HTTP status, console errors/warnings, page errors, requests fallidas (≥400), <img> con naturalWidth==0, <title>, <h1>, word count, tiempo de carga.
Severity:
OK— sin problemas detectadosWARN— imágenes rotas, requests 4xx, console errorsFAIL— HTTP ≥ 400 o error de navegación
Uso — Tier 2 (vision Gemma 4)
# Pregunta ad hoc sobre una captura
node ask-vision.js out/feadulta/latest/post-tolle-44247.png \
"¿Se ve la foto de Eckhart Tolle? ¿Hay placeholders rotos?"
# Auto: pasa cada captura WARN/FAIL a la vision y escribe vision.md
node ask-vision.js --auto out/feadulta/latest
Variables de entorno (opcionales):
LMSTUDIO_URL— defaulthttp://172.19.128.1:1234/v1(gateway WSL→Windows)LMSTUDIO_MODEL— defaultgoogle/gemma-4-e4bMAX_TOKENS— default 800TEMPERATURE— default 0.2
Requisito: LM Studio cargado con un modelo multimodal y escuchando en 0.0.0.0:1234.
Añadir un sitio
Crear sites/<nombre>.json:
{
"name": "ytsummaries",
"baseUrl": "https://farmer.taild3aaf6.ts.net",
"viewport": { "width": 1366, "height": 900 },
"timeoutMs": 30000,
"userAgent": "ytsummaries-e2e/0.1",
"urls": [
{ "slug": "home", "path": "/yt/" },
{ "slug": "admin", "path": "/yt/wp-admin/" }
]
}
baseUrl + path se concatena tal cual. Ejecutar con node run.js ytsummaries.
Diseño
- Sin LLM por defecto: los checks deterministas (HTTP/console/
naturalWidth) cubren los fallos reales. - Vision bajo demanda: Gemma 4 solo entra cuando hace falta opinar sobre maquetación o cuando Tier 1 marca algo raro.
- Claude solo lee texto:
report.mdcabe en ~10 KB. Las PNG no entran al contexto salvo que el usuario lo pida. - Stateless: cada pasada vive en su propia carpeta timestamp.
latestes symlink. - Reutilizable: el runner no sabe nada de feadulta; solo conoce el formato
sites/<x>.json.
Limitaciones conocidas
- Gemma 4 a veces escribe razonamiento antes de la respuesta final.
ask-vision.jsintenta extraer "Final Answer:" o la primera etiqueta[OK]/[WARN]/[FAIL]. - No hay comparación con baseline aún. El runner solo describe el estado actual. Snapshot/pHash es trivial de añadir cuando se quiera detección de regresiones contra una pasada anterior.
- Playwright 1.58.2 está pineado para reaprovechar los browsers ya cacheados (
chromium_headless_shell-1208). Subir a 1.60+ requierenpx playwright install.