feat(health): persistent strategy learning and meta-instructions for future continuity

This commit is contained in:
Nubenetes Bot
2026-05-09 15:43:51 +02:00
parent cde4a82ef0
commit bdfe38fd08
2 changed files with 47 additions and 7 deletions

34
GEMINI.md Normal file
View File

@@ -0,0 +1,34 @@
# Nubenetes Intelligent Curation: Meta-Instructions & Learning Roadmap
Este archivo contiene las instrucciones acumuladas y la visión de largo plazo para el mantenimiento autónomo de Nubenetes.com. Los agentes de IA deben consultar este documento en cada iteración para garantizar la continuidad del aprendizaje.
## 🧠 Core Mandates (Mandatos Principales)
1. **Preservación de la Información**: NUNCA elimines resúmenes, comentarios o estrellas (🌟) que acompañan a los enlaces. El bot solo debe actualizar la URL o reorganizar la posición del ítem, nunca borrar el contexto descriptivo.
2. **Aprendizaje Persistente**: Utiliza `src/memory/health_learning.json` para almacenar el conocimiento sobre dominios (bloqueos anti-bot, estrategias exitosas) y patrones de navegación.
3. **Resiliencia Total**: El workflow debe ser capaz de continuar incluso si hay errores individuales en validaciones de links o archivos. Prioriza generar un resultado (PR) aunque sea parcial.
4. **Consolidación de Repositorios**: Ante un fallo en un enlace profundo de GitHub/GitLab, intenta siempre validar la raíz del repositorio antes de darlo por muerto. Preferimos enlaces estables a raíces de repositorios que deep-links volátiles.
## 🛠️ Evolución Estructural (Progressive Reorganization)
* **Subsecciones Inteligentes**: El sistema debe detectar categorías superpobladas (>15 links) y proponer subdivisiones semánticas usando Gemini.
* **Integridad de Navegación**: Cada cambio en los archivos markdown debe reflejarse en:
* `mkdocs.yml` (Menú horizontal/lateral).
* `docs/index.md` (Tabla de contenidos principal).
* El TOC interno de cada markdown (si lo tiene).
* **Curación de Huérfanos**: Audita periódicamente la carpeta `docs/` para encontrar archivos no enlazados e intégralos en la navegación según su temática.
## 🚀 Estrategias de Evasión de Bloqueos
El bot debe rotar entre perfiles para evitar ser detectado:
1. **Desktop/Google**: Petición estándar de escritorio.
2. **Mobile/Twitter**: Petición móvil con Referer de Twitter (alta tasa de éxito).
3. **Playwright/LinkedIn**: Navegación real con JS habilitado.
4. **Firefox/Reddit**: Perfil alternativo de escritorio.
## 📈 Diario de Aprendizaje (Historial de Mejoras)
* **Mayo 2026**: Implementación inicial del motor autónomo con Playwright y Wayback Machine.
* **Mayo 2026**: Añadido sistema de Evasión Multidimensional (5 intentos, rotación de perfiles).
* **Mayo 2026**: Creación del `AgenticCurator` para auditoría de navegación y consolidación de repositorios.
* **Mayo 2026**: Generación de PRs con analíticas visuales (Mermaid) y Matriz de Salud.

View File

@@ -57,7 +57,6 @@ class IntelligentLinkCleaner:
async def _check_url_with_retries(self, url: str, max_retries=5) -> Tuple[str, bool, Optional[str], str]:
domain = url.split("//")[-1].split("/")[0]
domain_info = self.learning_data["domains"].get(domain, {})
use_playwright_first = domain_info.get("requires_playwright", False)
# Estrategias de Evasión (Perfiles)
strategies = [
@@ -68,9 +67,12 @@ class IntelligentLinkCleaner:
{"type": "playwright", "ua": "Mozilla/5.0 (Linux; Android 13; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", "ref": "https://www.google.com/", "desc": "PW Mobile/Google"}
]
if use_playwright_first:
# Reordenar para priorizar Playwright
strategies = [s for s in strategies if s["type"] == "playwright"] + [s for s in strategies if s["type"] == "http"]
# Inteligencia: Si ya conocemos qué estrategia funciona para este dominio, reordenamos
best_strat_idx = domain_info.get("best_strategy_idx")
if best_strat_idx is not None and best_strat_idx < len(strategies):
# Mover la mejor estrategia al principio
best_strat = strategies.pop(best_strat_idx)
strategies.insert(0, best_strat)
for attempt in range(min(max_retries, len(strategies))):
strategy = strategies[attempt]
@@ -80,11 +82,16 @@ class IntelligentLinkCleaner:
is_alive, reason = await self._check_url_logic(url, strategy)
if is_alive:
if domain not in self.learning_data["domains"]: self.learning_data["domains"][domain] = {"success_count": 0, "fail_count": 0}
self.learning_data["domains"][domain]["success_count"] += 1
if domain not in self.learning_data["domains"]: self.learning_data["domains"][domain] = {}
# Guardamos el índice real de la estrategia que funcionó
# (si movimos la mejor al principio, hay que mapearla de nuevo)
original_idx = attempt if best_strat_idx is None else (best_strat_idx if attempt == 0 else (attempt if attempt < best_strat_idx else attempt))
self.learning_data["domains"][domain]["best_strategy_idx"] = original_idx
self.learning_data["domains"][domain]["success_count"] = self.learning_data["domains"][domain].get("success_count", 0) + 1
return url, True, None, f"Alive ({strategy['desc']})"
if reason in ["404", "soft_404", "redirect_to_home"]:
# REPO CONSOLIDATION
if any(git_host in url for git_host in ["github.com", "gitlab.com", "bitbucket.org"]):
parts = url.split("/")
if len(parts) > 4:
@@ -92,7 +99,6 @@ class IntelligentLinkCleaner:
root_alive, _ = await self._check_url_logic(repo_root, strategies[0])
if root_alive: return url, False, f"REPO_ROOT:{repo_root}", f"Consolidated (Original: {reason})"
# Si es el último intento y sigue dando error duro (404), verificamos Wayback
if attempt == max_retries - 1:
archived = await self._check_wayback(url)
if archived: return url, False, f"ARCHIVE:{archived}", f"Archived (Original: {reason})"