Barrido del 2026-07-21: se buscaron todas las entidades del catálogo ES en la prosa de los 26 posts con enlazado flojo y se pusieron los 24 enlaces honestos que había. Los 19 que siguen bajo el umbral es porque no hay más entidades del corpus nombradas en su texto — el catálogo español se cruza poco y forzar un enlace temático es justo lo que estas excepciones existen para evitar. Cada waiver dice qué enlaza y qué nombra la prosa que NO tiene artículo, para que se pueda revisar cuando el catálogo crezca. Un falso positivo descartado a mano: el «Roosevelt» de la batalla de Los Ángeles es el presidente, no el portaaviones del artículo de Gimbal/GoFast. Fuera el waiver de roswell-1947-preguntas-sin-respuesta: ya tiene 2 enlaces (el de mayo y PURSUE), así que decía algo falso. El ES pasa de 143 violaciones accionables a 12 (11 meta_description y 1 meta_title largos, que ya estaban ahí pero tapados por el corte del top-6).
210 lines
13 KiB
Python
210 lines
13 KiB
Python
"""
|
|
Accepted SEO exceptions — consciously-waived findings for SPECIFIC posts.
|
|
|
|
This is an A-side (auditor) REPORTING filter, NOT a rule change. The shared rule
|
|
engine (seo_rules.py) still CHECKS every rule on every post; the auditor simply
|
|
moves a matching violation into a separate "Accepted exceptions (won't fix —
|
|
documented)" section so the headline "issues to fix" count reflects only
|
|
genuinely-actionable items. Each waiver carries its REASON (no silent skips).
|
|
|
|
Structure: EXCEPTIONS[site][rule_family][slug] = "documented reason"
|
|
|
|
El ámbito por SITIO ("en"/"es") es deliberado: los waivers se indexaban solo por
|
|
slug, así que 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 enterase. Hoy no hay colisiones (los slugs ES van en español), pero
|
|
nada lo impedía. Con el ámbito, los dos sitios son independientes de verdad.
|
|
|
|
Scope:
|
|
- A waiver applies ONLY to the named rule family for the named slug. Any OTHER
|
|
rule the same post violates still surfaces as a real issue.
|
|
- A waiver is matched against a Violation whose `rule` equals the family key or
|
|
starts with "<family>." — so "internal_links" covers "internal_links.too_few".
|
|
- If a post later stops violating the rule (e.g. a link is added and it clears
|
|
the threshold), there's simply no violation to waive — the waiver is moot.
|
|
|
|
Tool C (seo_validate.py, pre-publish validator) deliberately does NOT consult
|
|
this registry: exceptions are per-EXISTING-post decisions; a brand-new draft is
|
|
held to the full standard.
|
|
"""
|
|
|
|
EXCEPTIONS = {
|
|
"en": {
|
|
# internal_links < 2 — these posts are intentionally left as-is because there
|
|
# is no honest 2nd internal-link target in their prose; forcing one would hurt
|
|
# quality. (Decided 2026-06-23.)
|
|
"internal_links": {
|
|
"roswell-1947-ufo-incident":
|
|
"1 link (Spielberg); no 2nd entity in prose maps to an article — "
|
|
"forcing a link would hurt quality.",
|
|
"manises-1979-spain-ufo-incident":
|
|
"1 link (Canary Islands); no 2nd entity in prose maps to an article.",
|
|
"betty-barney-hill-1961-abduction":
|
|
"1 link (alien abduction → travis-walton, thematic); no 2nd honest target.",
|
|
"malmstrom-1967-ufo-nuclear-missiles-incident":
|
|
"1 link (Grusch); the only entity named in the prose.",
|
|
"rendlesham-forest-incident-1980":
|
|
"0 links; genuinely isolated — no entity in prose maps to one of our articles.",
|
|
"phoenix-lights-1997":
|
|
"0 links; only thematic candidates are stretches — left as-is.",
|
|
"canary-islands-1976-ufo-sighting":
|
|
"1 link (reciprocal → manises); no honest 2nd entity in prose — "
|
|
"Spanish case, self-contained.",
|
|
"chiles-military-declassified-uap-files-cefaas-groundbreaking-investigation-of-unexplained-aerial-phenomena":
|
|
"1 link (→ pursue-release-3, the 294 files); no 2nd verbatim entity "
|
|
"in prose — self-contained Chilean case.",
|
|
"belgium-1989-1990-nato-fighters-ufo-wave":
|
|
"1 link (→ gimbal-gofast, por la referencia a los vídeos de la Navy); "
|
|
"la prosa no nombra ningún otro caso del corpus — la oleada belga se "
|
|
"narra con sus propias fuentes (SOBEPS, De Brouwer, el memo de la DIA). "
|
|
"(Revisado 2026-07-20, en el remate previo a publicar.)",
|
|
"levelland-1957-texas-ufo-vehicle-interference":
|
|
"1 link (→ project-blue-book, la única entidad del corpus nombrada en "
|
|
"la prosa; lo demás es Hynek y 'ball lightning', sin artículo propio). "
|
|
"El enlace previo a AARO era un destino de conveniencia: Levelland se "
|
|
"creó el 30-jun y el artículo de Blue Book no existía hasta el 1-jul, "
|
|
"así que el autofill enlazó a lo más cercano disponible. Repuntado al "
|
|
"destino correcto el 2026-07-18.",
|
|
"hundreds-watched-in-broad-daylight-the-westall-ufo-mystery-that-australia-still-cant-explain":
|
|
"0 links; la prosa no nombra ningún otro caso del corpus — episodio "
|
|
"escolar australiano, autocontenido. (Revisado 2026-07-18, antes de "
|
|
"su publicación del 6-ago.)",
|
|
"colares-1977-brazil-air-force-ufo-investigation":
|
|
"1 link (→ varginha, el otro caso brasileño); lo único más que nombra "
|
|
"es 'Operation Saucer', que es su propia operación y no tiene artículo. "
|
|
"(Revisado 2026-07-18, antes de su publicación del 24-ago.)",
|
|
"tassili-najjer-cave-paintings-ancient-astronaut-theory":
|
|
"1 link (→ gimbal-gofast); ningún otro caso del corpus aparece en la "
|
|
"prosa — arqueología sahariana, autocontenida. (Revisado 2026-07-18.)",
|
|
"missing-scientists-theory-youtube-white-house-2026":
|
|
"1 link (→ grusch-capitol); trata de la cadena mediática, no de casos "
|
|
"UAP — no hay 2ª entidad del corpus en la prosa. (Revisado 2026-07-18.)",
|
|
"grusch-capitol-june-9-2026-uap-disclosure":
|
|
"1 link (→ la investigación completa, que es lo correcto). Es el ADELANTO "
|
|
"del evento, con canonical_url apuntando a esa investigación: sus señales "
|
|
"se consolidan allí, así que forzar enlaces aquí no aporta. "
|
|
"(Revisado 2026-07-18, ver project-en-seo-recovery.)",
|
|
"tehran-1976-iranian-f4-ufo-incident":
|
|
"0 links; prose names no case we have an article for (only Klass, "
|
|
"Jafari, the DIA report) — self-contained. Thematic candidates "
|
|
"(Malmstrom, Levelland, Manises) appear nowhere in the text. "
|
|
"(Reviewed 2026-07-18.)",
|
|
},
|
|
},
|
|
|
|
# El blog ES se auditó por primera vez el 2026-07-20 (al crear ghst-es) y
|
|
# arrastra backlog: 28 posts sin alt-text, 27 con enlazado flojo. Es deuda
|
|
# acumulada por no haberse revisado nunca, NO regresión — se irá waiveando o
|
|
# arreglando post a post, con la misma vara de medir que el EN.
|
|
"es": {
|
|
# Barrido del backlog el 2026-07-21: se buscaron TODAS las entidades del
|
|
# catálogo ES en la prosa de los 26 posts con enlazado flojo y se añadieron
|
|
# los 24 enlaces honestos que había. Los que siguen no llegan a 2 porque no
|
|
# hay más entidades del corpus nombradas en su texto — el catálogo español
|
|
# se cruza poco, y forzar un enlace temático sería justo lo que no queremos.
|
|
# (Un falso positivo descartado a mano: el «Roosevelt» de la batalla de Los
|
|
# Ángeles es el presidente, no el portaaviones del artículo de Gimbal.)
|
|
"internal_links": {
|
|
"archivos-pursue-uap-bases-nucleares-desclasificado":
|
|
"1 enlace (→ AARO). Lo demás que nombra son organismos sin artículo "
|
|
"propio: ODNI, Departamento de Energía, NASA, FBI, Hegseth.",
|
|
"batalla-los-angeles-1942-ovni-antiaereo":
|
|
"1 enlace (→ Kenneth Arnold, «cinco años antes de que Arnold acuñara "
|
|
"el término platillo volante»). El resto son Knox, Stimson y Marshall, "
|
|
"sin artículo propio.",
|
|
"betty-y-barney-hill-1961-el-primer-caso-de-abduccion-extraterrestre-documentado":
|
|
"0 enlaces; la prosa no nombra ningún otro caso del catálogo ES.",
|
|
"cefaa-chile-unidad-oficial-ovni":
|
|
"1 enlace (→ la segunda desclasificación). Caso chileno autocontenido.",
|
|
"chiles-whitted-1948-piloto-eastern-airlines-ovni":
|
|
"0 enlaces; nombra a Chiles, Whitted y el Proyecto Sign, ninguno con "
|
|
"artículo propio en el blog ES.",
|
|
"eeuu-desclasifica-162-archivos-secretos-sobre-ovnis-y-vida-extraterrestre-el-pentagono-abre-sus-bovedas":
|
|
"1 enlace (→ AARO). El resto del texto son agencias y el caso "
|
|
"Bariloche, sin artículos propios.",
|
|
"el-incidente-nimitz-2004-cuando-pilotos-de-caza-f-18-se-toparon-con-lo-inexplicable":
|
|
"1 enlace (→ Grusch, la única entidad del corpus en la prosa).",
|
|
"el-incidente-ovni-de-rendlesham-forest-1980-el-caso-mas-documentado-de-la-historia-militar-britanica":
|
|
"1 enlace (→ Roswell, sobre «el Roswell británico»). Halt, Penniston y "
|
|
"el faro de Orford Ness no tienen artículo propio.",
|
|
"gary-mckinnon-el-hacker-que-vio-algo-en-los-servidores-de-la-nasa":
|
|
"1 enlace (→ PURSUE). Trata de un caso de hacking y extradición, no de "
|
|
"avistamientos: no hay 2ª entidad del catálogo.",
|
|
"gimbal-gofast-uap-uss-theodore-roosevelt-2015":
|
|
"0 enlaces; los vídeos de la Navy se narran con sus propias fuentes.",
|
|
"la-explosion-de-luz-de-canarias-1976-testigos-militares-espanoles-desvelan-un-secreto-de-la-guerra-fria":
|
|
"0 enlaces; caso canario autocontenido (Padrón, los Poseidon C-3).",
|
|
"la-noche-que-el-cielo-se-nego-a-revelar-sus-secretos-el-caso-manises-de-1979":
|
|
"0 enlaces; la prosa no nombra otro caso del corpus.",
|
|
"las-revelaciones-del-pentagono-sobre-uap-un-hito-historico-en-la-busqueda-de-la-verdad-sobre-fenomenos-anomalos-no-identificados":
|
|
"1 enlace (→ Immaculate Constellation, el programa del que trata).",
|
|
"los-villares-1996-el-encuentro-extraterrestre-que-marco-la-ufologia-espanola":
|
|
"1 enlace (→ Manises, el otro caso español). Sin 2ª entidad honesta.",
|
|
"pascagoula-1973-abduccion-hickson-parker":
|
|
"0 enlaces; nombra a Hickson, Parker y Hynek, sin artículo propio.",
|
|
"phoenix-lights-1997-la-noche-cuando-arizona-vio-lo-inexplicable":
|
|
"0 enlaces; los candidatos temáticos serían forzados.",
|
|
"travis-walton-1975-abduccion-ovni-arizona":
|
|
"1 enlace (→ Betty y Barney Hill, por la película que disparó los "
|
|
"reportes de abducción). Sin 2ª entidad del corpus.",
|
|
"varginha-1996-el-caso-ovni-mas-intrigante-de-brasil":
|
|
"1 enlace (→ Roswell, por la comparación de los restos). Chereze y "
|
|
"Mudinho son de su propio caso.",
|
|
"vaticano-extraterrestres-iglesia-catolica":
|
|
"0 enlaces; trata de teología y astrobiología, no de casos UAP.",
|
|
},
|
|
# Los dos Roswell del ES comparten caso y año, así que topic.collision va a
|
|
# seguir avisando siempre. Se resolvió por la vía que propone la propia regla
|
|
# ("retitle to a distinct angle" + "interlink deliberately"), no fusionando:
|
|
# el de mayo es el relato de referencia (2.278 palabras, 3 clics / 152 impr.)
|
|
# y el de julio cubre otra cosa — el vídeo de 2025 de los Archivos Nacionales,
|
|
# el diario de Marcel y la confesión póstuma de Haut. El 2026-07-21 el de julio
|
|
# se retituló a su ángulo real (dejó de presentarse como "Roswell 1947: El
|
|
# Accidente OVNI...") y se enlazaron entre sí en ambos sentidos.
|
|
"topic": {
|
|
"roswell-1947-el-misterio-que-cambio-la-historia-de-la-ufologia":
|
|
"Complementario, no duplicado: es el relato de referencia y enlaza al "
|
|
"de julio como «las preguntas que 78 años no han resuelto». "
|
|
"(Decidido 2026-07-21 con datos de Search Console.)",
|
|
"roswell-1947-preguntas-sin-respuesta-78-anos":
|
|
"Complementario, no duplicado: retitulado a su ángulo propio (el vídeo "
|
|
"y el diario) y enlaza al de mayo sobre «Ese relato ya lo cubrimos». "
|
|
"(Decidido 2026-07-21 con datos de Search Console.)",
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def accepted_reason(rule, slug, site="en"):
|
|
"""Devuelve la razón documentada si (rule, slug) está waiveado EN ESE SITIO.
|
|
|
|
Empareja la familia de reglas con una Violation cuyo `rule` sea la clave
|
|
misma o "<clave>.<detalle>" (la familia "internal_links" cubre
|
|
"internal_links.too_few").
|
|
|
|
`site` por defecto "en" para no romper a seo_audit.py, que es EN-only.
|
|
Quien audite el ES DEBE pasar site="es" explícitamente.
|
|
"""
|
|
for family, slugs in EXCEPTIONS.get(site, {}).items():
|
|
if (rule == family or rule.startswith(family + ".")) and slug in slugs:
|
|
return slugs[slug]
|
|
return None
|
|
|
|
|
|
def collisions():
|
|
"""Slugs waiveados en más de un SITIO. Debe estar SIEMPRE vacío: si algo
|
|
aparece aquí, hay una excepción ambigua que revisar a mano.
|
|
|
|
Cuenta una vez por sitio, no por familia de reglas: un mismo post puede
|
|
tener waivers en varias familias del mismo blog sin que eso sea ambiguo
|
|
(el par Roswell del ES está waiveado en `internal_links` y en `topic`).
|
|
"""
|
|
from collections import Counter
|
|
c = Counter()
|
|
for fams in EXCEPTIONS.values():
|
|
site_slugs = set()
|
|
for slugs in fams.values():
|
|
site_slugs.update(slugs.keys())
|
|
c.update(site_slugs)
|
|
return sorted(s for s, n in c.items() if n > 1)
|