Commit Graph
6 Commits
Author SHA1 Message Date
ChemaVX 48fbe4302f seo_watch: el corpus hace de diccionario en el check de mayúsculas
La lista blanca CASE_KEEP no escala y se rompió en el primer artículo real: el
Canarias de prueba del 2026-07-21 traía «El testigo del carguero Osaka Bay» y
«Ricardo Campo Pérez y Vicente-Juan Ballester Olmos», los dos encabezados
correctos, marcados como Title Case porque un barco y dos investigadores no
estaban en la lista. El umbral de 2 palabras protege de un nombre propio
suelto, no de uno de varias palabras — y cada caso nuevo trae nombres nuevos.

Ahora el propio blog resuelve la duda: si el corpus escribe esa palabra en
minúscula en algún otro sitio, capitalizarla a mitad de frase es estilo inglés;
si no aparece nunca en minúscula, es un nombre propio. «ciudad», «borde» y
«pánico» salen por todas partes; «Osaka» y «Ballester» jamás.

minimo=1 elegido barriendo el umbral contra 10 encabezados reales, el corpus
limpio y el sucio de la víspera — la tabla queda en el docstring. Una sola
aparición en minúscula ya prueba lo que hace falta: que la palabra PUEDE ir en
minúscula. Subir el umbral solo pierde detección (10/10 → 7/10 con mínimo 5).

CASE_PHRASES y CASE_KEEP siguen como red: «Guerra Fría» necesita protección
explícita porque «guerra» sí aparece en minúscula. Sin vocabulario, la función
se comporta como antes.
2026-07-21 08:46:04 +00:00
ChemaVX b0d5e1bd0c seo_watch: check de mayúsculas en el ES + arregla los checks de enlaces del ES
El check nuevo detecta encabezados en Title Case inglés, que en español no se
usa. Solo corre sobre el ES (en el EN es lo correcto). Umbral de 2 palabras
sospechosas por encabezado: con 1 sola, un nombre propio que falte en las
listas daría un falso positivo. Verificado en los dos sentidos — 0 avisos
sobre los 271 encabezados del ES ya limpios, 208 sobre el corpus de ayer, 0
sobre el corpus EN. Hace falta porque la fuga es intermitente: el 2026-07-21
había 219 encabezados sucios en 23 de 29 posts, pero otros del mismo mes
salieron limpios, así que no basta con limpiar una vez.

Y de paso, un agujero que apareció leyendo el código para meter el check:
internal_urls() tenía theexclusionzone.com escrito a fuego. Desde que el
vigilante se extendió al ES (ayer), los checks de enlaces rotos, no canónicos
y prematuros buscaban en el corpus español un dominio que no aparece en él —
o sea, veían 0 enlaces y no podían fallar nunca. Ahora el dominio sale de
R.SITE_HOST, como el resto.

Nada más arreglarlo encontró su primer hallazgo real: los-villares enlazaba a
www.zonadeexclusion.com, y en el ES el canónico es el apex (al revés que en el
EN). Salto 301 evitable, ya corregido en Ghost.
2026-07-21 07:41:18 +00:00
ChemaVXandClaude Opus 4.8 7b92079c41 seo_exceptions: ámbito por sitio — los dos blogs, independientes de verdad
Los waivers se indexaban SOLO por slug. Hoy no hay colisiones (los slugs ES
van en español), pero nada lo impedía: un slug repetido entre blogs habría
aplicado en silencio la excepción del sitio equivocado, y ese post habría
dejado de auditarse sin que nadie se enterara. Riesgo latente, no activo — pero
'no pasa hoy' no es lo mismo que 'no puede pasar'.

  EXCEPTIONS[site][familia][slug] = razón
  accepted_reason(rule, slug, site='en')

El default 'en' mantiene funcionando a seo_audit.py sin tocarlo (es EN-only).
seo_watch y seo_finish pasan el sitio activo explícitamente.

Añadido collisions(): lista los slugs waiveados en más de un sitio. Debe estar
siempre vacío; si algo aparece ahí, hay una excepción ambigua que revisar.

Verificado: mismo slug waiveado en EN se audita con normalidad en ES; los 16
waivers EN intactos; seo_audit, seo_watch (ambos sitios) y seo_finish (ambos
sitios) siguen dando los mismos resultados que antes del cambio.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 16:16:47 +00:00
ChemaVXandClaude Opus 4.8 f772703051 seo_watch: extiende el vigilante al blog ES (--site en|es|both)
El ES nunca se había auditado: no había wrapper hasta hoy (ghst-es). Corre
ambos por defecto y emite un informe con una sección por sitio.

Detalles del diseño:
  * canónico invertido entre sitios (EN www, ES apex) → SITES parametriza url,
    cli y host.
  * seo_rules.SITE_HOST se reasigna en runtime desde use_site(): ese fichero se
    vendoriza byte a byte en ResearchOwl y la CI lo verifica, así que no se
    puede parametrizar en origen.
  * huella de estado por sitio (dict), para que un cambio en uno no reabra la
    notificación del otro por accidente.
  * un sitio caído no tumba el informe del otro: run_site va en try/except y
    el error se reporta como hallazgo de ese sitio.

Primera pasada real: EN 0 hallazgos; ES 152 violaciones del auditor (28 sin
alt-text — todos —, 27 con enlazado flojo, 21 sin OG/Twitter). Ninguna dispara
Telegram: las violaciones de reglas son inventario, no regresión — solo avisan
los enlaces rotos/no canónicos/prematuros/envejecidos y el sitemap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 16:09:45 +00:00
ChemaVXandClaude Opus 4.8 2833144653 seo_watch: detecta enlaces envejecidos (check 4)
El corpus crece 2 posts/semana, así que un enlace apunta al mejor destino que
existía el día en que se escribió — y envejece. Caso real que motivó el check
(2026-07-18): levelland enlazaba el anchor 'Project Blue Book' al artículo de
AARO porque cuando se generó (30-jun) el de Blue Book no existía (1-jul).
No fue un fallo del generador: fue el mejor destino disponible, un día antes
de que apareciera el correcto.

Heurística deliberadamente conservadora, por la misma razón que el resto del
job: un vigilante desatendido que grita en falso se acaba ignorando.
  * solo marca si el anchor contiene TODOS los tokens distintivos de otro post
  * no marca si el anchor ya casa con el destino actual (el enlace está bien)
  * ignora posts con <2 tokens distintivos — p.ej. roswell-1947 se reduce a
    {roswell}, que casa con demasiadas cosas

Test de regresión hecho contra los datos reales pre y post arreglo: caza
levelland antes, 0 hallazgos después (incluido el falso positivo de yellow-sea,
cuyo anchor menciona 'roswell' pero apunta correctamente al post del vídeo).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 10:50:06 +00:00
ChemaVXandClaude Opus 4.8 744ed9fceb seo_watch: vigilante semanal de regresiones SEO (Tool D)
Chequeos deterministas sobre el sitio EN, READ-ONLY, con aviso a Telegram
solo cuando hay hallazgos Y algo cambia respecto a la ejecución anterior
(un informe semanal de 'todo bien' se acaba ignorando):

  1. enlaces internos rotos (destino inexistente)
  2. enlaces internos no canónicos (apex/http/sin barra → salto 301 evitable)
  3. enlaces PREMATUROS — destino programado para DESPUÉS del post que lo
     enlaza. Caso real detectado el 2026-07-18: el post Wilson-Davis (3-ago)
     enlazaba a MJ-12 (13-ago) → habría sido 404 durante 10 días justo en la
     ventana de rastreo del post recién publicado.
  4. sitemap: toda URL listada debe dar 200 directo
  5. violaciones accionables del motor de reglas (respeta seo_exceptions)

Por qué no lleva Gemini: el 2026-07-18 se midió con datos reales — de 6
sugerencias de enlazado interno, 0 cumplían el estándar 'entidad nombrada en
la prosa', y no vio las 3 entidades que había servidas en una sola frase.
Es fiable extrayendo (12/12 slugs correctos, cero alucinaciones) pero no
juzgando. Se deja fuera del job hasta tener un uso donde aporte de verdad.

Unidades systemd incluidas: timer semanal (lunes 05:30 UTC, antes de la
publicación de las 06:00). Instaladas en /etc/systemd/system/ y habilitadas.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 15:35:29 +00:00