Files
chemavx-seo-tools/seo_exceptions.py
T
ChemaVXandClaude Opus 4.8 b5ce7638bd waivers de internal_links: tehran-1976 y chile-cefaa
- tehran-1976-iranian-f4-ufo-incident: 0 enlaces, prosa autocontenida — solo
  nombra a Klass, Jafari y el informe de la DIA; ningún caso con artículo
  propio. Los candidatos temáticos (Malmstrom, Levelland, Manises) no
  aparecen en el texto, así que enlazarlos sería forzado. Revisado 2026-07-18.
- chiles-military-...-cefaas: waiver que ya estaba en el working tree sin
  commitear (1 enlace → pursue-release-3; caso chileno autocontenido).

Efecto en el auditor: internal_links accionables 9 → 7 (el otro punto lo
cierra el post Wilson-Davis, que pasa a 2 enlaces reales).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 21:29:28 +00:00

69 lines
3.4 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.",
"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