seo_watch/seo_link: dos fallos que se manifestaban callándose

1) El corpus se pedía con --limit 100. Con 40 posts en EN y 29 en ES sobraba,
   pero a 2 por semana habría cruzado los 100 hacia 2027 y se habría truncado
   EN SILENCIO. Y un corpus truncado no da error: da falsos «enlaces rotos»
   contra los posts que faltan, que es peor que no mirar. Pasa a --limit all
   y, sobre todo, se comprueba el número contra meta.pagination.total: eso es
   lo que convierte un fallo mudo en uno que se oye. En seo_link el truncado
   era aún peor, porque haría rechazar como inexistente un destino que sí
   está.

2) check_sitemap daba VERDE cuando curl fallaba: 0 URLs descargadas son 0
   problemas encontrados, y el informe decía « sin hallazgos». O sea que
   justo el rato en que el sitio está caído era cuando este check se callaba.
   Un sitemap vacío pasa a ser hallazgo con mensaje propio; en un blog con
   29-40 artículos nunca lo está legítimamente.

Probado con casos que DEBEN fallar: corpus recortado a 3 de 40 (revienta y lo
dice), corpus completo (pasa limpio), y sitemap vacío (hallazgo, informe claro
y notificaría). Y en real contra los dos blogs: EN 40 posts / 33 URLs, ES 29 /
31, ambos sin hallazgos.

No toca seo_rules.py, así que el vendor-sync con ResearchOwl sigue en verde.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ChemaVX
2026-07-21 16:01:28 +00:00
co-authored by Claude Opus 4.8
parent 48fbe4302f
commit d381c0225c
2 changed files with 44 additions and 7 deletions
+14 -3
View File
@@ -62,14 +62,25 @@ def sh(cmd, timeout=240):
def fetch_corpus(cli):
"""Corpus completo (published + scheduled). A fichero: un PIPE se trunca."""
"""Corpus completo (published + scheduled). A fichero: un PIPE se trunca.
`--limit all` + comprobación del total (ver seo_watch.fetch_posts). Aquí un
corpus truncado es peor todavía que en el vigilante: haría fallar el
«¿existe el destino?» y rechazaría un enlace perfectamente válido, o peor,
daría por inexistente un post que sí está."""
fd, path = tempfile.mkstemp(suffix=".json")
os.close(fd)
try:
sh(f"{cli} post list --limit 100 --formats lexical,mobiledoc,html --json > {path}")
sh(f"{cli} post list --limit all --formats lexical,mobiledoc,html --json > {path}")
with open(path) as f:
d = json.load(f)
return d.get("posts", d) if isinstance(d, dict) else d
posts = d.get("posts", d) if isinstance(d, dict) else d
total = ((d.get("meta") or {}).get("pagination") or {}).get("total") \
if isinstance(d, dict) else None
if total is not None and len(posts) != total:
sys.exit(f"✗ corpus truncado: {len(posts)} de {total} posts — abortado "
f"sin escribir (el chequeo de destinos sería mentira)")
return posts
finally:
os.unlink(path)