Commit Graph
97 Commits
Author SHA1 Message Date
Renovate Bot 1bb61a85d9 chore(deps): update dependency tenacity to v9.1.4 2026-07-07 12:00:36 +00:00
ChemaVXandClaude Fable 5 d31badeb5b refactor(ghost): _admin_get/_admin_headers compartidos en GhostPublisher
Build & Deploy ResearchOwl / build-and-push (push) Successful in 8s
find_draft_by_title y fetch_published_menu duplicaban línea a línea el GET
admin de Ghost (token, URL, ClientSession, dict de headers, status check,
resp.json), y el dict Authorization/Accept-Version/Accept-Encoding estaba
copiado en 3 sitios — el incidente brotli ya demostró que el fix por copia
se deja sitios. Ahora los headers se construyen en un único punto
(_admin_headers, con SAFE_ACCEPT_ENCODING de config) y el GET en _admin_get;
publish_draft usa los mismos headers. aiohttp pasa a import de módulo en
generator.py (era 'import aiohttp as _aio' repetido dentro de dos métodos).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 16:00:31 +00:00
ChemaVXandClaude Fable 5 fbe8ae1885 fix(scraper): _unwrap_news_link normaliza //url y descarta apiclick sin URL usable
Un ?url= protocol-relative (//publisher.com/...) caía por el startswith('http')
y devolvía el link apiclick.aspx crudo, que pasaba blacklist y relevancia y
acababa raspado como página de redirect/consent de Bing. Ahora se antepone
https: a los protocol-relative y, si no se puede extraer URL válida de un
apiclick, se devuelve '' para que el caller lo salte.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:58:40 +00:00
ChemaVXandClaude Fable 5 b58a3ce118 fix(ghost): find_draft_by_title compara títulos normalizados, no igualdad exacta
Si Ghost normaliza el título al guardar (trunca a 255 un H1 largo del LLM,
colapsa whitespace), la igualdad exacta no encontraba el draft recién creado
y la recuperación fallaba justo en el escenario que debe cubrir. _norm_title
aplica el mismo colapso+truncado a ambos lados de la comparación.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:56:50 +00:00
ChemaVXandClaude Fable 5 634f38e016 fix(scraper): SAFE_ACCEPT_ENCODING compartida — cubre la sesión SearXNG olvidada
La sesión aiohttp de _search_searxng (camino primario de búsqueda) construía
sus headers sin Accept-Encoding: heredaba el default de aiohttp, que volvería
a anunciar br si algún día se reinstala un backend brotli. El valor vive ahora
en src/config.py (SAFE_ACCEPT_ENCODING) en vez de copiado literal por sitio.

También corrige el comentario de HEADERS que afirmaba que brotlicffi seguía
instalado como fallback — 397546a lo quitó: un br no anunciado hoy falla sin
recuperación y la fuente se pierde.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:56:07 +00:00
ChemaVXandClaude Fable 5 3d74857d85 fix(ghost): guard anti-duplicados dentro de publish_draft, solo tras POST aceptado
El guard anterior vivía en el fallback de autofill y casaba con CUALQUIER
draft del mismo título: un fallo pre-POST (Ollama caído) con un draft viejo
del mismo topic descartaba en silencio el contenido recién generado.

Ahora la recuperación vive dentro de publish_draft y solo se activa cuando
el POST fue aceptado (2xx) y falla la lectura de la respuesta — el escenario
exacto del incidente br del 2026-07-04 — con filtro since=attempt_start en
find_draft_by_title. Cubre a todos los callers: autofill, bare publish y
/publish (antes sin proteger: reintento del usuario = draft duplicado).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:54:49 +00:00
ChemaVXandClaude Fable 5 68ae6b436c docs: KNOWN-ISSUES.md — gotchas operacionales (brotli/aiohttp, WAL, DDGS, Google News) [skip ci]
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 20:53:34 +00:00
ChemaVXandClaude Fable 5 7d07375dd1 feat(ghost): guard anti-duplicados en el fallback de publish
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
Si el autofill falla DESPUÉS de que Ghost aceptara el POST (p.ej. error
leyendo la respuesta), el draft ya existe: el fallback ahora comprueba
por título exacto entre los drafts recientes (find_draft_by_title) y
reutiliza el existente en vez de duplicar. Solo aplica al camino de
fallback-tras-excepción — el modo OFF y la re-generación deliberada
siguen creando draft nuevo como siempre. Best-effort: si el check
falla, se publica bare como antes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 20:26:23 +00:00
ChemaVXandClaude Fable 5 76c927f0d5 fix(ghost): Accept-Encoding gzip explícito en publish_draft y menú SEO
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
Blindaje contra el default de aiohttp: si algún día se reinstala un
backend brotli, las sesiones sin Accept-Encoding volverían a anunciar
br y el decode roto de aiohttp 3.14 rompería la lectura de respuestas
de Ghost (duplicando drafts). Con el header fijado no puede regresar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 20:19:50 +00:00
ChemaVXandClaude Fable 5 397546a37a fix(deps): quita brotlicffi — hacía que aiohttp anunciara br por defecto
Build & Deploy ResearchOwl / build-and-push (push) Successful in 48s
Regresión de esta mañana: con un backend brotli instalado, aiohttp mete
'br' en el Accept-Encoding por defecto de toda sesión que no lo fija
(publish_draft, seo/autofill). Ghost/Cloudflare responde br y el decode
incremental roto de aiohttp 3.14.1 revienta al leer la respuesta de un
POST ya aceptado: el fallback re-publicaba → drafts duplicados y aviso
SEO perdido. Sin backend brotli nada anuncia br (el scraper ya lo quitó
de sus HEADERS explícitos en 57f341f).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 20:17:56 +00:00
ChemaVXandClaude Fable 5 57f341fc22 fix(scraper): no anunciar br en Accept-Encoding — decode incremental roto en aiohttp 3.14
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
Diagnóstico definitivo: los streams br de disclosure.org/todaywhy.com son
VÁLIDOS (los bytes crudos descomprimen offline con brotlicffi), pero el
descompresor incremental de aiohttp 3.14.1 falla según el troceo de
chunks — intermitente entre sitios e intentos, con Brotli y con
brotlicffi. Sin anunciar br los servidores mandan gzip (verificado:
los 4 sitios afectados, 200 + contenido íntegro) y el camino zlib es
sólido. brotlicffi se queda como red por si llega br no anunciado.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 11:25:58 +00:00
ChemaVXandClaude Fable 5 6c807212a0 fix(scraper): brotlicffi en vez de Brotli — aiohttp 3.14 falla en streams br troceados
Build & Deploy ResearchOwl / build-and-push (push) Successful in 54s
Con Brotli puro, aiohttp 3.14.1 seguía sin decodificar algunos streams
br (disclosure.org, todaywhy.com: 'Can not decode content-encoding: br'
en 3/3 intentos, mientras httpx los decodifica bien). Con brotlicffi
(que aiohttp prefiere sobre Brotli) los mismos sitios decodifican al
primer intento — verificado en el pod con install efímero.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 11:20:04 +00:00
ChemaVXandClaude Fable 5 becc43f770 fix(scraper): instala Brotli — se perdían fuentes br en silencio
Build & Deploy ResearchOwl / build-and-push (push) Successful in 55s
HEADERS anuncia 'Accept-Encoding: gzip, deflate, br' pero aiohttp no
tenía backend brotli: cualquier web que respondiera br fallaba con
'Can not decode content-encoding: brotli' tras 3 reintentos (vistos
washingtontimes, defensescoop, disclosure.org en sesión de prueba).
Verificado local: con Brotli==1.1.0 ambos decodifican (200, encoding=br).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 11:16:00 +00:00
ChemaVXandClaude Fable 5 a23810b9cc feat(scraper): seed de noticias Bing News RSS tras ENABLE_NEWS_SEED (off)
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
Nuevo _seed_news: registra el feed de Bing News como fuente rss y
_extract_rss (F3) siembra sus entries — sin lógica de parseo duplicada.
_unwrap_news_link extrae la URL real del publisher del ?url= de
apiclick.aspx.

Bing y no Google News como pedía el plan: verificado en vivo que los
links de news.google.com/rss/articles/ acaban en muro de consent (UE) y
el id AU_yqL solo se resuelve vía batchexecute interno — habría sembrado
URLs muertas. Los de Bing llevan la URL real en texto plano.

Flag off por defecto: se activará deliberadamente vía deployment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 10:59:00 +00:00
ChemaVXandClaude Fable 5 8dfd0116b2 fix(scraper): DDGS síncrono ya no bloquea el event loop
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
Las llamadas DDGS() en _seed_search y _seed_youtube eran síncronas y se
ejecutaban directamente en código async: durante cada búsqueda DDG el bot
entero (comandos de Telegram incluidos) quedaba congelado. Extraídas a
helpers sync (_ddg_text_sync/_ddg_videos_sync) ejecutados vía
run_in_executor. Sin cambios en queries ni procesamiento de resultados.

Verificado con mock de DDG lento (3s): peor parón del loop 52ms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 10:49:22 +00:00
ChemaVXandClaude Fable 5 c701628144 feat(scraper): F4 — siembra de Internet Archive (mediatype:texts)
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s
_seed_archive() consulta advancedsearch con el topic completo y siembra
las páginas /details/<id> (rows=12); los PDFs de cada item los descubre
la recursión y los extrae _extract_pdf. Verificado contra la API:
'Roswell incident declassified' devuelve informes del Proyecto Mogul y
documentos del CIA Reading Room.

Sin fallback de query a propósito: el AND estricto prima precisión —
relajar términos mete ruido (verificado: 'Manises' a secas devuelve
libros de loza valenciana). Topics sin material en Archive siembran 0
y el resto de seeds cubren. Aislado en try/except propio, además del
return_exceptions del gather.

Cierra el plan F1-F4 de mejora del descubrimiento de fuentes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:49:10 +00:00
ChemaVXandClaude Fable 5 0ce9933aaa fix(scraper): F3 — el cap del RSS aplica a entries añadidas, no escaneadas
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
entries[:20] antes del filtro de relevancia perdía las entries
relevantes que no caen entre las 20 más recientes del feed (verificado
con thedebrief.org: 0 relevantes en las primeras 20, 3 en las 100
totales). Ahora se escanean todas y el cap corta al añadir 20.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:46:09 +00:00
ChemaVXandClaude Fable 5 f210b34e5f feat(scraper): F3 — extractor RSS + detección de feeds con word-boundary
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
detect_source_type ya clasificaba 'rss' pero no había rama en
_process_source: los feeds caían a _extract_web y trafilatura no saca
nada del XML (se descartaban por cortos). Ahora _extract_rss parsea el
feed (feedparser en hilo, como el monitor de noticias) y siembra sus
entries como fuentes nuevas: filtro de relevancia por título/URL,
blacklist, dedupe, cap de 20 y respeto de max_depth. El feed en sí se
marca skipped — es puro descubrimiento, no tiene contenido que trocear.

De paso, la detección pasa de substring puro (clasificaba /feedback y
/atomic como rss) a RSS_RE con límites de palabra. Tests de ambos.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:43:50 +00:00
ChemaVXandClaude Fable 5 9dae61fde8 feat(scraper): F2 — Wikipedia bilingüe siempre y búsqueda full-text
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s
Dos arreglos en _seed_wikipedia, motivados por la validación de F1
(found=0 en ambos idiomas para 'incidente ovni de Manises 1979' pese
a existir el artículo en es.wikipedia):

- opensearch es prefix-de-título: topics verbosos devuelven 0. Se pasa
  a action=query&list=search (full-text, srlimit=8), que para el mismo
  topic encuentra 'Manises UFO incident' (en) e 'Incidente OVNI de
  Manises' (es) + relacionados. Verificado contra la API en ambos.
- Se elimina el break tras en: ahora siembra en + es SIEMPRE, cada
  idioma aislado en su try/except.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:40:08 +00:00
ChemaVXandClaude Fable 5 200de018e4 feat(scraper): F1 — lista de motores SearXNG ampliada y configurable
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
_search_searxng hardcodeaba engines=duckduckgo,google,bing,brave, lo
que ignoraba cualquier motor extra de la instancia (y dejaba la
búsqueda coja cuando brave cae en rate limit y DDG en CAPTCHA, ambos
observados en vivo). Ahora la lista vive en settings.searxng_engines
(env SEARXNG_ENGINES, tunable por GitOps sin rebuild).

Default ampliado con motores verificados contra la instancia el
2026-07-03: +startpage +mojeek +presearch +google news +bing news
+internet archive scholar. Medido con queries reales: ~2x resultados
y ~2x dominios únicos por ~+2s/query. Excluidos qwant (denied),
yahoo (roto), wikidata (0 en texto) y wayback machine (duplicados).

Sin cambios en k8s-manifests: use_default_settings:true ya trae todos
estos motores configurados. El fallback a DDG queda intacto.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:36:04 +00:00
ChemaVXandClaude Fable 5 228ea5c601 feat(news): 3 feeds ES nuevos tras retirar Espacio Misterio
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s
Verificados desde el pod (HTTP 200, entries y match de keywords):
- MysteryPlanet (mysteryplanet.com.ar) — diario, 3/10 match hoy
- Marcianitos Verdes (marcianitosverdes.haaan.com) — casi diario, 2/5
- El Ojo Crítico (elojocritico.info) — mensual, calidad ES

Descartados: Ufopolis (último post feb 2025), Mundo Esotérico (bozo).
Cold-start por fuente: el primer poll de cada uno siembra sin notificar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:25:51 +00:00
ChemaVXandClaude Fable 5 2cb0cf841c feat(deps): python-telegram-bot 21.5 -> 22.8
Build & Deploy ResearchOwl / build-and-push (push) Successful in 49s
Salto mayor revisado contra el changelog oficial:
- La 22.0 elimina lo deprecado en v20.x. De esa lista el bot solo usaba
  disable_web_page_preview (3 sitios), que ademas resulta seguir vivo
  como shim en 22.8 — se migra igualmente a link_preview_options, que
  es la API canonica desde Bot API 7.0.
- No usamos quote=, proxy_url, ni *_timeout en run_polling (el resto
  de eliminaciones). Python >=3.10 (imagen 3.12) y httpx >=0.27,<0.29
  (tenemos 0.28.1) OK.
- Verificado aislado: import de src.bot.bot sin DeprecationWarnings,
  8 tests en verde.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:17:21 +00:00
ChemaVXandClaude Fable 5 5876361ac7 chore(deps): pdfplumber 0.11.10 y python-dotenv 1.2.2
Build & Deploy ResearchOwl / build-and-push (push) Successful in 49s
Folda las dos ramas nuevas de Renovate (las 11 rancias ya las cerró
tras el bump b872d00).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:12:36 +00:00
ChemaVXandClaude Fable 5 a91e119171 refactor(config): migrar a SettingsConfigDict y quitar Field(env=...)
Elimina las dos deprecaciones de Pydantic V2 (se rompían en V3):
- class Config -> model_config = SettingsConfigDict(env_file='.env')
- Field(env='X') en los 33 campos -> resolución automática por nombre
  de campo (case-insensitive en pydantic-settings); verificado que los
  33 nombres coinciden con su variable, así que el comportamiento es
  idéntico. pytest pasa de 34 warnings a 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:12:36 +00:00
ChemaVXandClaude Fable 5 19efc70539 feat(seo): fijar vocabulario de tags ES en el autofill
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s
Cierra el TODO de ALLOWED_TAGS: el allow-list ES son los 6 tags del
núcleo vivo de zonadeexclusion (Admin API, 2026-07-03), espejo de la
lista EN confirmada: uap, desclasificados, casos-militares,
casos-clasicos, investigacion, casos-espana. Quedan fuera la cola de
tags de 1 post que el modelo inventó antes de constreñir y el duplicado
investigacion-2 (colisión de mayúsculas en Ghost).

La regla anti-legacy 'never use "investigacion"' del prompt pasa a ser
solo-EN: en ES ese slug es el tag canónico del allow-list.

Tests de las funciones puras (_coerce, _system_prompt) por idioma.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 07:52:26 +00:00
ChemaVXandClaude Fable 5 b872d00efc chore(deps): bump 9 dependencias y eliminar fastapi/uvicorn sin uso
Build & Deploy ResearchOwl / build-and-push (push) Successful in 47s
Consolida el backlog de Renovate en un solo bump, ignorando las ramas
rancias que proponían downgrades (youtube-transcript-api, trafilatura,
sqlite-vec se quedan como están):

- aiohttp 3.10.0 -> 3.14.1
- httpx 0.27.0 -> 0.28.1 (PTB 21.5 requiere httpx~=0.27, que admite 0.28;
  el código solo usa AsyncClient(timeout=...), sin APIs eliminadas)
- beautifulsoup4 4.12.3 -> 4.15.0
- lxml 5.2.2 -> 5.4.0
- duckduckgo-search 6.2.6 -> 6.4.2
- aiosqlite 0.20.0 -> 0.22.1
- markdown 3.7 -> 3.10.2
- pydantic 2.8.0 -> 2.13.4 + pydantic-settings 2.4.0 -> 2.14.2
- fastapi y uvicorn eliminados: ningún módulo los importa

Verificado con install aislado (--target) + imports completos + pytest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 07:38:27 +00:00
ChemaVXandClaude Fable 5 dcfe674322 fix(news): rotar feeds de Reddit por run + loguear HTTP >=400
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
Reddit sin OAuth ratelimita por IP con ventana larga: la 2ª petición
seguida a reddit.com devuelve 429 siempre (verificado: 30s de pausa no
bastan). Como r/UFOs va antes en FEEDS, r/UFOB recibía 429 en cada poll
desde F1 y nunca llegó a sembrar (0 filas en news_seen).

- Solo se pollea un feed de reddit por run, rotando (_reddit_turn por
  proceso). Con poll de 6h cada subreddit se refresca cada 12h.
- El 429 llegaba con bozo=False y 0 entries, invisible para el guard
  existente: ahora status >= 400 se loguea como warning y se salta.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 07:18:15 +00:00
ChemaVXandClaude Fable 5 480d86e975 fix(news): quitar feed Espacio Misterio (XML malformado)
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s
El feed devuelve 'not well-formed (invalid token)' en cada poll desde la
activación; feedparser nunca extrae entries (bozo). Se elimina para no
generar un warning por tick. Quedan 6 feeds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 07:09:38 +00:00
ChemaVXandClaude Opus 4.8 b1c5bc737d feat(news): F2 — scheduler automático del monitor RSS (inerte)
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
Engancha el monitor de noticias al scheduler loop EXISTENTE de
watched_topics (sin segunda task asyncio). Cada tick, si NEWS_ENABLED,
y si ha pasado NEWS_POLL_INTERVAL_HOURS desde el último poll (estado en
memoria), pollea los feeds, empuja los pendientes (notified=0) al chat
destino (NEWS_CHAT_ID o 1er TELEGRAM_ALLOWED_USERS) y los marca.

Best-effort: la rama de noticias va en su propio try/except — un fallo
del news-poll NUNCA tumba el scheduler de watched_topics. Deploy inerte:
NEWS_ENABLED sigue False; el flip se hará aparte en k8s-manifests.

- db: get_unnotified(limit=None) — pendientes, recientes primero (NULLS LAST)
- bot: rama news al final del tick del scheduler, reusa poll_feeds /
  item_from_row / format_digest de F1; "…y N más" si excede NEWS_MAX_ITEMS

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 10:16:07 +00:00
ChemaVXandClaude Opus 4.8 c0b0c1ba0a feat(news): F1 — monitor RSS best-effort + comando /news manual
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
src/news/monitor.py: 7 feeds UAP/OVNI, matching normalizado (NFKD sin tildes)
con word-boundary para tokens cortos (uap/ufo/nhi/aaro/ovni) y substring para
frases. poll_feeds aísla cada feed (uno caído nunca tumba el run) y parsea en
hilo aparte para no bloquear el event loop; cold-start por fuente (siembra sin
notificar). format_digest agrupa por fuente y trocea <4000 chars. /news refresca
y muestra novedades 24h. Capa desacoplada: src/news NO importa de src/bot.
No toca el scheduler (F2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 20:57:26 +00:00
ChemaVXandClaude Opus 4.8 fd814a3ccf feat(news): F0 — schema, config y métodos DB del monitor RSS (inerte)
news_seen table (CREATE TABLE IF NOT EXISTS, sin migración) + índices.
Config NEWS_* (NEWS_ENABLED=false por defecto) + propiedad news_chat_id
(fallback al 1er TELEGRAM_ALLOWED_USERS). Métodos ResearchDB: source_seeded,
record_news_item, mark_news_notified, get_recent_news. Todo inerte: nada
lo invoca aún. feedparser ya estaba en requirements (6.0.12 >= 6.0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 20:46:38 +00:00
ChemaVXandClaude Opus 4.8 727662294c docs(seo): clarify alt vs caption in pre-publish checklist
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 06:11:45 +00:00
ChemaVXandClaude Opus 4.8 6d39875fcf fix(seo): sanitize malformed link suggestions + clean link report
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 14:49:29 +00:00
ChemaVXandClaude Opus 4.8 7a995da88f feat(seo): wire autofill into publish path behind SEO_AUTOFILL (default off, dry-run support)
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 14:14:39 +00:00
ChemaVXandClaude Opus 4.8 89aa8d6030 Vendor shared SEO rule engine + CI drift guard (no pipeline wiring yet)
src/seo/rules.py is a byte-for-byte copy of the canonical seo_rules.py
(git.chemavx.xyz/chemavx/chemavx-seo-tools). CI step "Verify vendored SEO engine"
clones the canonical and fails the build on any divergence; `make sync-seo` /
`make check-seo-sync` cover the local-canonical workflow. Nothing imports this in
a runtime path yet — generate_seo_fields integration comes next behind SEO_AUTOFILL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 08:17:37 +00:00
ChemaVXandClaude Opus 4.8 b6dc5192b8 Add pre-publish SEO checklist to Ghost draft Telegram notice (deploy pending)
Build & Deploy ResearchOwl / build-and-push (push) Successful in 2m31s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 07:40:20 +00:00
ChemaVXandClaude Opus 4.8 cd557a2d71 perf(db): conexión SQLite compartida por proceso
Build & Deploy ResearchOwl / build-and-push (push) Successful in 5s
get_db() devuelve un proxy sobre una única conexión real reutilizada durante
toda la vida del proceso, en vez de abrir una conexión nueva y re-ejecutar
todo el SCHEMA en cada comando/scoring.

- _SharedConnection: proxy con close() no-op → los handlers conservan el
  patrón get_db()/finally close() sin cambios
- aiosqlite serializa las operaciones en el hilo de la conexión: compartirla
  entre coroutines es seguro y elimina la contención del lock de escritura a
  nivel de fichero (scheduler solapado, /compare con 2 sesiones)
- close_db() vía post_shutdown para checkpoint WAL limpio al apagar

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:16:24 +00:00
ChemaVXandClaude Opus 4.8 2ffad8aad3 perf: scoring de calidad en lote + fuentes YouTube/Reddit opcionales
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s
#1 batch scoring (processor):
- _score_quality_batch puntúa hasta 25 chunks por llamada a Claude en vez
  de una por chunk (una fuente de 19 chunks pasaba de 19 llamadas a 1)
- parser robusto (último número por línea, padding neutro si faltan)
- fallback por-chunk con Ollama si el batch falla

#2 fuentes opcionales (config + scraper):
- ENABLE_YOUTUBE / ENABLE_REDDIT, default False: la IP del homelab está
  bloqueada por Reddit (403) y YouTube (transcripts vacíos), eran peso muerto
- se saltan también las URLs de yt/reddit descubiertas dentro de webs, sin
  gastar petición de red

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:00:47 +00:00
ChemaVXandClaude Opus 4.8 972bd2f883 fix(rag): chunking real por líneas + embedding de chunk completo
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s
simple_chunk:
- parte por \n+ (no solo \n\n): Wikipedia/trafilatura usan \n simple, lo
  que colapsaba cada fuente en un único chunk gigante
- subdivide párrafos que superan chunk_size
- el overlap arrastra un tail de N palabras en vez del párrafo completo
  (evita chunks inflados a ~2x cuando los párrafos son grandes)

processor: embedding sobre el chunk completo (antes truncaba a 1000 chars,
el vector solo representaba el principio del chunk → ranking RAG pobre)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 14:53:00 +00:00
ChemaVXandClaude Opus 4.8 94dc0316f9 chore: add .gitignore y dejar de trackear bytecode .pyc
Build & Deploy ResearchOwl / build-and-push (push) Successful in 1m8s
- .gitignore para Python, .env, *.db y artefactos de editor/OS
- git rm --cached de todos los __pycache__/*.pyc previamente trackeados

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 14:38:46 +00:00
ChemaVXandClaude Opus 4.8 bf275b7f82 fix: correcciones de scraping/DB y mejoras de robustez
Sección crítica:
- is_blacklisted: match por dominio/subdominio exacto (antes "x.com" como
  substring bloqueaba netflix.com, phoenix.com, etc.)
- normalize_url: conserva el query string (rompía YouTube watch?v= y URLs
  con ?id=); solo borra el fragment
- get_db: PRAGMA busy_timeout=5000 para evitar "database is locked" en
  /compare y watches solapados
- OllamaClient.embed: usa OLLAMA_EMBED_MODEL en vez del modelo de chat
- log_api_call: coste por modelo (opus/sonnet/haiku) en vez de Haiku fijo

Mejoras:
- src/llm.py: cliente Anthropic compartido y cacheado (antes se instanciaba
  uno por cada llamada/chunk)
- SEARXNG_URL configurable via env
- get_running_loop() en vez de get_event_loop() (deprecado)
- soup.title.get_text() robusto ante <title> con tags anidados
- limpieza: import muerto, total_words duplicado, w_id no usado

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 14:38:03 +00:00
ChemaVXandClaude Opus 4.8 fd9aaa193b fix: watch diff — envío con fallback a texto plano y task no silencioso
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
El resumen de diff generado por Claude se enviaba con parse_mode=MARKDOWN;
texto libre con entidades desbalanceadas provocaba BadRequest y el mensaje
no llegaba. Además el send_message estaba fuera del try/except de _task y el
task se retenía en _active_tasks sin await, así que la excepción se tragaba
sin log alguno.

- _safe_send(): intenta Markdown y reintenta en texto plano si falla
- _task: except de nivel superior que loguea cualquier fallo

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 18:39:55 +00:00
ChemaVXandClaude Opus 4.8 ca0eb059e8 fix: purga de inicio borra api_usage antes de research_sessions
Build & Deploy ResearchOwl / build-and-push (push) Successful in 6s
La purga de sesiones >30d fallaba con FOREIGN KEY constraint failed:
api_usage.session_id referencia research_sessions(id) pero nunca se
borraba antes de la sesión padre (con PRAGMA foreign_keys = ON).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 14:00:54 +00:00
ChemaVXandClaude Opus 4.8 41e4e3f5d6 feat: /watch --at HH:MM — hora absoluta en Europe/Madrid
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 13:55:52 +00:00
Renovate Bot 4bfd27db2c chore(deps): update dependency youtube-transcript-api to v0.6.3
Build & Deploy ResearchOwl / build-and-push (push) Successful in 1m5s
2026-05-29 12:00:36 +00:00
Renovate Botandchemavx 30509dab4a chore(deps): update dependency trafilatura to v1.12.2
Build & Deploy ResearchOwl / build-and-push (push) Successful in 45s
2026-05-28 12:00:45 +00:00
Renovate Bot f6ad57e3c7 chore(deps): update dependency sqlite-vec to v0.1.9
Build & Deploy ResearchOwl / build-and-push (push) Successful in 49s
2026-05-26 12:00:47 +00:00
Renovate Bot 2a9962a1bd chore(deps): update dependency pdfplumber to v0.11.9
Build & Deploy ResearchOwl / build-and-push (push) Successful in 46s
2026-05-25 18:00:36 +00:00
Renovate Bot 0e52b404bf chore(deps): update dependency feedparser to v6.0.12
Build & Deploy ResearchOwl / build-and-push (push) Successful in 53s
2026-05-21 12:00:29 +00:00
chemavx 3a9ab2848a Merge pull request 'chore: Configure Renovate' (#1) from renovate/configure into main
Build & Deploy ResearchOwl / build-and-push (push) Successful in 5s
Reviewed-on: #1
2026-05-20 14:11:30 +00:00