2026-09-22 — Surtido → un solo engine (remoto): evaluación de fusión
Veredicto: fusionar SOLO la capa de captura (retrieval + almacén + dedup +
búsqueda) en el engine del Linode. La capa editorial (terna, boletín, web, radio)
y lo privado (likes/X, corpus paywall) se quedan donde están. Surtido deja de
scrapear y pasa a consumir: feed.generar() se vuelve adaptador del export del
engine. Con la fusión, el que scrapea es el remoto; el Mac deja de scrapear.
Evidencia medida hoy (batería del engine)
| dato | valor | fuente |
|---|---|---|
| solape surtido ↔ engine | 100% (0 items solo-surtido en la ventana) | 2026-09-22-bateria-engine-resultados.md (unfetch) |
| profundidad (t3) | titular 50% → texto 90% (grafo); perfil 0% → 70% | ídem |
| fuentes del engine | 122 (65 RSS + 51 X + 6 TG) ya derivadas de feed.py + channels-todo.json | nef-silo/recursos.json |
| captura | cada 30 min en el Linode, con backoff/topes/salud (mejora 7) | crontab Linode + silo.log |
| contenido en el engine | aún NO (F1 pendiente: guardar content:encoded/summary) | batería, decisiones F1/F2.5 |
→ El engine ya cubre el scraping de surtido. Mantener los dos es doble costo, dedup inconsistente y dos puntos de falla.
Gaps a cerrar antes de fusionar
- 3 fuentes de prensa sin cubrir: Página 12, Periodistas Unidos, Revista
Espejo. (Chiapas Paralelo, Río Doce, Contralínea y Rompeviento sí están
en
recursos.json.) - Temas: el engine usa 4 (Tech/IA 80 · Mundo 22 · Mexico 15 · Seguridad 5);
el home de surtido usa 7 (+Economia, Ciencia, Cultura). Opción A: añadir los
3 temas al engine; opción B:
home_feed.pyre-clasifica (ya lo hace por palabras del titular). - Campos del export:
exportar()hoy manda solotitulo/url/tema/medio/publicado. Faltanlang,region,thumbnaily el flagenque usan la terna (candidatas_desde_feed) y el feed tech. - Contenido (F1): sin texto, la terna del boletín sigue a ciegas; la batería mide el lift (50%→90%).
Destino por componente
| Componente de surtido | Destino |
|---|---|
feed.py (RSS/X/YT/TG/HN) | FUSIONAR → engine; queda como adaptador delgado |
scripts/prensa_noche.py | FUSIONAR (añadir sus 3 fuentes); muere el cron local y su fragilidad de DNS |
papers_cn.py, pulso.py, ingest.py, nlp.py, sentimiento.py, traducciones.py | EVALUAR por caso: si alimentan la capa editorial, consumen del engine |
hn.py (home) + scripts/home_feed.py | ADAPTAR: leer el export del engine |
asgi.py (Vercel: proponer/cron/subscribe/leer) | QUEDA (producto) |
proponer.py, mailing.py, cartel.py, imagen.py, miniaturas.py | QUEDA (producto) |
radio (radio_push.sh + /opt/radio) | YA VIVE en el remoto (precedente de fusión) |
likes_*, fomo_release.py, pool-likes.json | LOCAL-ONLY (privado; no al engine) |
| corpus news-silo (NYT/WaPo/Atlantic) | LOCAL-ONLY (tiers T2/T3, decisión F2.5) |
Interfaz engine → surtido
Opción 1 (recomendada): export JSON estático servido por nginx del Linode
(p. ej. /silo/export-surtido.json), regenerado en cada capturar/todo.
Público (solo metadatos), sin secretos nuevos, Vercel lo lee server-side.
Añadir TLS (certbot) si molesta el HTTP.
Opción 2: artefacto Supabase (feed_engine) — surtido ya usa ese bus
(propuesta_edicion, envio_ultimo); el engine necesitaría una key de escritura.
En ambos casos el adapter conserva la firma feed.generar(limite=40) para no
tocar los 2 call sites de asgi.py (líneas 819 y 962).
Shape propuesto (compatible con proponer.candidatas_desde_feed):
{"generado": "ISO", "items": [{"url": "", "titulo": "", "medio": "", "tema": "",
"publicado": "", "capturado": "", "lang": "", "region": "", "en": true,
"thumbnail": null}]}Fases (cada una termina en artefacto verificable)
- Acordar con la sesión nef-silo: F1 (contenido) + export enriquecido + fuentes/temas faltantes. → esta evaluación + lo que decida esa sesión.
- Añadir las 3 fuentes de prensa a
recursos.json→capturaren el Linode →n_items > 0por fuente. - Export servido: engine escribe
export-surtido.json+ nginx →curldesde la Mac devuelve N items. feed.py --desde-engine: arma el doc desde el export; comparación 1:1 contrafeed.generar()(mismo total/categorías, diff de títulos)./api/proponery/api/cronconFEED_SOURCE=engine(flag) → una edición completa de prueba.- Retirar el cron local de
prensa_noche.pyy congelar el scraping defeed.py(no borrar).
Riesgos / lista de NO
- NO mover likes/X ni el corpus paywall al server (privacidad).
- NO entregar el export por rsync local: reintroduce el punto de falla que tumbó el 17-18 sep (DNS del Mac a las 03:00).
- NO duplicar captura (dos scrapers = doble costo, dedup inconsistente).
- NO tocar
asgi.pyhasta que el adapter dé el mismo shape (2 call sites). - Un solo engine = punto único de falla → mitigación ya existente: salud/alertas
del engine (mejora 7) + failsafe de >2 días en
_resolver_ediciondeasgi.py. - Rate limits RSSHub: radio + silo + surtido comparten cuota; el engine ya corre con topes por fuente.
Coordinación
La sesión nef-silo es dueña del engine (batería 2026-09-22-bateria-engine-resultados.md,
F1/F2.5). Esta evaluación es el lado surtido: fuentes faltantes, export
enriquecido y el adapter. Punto de contacto único: el shape del export.