diff --git a/src/seo/autofill.py b/src/seo/autofill.py index 717599c..ccd397d 100644 --- a/src/seo/autofill.py +++ b/src/seo/autofill.py @@ -27,12 +27,24 @@ from src.seo import rules as R logger = structlog.get_logger(__name__) -# Canonical site host per language. The wrapped hrefs use the www host (the -# site's canonical form); rules.internal_links still counts them because its -# SITE_HOST ("theexclusionzone.com") is a substring of "www.theexclusionzone.com". +# Canonical site host per language — and the two are INVERTED, which is exactly +# the trap: EN canonicalizes on www, ES canonicalizes on the APEX. Same +# inversion that caused the 522 outage, and the same one ghst-es warns about. +# +# ES said "www." until 2026-07-21. Every internal link the generator wrote into +# a Spanish draft therefore pointed at a non-canonical host and ate a 301 — +# that is where the stray www link found in los-villares came from. Nothing +# downstream would have stopped it either: seo_watch only sees it after the +# post is published. +# +# Caveat, unchanged by this fix: rules.internal_links counts EN links because +# its SITE_HOST ("theexclusionzone.com") is a substring of the www form, but it +# counts ZERO for ES under either host, since that module is hardcoded to the +# EN host (it is vendored byte-for-byte and CI enforces the copy). Harmless +# today because internal_links.too_few is not a blocking rule at draft time. SITE_BY_LANG = { "en": "www.theexclusionzone.com", - "es": "www.zonadeexclusion.com", + "es": "zonadeexclusion.com", } # Tag allow-list — the model may ONLY pick from these; invented tags are dropped diff --git a/tests/test_seo_autofill.py b/tests/test_seo_autofill.py index da938f3..8ed2a4f 100644 --- a/tests/test_seo_autofill.py +++ b/tests/test_seo_autofill.py @@ -1,4 +1,5 @@ -from src.seo.autofill import ALLOWED_TAGS, DEFAULT_TAG, _coerce, _system_prompt +from src.seo.autofill import (ALLOWED_TAGS, DEFAULT_TAG, _coerce, _system_prompt, + insert_internal_links) BASE = { "meta_title": "t", @@ -81,3 +82,28 @@ def test_collision_note_is_markdown_safe(): def test_slugify_title(): assert _slugify_title("USS Russell 2019: The Pyramid UAP!") == "uss-russell-2019-the-pyramid-uap" + + +# --- canonical host per language ------------------------------------------- +# EN canonicalizes on www, ES on the APEX. They are INVERTED, and the ES entry +# said "www." until 2026-07-21, so every internal link written into a Spanish +# draft ate a 301. Nothing caught it: no test covered the host, and seo_watch +# only sees a link once the post is published. These two pin it. + +def test_internal_link_uses_es_apex_canonical(): + html = "

El caso de Manises sigue abierto.

" + out, pairs = insert_internal_links( + html, [{"phrase": "Manises", "slug": "manises-1979"}], + [{"slug": "manises-1979", "title": "Manises"}], "es") + assert 'href="https://zonadeexclusion.com/manises-1979/"' in out + assert "www.zonadeexclusion.com" not in out + assert len(pairs) == 1 + + +def test_internal_link_uses_en_www_canonical(): + html = "

The Roswell debris was recovered.

" + out, pairs = insert_internal_links( + html, [{"phrase": "Roswell", "slug": "roswell-1947"}], + [{"slug": "roswell-1947", "title": "Roswell"}], "en") + assert 'href="https://www.theexclusionzone.com/roswell-1947/"' in out + assert len(pairs) == 1