diff --git a/seo_estado.py b/seo_estado.py
index d41e8f9..3174296 100644
--- a/seo_estado.py
+++ b/seo_estado.py
@@ -123,7 +123,28 @@ def check_tags(site, cfg, posts):
if vacios:
out.append(f"tags SIN NINGÚN POST (archivo vacío en el sitemap): {vacios}")
- # 3. Posts sin categoría. Incluye los PROGRAMADOS a propósito: los 9 de
+ # 3. El nombre del tag es lo que el TEMA pinta como H1 del archivo. En el EN
+ # los siete se llamaban como su slug, así que /tag/military-cases/ le
+ # enseñaba «military-cases» al lector (el ES enseñaba «Casos Militares»).
+ # Un slug crudo de H1 en una página indexada es un fallo visible.
+ feos = sorted(t["slug"] for t in publicos
+ if (t.get("name") or "") == t["slug"] and "-" in t["slug"])
+ if feos:
+ out.append(f"tags cuyo NOMBRE es el slug (sale de H1 en el archivo): {feos}")
+
+ # 4. Dos archivos de tag con el MISMO
compiten entre ellos en el
+ # SERP. Salió al renombrar: `uap` e `investigation` tenían los dos
+ # «UAP Investigation — The Exclusion Zone».
+ por_meta = {}
+ for t in publicos:
+ mt = (t.get("meta_title") or "").strip()
+ if mt:
+ por_meta.setdefault(mt, []).append(t["slug"])
+ for mt, v in sorted(por_meta.items()):
+ if len(v) > 1:
+ out.append(f"mismo en {sorted(v)}: «{mt[:52]}»")
+
+ # 5. Posts sin categoría. Incluye los PROGRAMADOS a propósito: los 9 de
# agosto salían sin ninguna y se habrían publicado así.
for estado in ("published", "scheduled"):
sin = sorted(p["slug"] for p in posts if p.get("status") == estado
diff --git a/test_seo_estado.py b/test_seo_estado.py
index 298b53a..9fe22a6 100644
--- a/test_seo_estado.py
+++ b/test_seo_estado.py
@@ -140,6 +140,59 @@ def test_las_reglas_de_post_que_no_aplican_a_paginas_quedan_fuera():
assert not E._fuera_de_paginas(regla), regla
+
+# ───────── archivos de tag: lo que el lector ve y lo que ve Google ─────────
+
+def test_detecta_el_nombre_de_tag_que_es_el_slug():
+ """El H1 del archivo sale del `name`. En el EN los siete se llamaban como
+ su slug: /tag/military-cases/ enseñaba «military-cases» al lector."""
+ orig = E.sh
+ E.sh = _tags_falsos([
+ {"slug": "military-cases", "name": "military-cases", "visibility": "public"},
+ ])
+ try:
+ salida = E.check_tags("en", E.SITES["en"], [
+ {"slug": "p", "status": "published",
+ "tags": [{"slug": "military-cases", "visibility": "public"}]}])
+ finally:
+ E.sh = orig
+ assert any("NOMBRE es el slug" in l for l in salida), salida
+
+
+def test_un_acronimo_de_una_palabra_no_cuenta_como_slug_crudo():
+ """`uap` llamándose «uap» no es el mismo problema que «military-cases»:
+ sin guion no se lee como slug. Exigir el guion evita el falso positivo."""
+ orig = E.sh
+ E.sh = _tags_falsos([{"slug": "uap", "name": "uap", "visibility": "public"}])
+ try:
+ salida = E.check_tags("en", E.SITES["en"], [
+ {"slug": "p", "status": "published",
+ "tags": [{"slug": "uap", "visibility": "public"}]}])
+ finally:
+ E.sh = orig
+ assert not any("NOMBRE es el slug" in l for l in salida), salida
+
+
+def test_detecta_dos_archivos_de_tag_con_el_mismo_title():
+ """Salió al renombrar: `uap` e `investigation` compartían meta_title, o sea
+ dos páginas indexadas compitiendo con el mismo titular."""
+ orig = E.sh
+ E.sh = _tags_falsos([
+ {"slug": "uap", "name": "UAP", "visibility": "public",
+ "meta_title": "UAP Investigation — The Exclusion Zone"},
+ {"slug": "investigation", "name": "Investigation", "visibility": "public",
+ "meta_title": "UAP Investigation — The Exclusion Zone"},
+ ])
+ try:
+ salida = E.check_tags("en", E.SITES["en"], [
+ {"slug": "p", "status": "published", "tags": [
+ {"slug": "uap", "visibility": "public"},
+ {"slug": "investigation", "visibility": "public"}]}])
+ finally:
+ E.sh = orig
+ assert any("mismo " in l for l in salida), salida
+
+
if __name__ == "__main__":
fallos = 0
for nombre, fn in sorted(globals().items()):