# fea-verify — Verificación automática del sitio (issue #121)

Proceso por capas para validar el multiidioma del WP local **sin coste** (todo OSS + Gemma local).
Se ejecuta **contra el LOCAL** `https://farmer.taild3aaf6.ts.net/fea/` (prod está tras Cloudflare y bloquea headless).

## Principio
Lo determinista (barato, exhaustivo) primero; el LLM solo sobre lo que las capas baratas marcan.

## Capas / scripts
| Paso | Script | Qué hace | Coste |
|------|--------|----------|-------|
| Enumerar | `enum_urls.php` (en contenedor `wordpress-web`) | Saca de la BD todas las URLs: menús×5 idiomas, categorías con contenido, contenido no-ES. → `urls.json` | 0 |
| Capa 0+1 crawl | `crawl.cjs` (Playwright) | status HTTP, URL final (redirección cross-idioma), `<html lang>`, texto visible, enlaces salientes. Resumible. → `report.jsonl` | 0 |
| Capa 1 análisis | `analyze.py` (lingua, venv) | detección de idioma offline + flags. → `report.json` + `report.csv` | 0 |
| Capa 0 enlaces | `link_audit.py` (stdlib) | comprueba status de los enlaces internos hallados. → `broken_links.json` | 0 |
| Capa 3 (opcional) | `llm_review.py` (Gemma local) | review semántica SOLO de páginas marcadas. → `llm_review.json` | 0 |

## Requisitos (una vez)
- Node + Playwright: ya en `tools/e2e/node_modules`.
- venv con lingua: `python3 -m venv verify/.venv && verify/.venv/bin/pip install lingua-language-detector`
- Docker WP arriba (`wordpress-web`, `wordpress-mysql`), LM Studio con `google/gemma-4-e4b` (solo Capa 3).

## Ejecutar (todo)
```bash
cd tools/e2e/verify
# 1) enumerar URLs desde la BD
docker cp enum_urls.php wordpress-web:/tmp/ && docker exec wordpress-web php /tmp/enum_urls.php && docker cp wordpress-web:/tmp/verify_urls.json urls.json
# 2) crawl (resumible; borra report.jsonl para empezar de cero)
CONC=5 node crawl.cjs
# 3) análisis + flags
.venv/bin/python analyze.py
# 4) enlaces rotos
python3 link_audit.py
# 5) (opcional) review LLM local sobre lo marcado
.venv/bin/python llm_review.py
```

## Leer el resultado
- **`report.csv`** — lo accionable, ordenado por gravedad. Columnas: `sev`(3 crítico→1 aviso), `flags`, `lang`, `kind`, `status`, `detected`(idioma detectado), `conf`, `redir`, `url`, `final`.
- **`broken_links.json`** — enlaces internos rotos + qué páginas los referencian.
- **`report.json`** — todo, en JSON.

### Flags
- `HTTP_5XX` / `HTTP_4XX` / `CRAWL_ERROR` — la página falla (p.ej. el 500 de `/en/category/letters-from-other-weeks/`).
- `CROSS_LANG_REDIRECT` — pediste `/en/x/` y acabaste en otro idioma (bug de slug duplicado).
- `SPANISH_LEAK` — página no-ES cuyo texto detectado es español (sin traducir / widget en ES).
- `LANG_MISMATCH` / `HTML_LANG_MISMATCH` — idioma del texto / del `<html lang>` ≠ esperado.

## Scope de la corrida
Por defecto crawlea: menús (5 idiomas), categorías con contenido, y **todo el contenido no-ES** (~3.9k). El contenido ES masivo (~24k) se omite por volumen; añadirlo es cambiar la query de `enum_urls.php`.

## Coste
**0 €.** Sin SaaS ni APIs de pago. Detección de idioma offline (lingua) y review con Gemma local. Minimax NO se usa.
