Cierra las 3 incidencias restantes de internal_links.too_few. Ninguno de los tres nombra en su prosa una entidad con artículo propio (criterio de siempre: entidad nombrada en el texto, no afinidad temática): - tassili-najjer: arqueología sahariana, autocontenida. - missing-scientists: trata de la cadena mediática, no de casos UAP. - grusch-capitol-june-9 (adelanto): además lleva canonical_url a la investigación completa, así que sus señales se consolidan allí; forzar enlaces en un preview superado no aporta. El cuarto caso (roswell-341) SÍ tenía enlace honesto y se aplicó: 'the Air Force's official Roswell explanation' → roswell-1947-ufo-incident. Nota técnica: ese post usa nodos lexical nativos (no bloques HTML como Wilson-Davis), así que hubo que partir el nodo de texto e insertar un nodo 'link' con el mismo esquema que el enlace preexistente. Con esto el auditor queda a CERO violaciones accionables (venía de 20), lo que hace que cualquier aviso futuro de seo_watch signifique algo de verdad. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
80 lines
4.2 KiB
Python
80 lines
4.2 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[rule_family][slug] = "documented reason"
|
|
|
|
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 = {
|
|
# 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.",
|
|
"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.)",
|
|
},
|
|
}
|
|
|
|
|
|
def accepted_reason(rule, slug):
|
|
"""Return the documented waiver reason if (rule, slug) is an accepted
|
|
exception, else None.
|
|
|
|
Matches a rule-family key to a Violation whose `rule` is the key itself or
|
|
"<key>.<detail>" (e.g. family "internal_links" matches "internal_links.too_few").
|
|
"""
|
|
for family, slugs in EXCEPTIONS.items():
|
|
if (rule == family or rule.startswith(family + ".")) and slug in slugs:
|
|
return slugs[slug]
|
|
return None
|