Files
dtic-DIIAA/docs/ambito/dtic-ADN/A01.P009_Evolucion-Dron-ADN.md
T
Ricardo MonlaandClaude Opus 4.6 4a0cdd883e [DRON] Heartbeat con output acumulado en Bitácora Web
Mejora en el sistema de latidos de los drones para mostrar el progreso
acumulado en lugar de solo la última línea.

Cambios realizados:
- ejecutor.rb: Acumulación de output en array accumulated_out[]
- actualizar_heartbeat_web(): Muestra últimas 15 líneas en bloque ```text
- A01.P009: Documentada Fase F1.5b con ejemplo técnico
- A04.P006: Actualizado plan DASUTEN con mejora y lección aprendida

Beneficio: Visibilidad completa del progreso de drones multi-paso
(ej: [1/3], [2/3], [3/3] en pipelines de backup/restore)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 10:45:11 -03:00

41 KiB

A01.P009 - Evolución del Ecosistema ADN al Concepto de Dron

Estado: 🟢 Fase 2 en progreso
Pertenece a: A01 - Ecosistema ADN
Fecha: 2026-04-09
Responsable: Lic. Ricardo MONLA
Versión: 3.5 — Fase 2: rclone atómico planificado + Heartbeat acumulado (2026-04-15)


📈 Progreso

Fase 0:    ██████████ 100%  Infraestructura de Datos (tablas internas) ✅
Fase 1:    ██████████ 100%  Atomicidad (bugs corregidos) ✅
Fase 1.5:  ██████████ 100%  Visibilidad Bitácora Web + Corrección Bugs ✅
Fase 1.5b: ██████████ 100%  Heartbeat con Output Acumulado ✅ (2026-04-15)
Fase 2:    ████████░░  80%  Composición — Orquestación de Flujos 🔧
Fase 3:    ░░░░░░░░░░   0%  Planificación — Drones Programados
Fase 4:    ░░░░░░░░░░   0%  Inteligencia de Colmena
Fase 5:    ░░░░░░░░░░   0%  Observabilidad — Dashboard y Métricas

📋 Resumen Ejecutivo

Este plan define la evolución del ecosistema ADN hacia una arquitectura de Inteligencia de Colmena basada en AtomicDrones — drones atómicos especializados que, compuestos, forman una maquinaria autónoma integral.

Filosofía de Diseño:

Principio Descripción
🧬 AtomicDrones Cada dron hace UNA cosa y la hace bien. Sin monolitos.
🤖 Inteligencia de Colmena La solución emerge de la composición de drones simples.
⚙️ Engranajes Especializados Drones ejecutores, vigilantes, sanadores, planificadores.
🚫 No FrankenDrones Rechazar drones que intentan hacer todo mal.

Concepto de Dron ADN:

  • Atómico: Una responsabilidad única (Single Responsibility)
  • Componible: Se ensambla con otros drones para soluciones complejas
  • Auto-bitácora: Registra inicio/fin automáticamente
  • Observable: Estado visible vía dron flota y dashboard web
  • Efímero: Nace, ejecuta, muere (sin estado persistente innecesario)

🎯 Objetivo

Evolucionar el ecosistema ADN hacia una colonia de AtomicDrones especializados que, compuestos, resuelven problemas complejos mediante inteligencia de colmena.

Meta: Reducir la fricción operativa mediante drones atómicos que se especializan en una tarea única y se componen para flujos complejos.


🐝 Tipos de AtomicDrones (Especialización)

Cada tipo de dron tiene una única responsabilidad. La composición de múltiples drones resuelve problemas complejos.

Tipo Símbolo Responsabilidad Ejemplo
Ejecutor 🛠️ Ejecuta un comando y reporta resultado dron_ejecutor
Vigilante 👁️ Monitorea estado de otros drones dron_vigilante
Sanador 🩹 Repara drones zombies/fallidos dron_sanador
Planificador 📅 Lanza drones según cron dron_planificador
Mensajero 📨 Notifica resultados (Slack, email) dron_mensajero
Orquestador 🎼 Compone múltiples drones en flujo dron_orquestador

Ejemplo de Composición (Inteligencia de Colmena)

Problema: Backup nocturno de SQL + sync a nube + notificación

Solución FrankenDron ( rechazar):

# Un solo dron que hace todo (monolito frágil)
dron_backup_nocturno.rb # 500 líneas, 7 responsabilidades

Solución AtomicDrones ( adoptar):

# Orquestador compone 4 drones especializados
dron_orquestador \
  --step "dron_ejecutor --cmd 'vzdump 103'" \
  --step "dron_ejecutor --cmd 'rclone sync /bkps oneDrive:'" \
  --step "dron_vigilante --check exit_code" \
  --step "dron_mensajero --notify slack 'Backup OK'"

📡 Medio de Comunicación: Bitácora Web como Registro Central

La Bitácora Web es el medio de comunicación donde los drones dejan registro de su actividad. El sistema sigue el principio DB-First con capas de visibilidad.

Principio de Codificación: Atomisidad

Atomisidad es el principio de código compartido que aplica "Menos es Más" a la implementación de drones:

┌─────────────────────────────────────────────────────────────┐
│  Dron::Base (módulo compartido)                             │
│  - logger: Logger único para todos los drones               │
│  - db_query: Ejecución de SQL con resultados                │
│  - db_exec: Ejecución de SQL sin resultados                 │
│  - formato_duracion: Utilitario común                       │
│  - slice_safe: Manejo seguro de strings                     │
│  - blank?: Verificación de nil/empty                        │
└─────────────────────────────────────────────────────────────┘
                          │
        ┌─────────────────┼─────────────────┐
        │                 │                 │
        ▼                 ▼                 ▼
┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│  Ejecutor    │  │  Vigilante   │  │   Sanador    │
│  extend Base │  │  extend Base │  │  extend Base │
└──────────────┘  └──────────────┘  └──────────────┘

Beneficios:

  • Un solo lugar para corregir errores comunes
  • ~40% menos líneas de código
  • Nuevos drones heredan funcionalidad automáticamente

Arquitectura de Registros

┌─────────────────────────────────────────────────────────────────┐
│  DRONES (Ejecutores, Vigilantes, Sanadores, etc.)               │
│  Cada dron deja registro al ejecutar                            │
└────────────┬────────────────────────────────────────────────────┘
             │ registra
             ▼
┌─────────────────────────────────────────────────────────────────┐
│  CAPA 1: Tablas Internas (NO visibles directamente)             │
│  - bitacoras.dron_logs: Logs detallados de cada dron           │
│  - bitacoras.dron_avances: Progreso paso-a-paso                │
│  - bitacoras.dron_metricas: Métricas de rendimiento            │
│  - bitacoras.dron_errores: Errores y reintentos                │
└────────────┬────────────────────────────────────────────────────┘
             │
             │ dron_vigilante consolida
             ▼
┌─────────────────────────────────────────────────────────────────┐
│  CAPA 2: Bitácora Web (Resumen visible)                         │
│  - events: Eventos consolidados por flujo/orquestación         │
│  - Descripción: Resumen legible del progreso                   │
│  - Estado: ⏳ En ejecución | ✅ Completado | ❌ Fallido         │
│  - URL: http://localhost:5174/bitacoras/                       │
└─────────────────────────────────────────────────────────────────┘

Tablas Internas (Esquema bitacoras)

-- Logs detallados de cada dron (NO visible en web directamente)
CREATE TABLE bitacoras.dron_logs (
    id              SERIAL PRIMARY KEY,
    dron_id         VARCHAR(50) NOT NULL,        -- ej: dron_083045_123
    flujo_id        VARCHAR(50),                  -- ID de orquestación (si aplica)
    tipo            VARCHAR(20) NOT NULL,         -- ejecutor|vigilante|sanador|planificador|mensajero
    estado          VARCHAR(20) NOT NULL,         -- pending|running|completed|failed|zombie
    cmd             TEXT,                         -- Comando ejecutado
    output          TEXT,                         -- Salida del comando
    exit_code       INTEGER,                      -- Código de retorno
    started_at      TIMESTAMP DEFAULT NOW(),
    completed_at    TIMESTAMP,
    heartbeat       TIMESTAMP DEFAULT NOW(),      -- Último heartbeat
    reintentos      INTEGER DEFAULT 0,
    metadata        JSONB DEFAULT '{}'::jsonb     -- Metadata adicional
);

-- Avances paso-a-paso (para flujos multi-paso)
CREATE TABLE bitacoras.dron_avances (
    id              SERIAL PRIMARY KEY,
    dron_id         VARCHAR(50) NOT NULL,
    paso            INTEGER NOT NULL,             -- Número de paso en flujo
    descripcion     TEXT NOT NULL,                -- Qué se está haciendo
    estado          VARCHAR(20) NOT NULL,         -- pending|running|completed|failed
    started_at      TIMESTAMP DEFAULT NOW(),
    completed_at    TIMESTAMP,
    metadata        JSONB DEFAULT '{}'::jsonb
);

-- Métricas de rendimiento (agregaciones)
CREATE TABLE bitacoras.dron_metricas (
    id              SERIAL PRIMARY KEY,
    fecha           DATE NOT NULL,
    dron_tipo       VARCHAR(20) NOT NULL,
    total_ejecuciones INTEGER DEFAULT 0,
    completados     INTEGER DEFAULT 0,
    fallidos        INTEGER DEFAULT 0,
    zombies         INTEGER DEFAULT 0,
    tiempo_promedio INTERVAL,
    reintentos_total INTEGER DEFAULT 0,
    UNIQUE(fecha, dron_tipo)
);

-- Índices para consultas eficientes
CREATE INDEX idx_dron_logs_estado ON bitacoras.dron_logs(estado);
CREATE INDEX idx_dron_logs_heartbeat ON bitacoras.dron_logs(heartbeat);
CREATE INDEX idx_dron_logs_flujo ON bitacoras.dron_logs(flujo_id);
CREATE INDEX idx_dron_avances_dron ON bitacoras.dron_avances(dron_id);

Dron Vigilante: El Monitor de Flotas

El dron_vigilante es responsable de:

  1. Escanear flota: Lee bitacoras.dron_logs para detectar drones activos
  2. Detectar zombies: heartbeat > 5min → marca como zombie
  3. Registrar avances: Inserta en bitacoras.dron_avances el progreso
  4. Consolidar resumen: Crea/actualiza evento en bitacoras.events (visible en web)
  5. Alertar anomalías: Si detecta >5 fallos en 1h → notifica
# Ejemplo: dron_vigilante escanea y registra avances
class DronVigilante
  def escanear_flota
    drones_activos = DronDB.where("estado = 'running' AND heartbeat < ?", 5.minutes.ago)
    
    drones_activos.each do |dron|
      # Marcar como zombie
      dron.update(estado: 'zombie')
      
      # Registrar en avances
      DronAvance.create(
        dron_id: dron.id,
        paso: -1,
        descripcion: "Dron detectado como zombie (heartbeat > 5min)",
        estado: 'failed'
      )
      
      # Consolidar resumen en events (visible en Bitácora Web)
      consolidar_resumen(dron.flujo_id)
    end
  end
  
  def consolidar_resumen(flujo_id)
    # Obtener todos los logs del flujo
    logs = DronLog.where(flujo_id: flujo_id)
    
    # Calcular estado consolidado
    estado = if logs.all? { |l| l.estado == 'completed' }
               'completed'
             elsif logs.any? { |l| l.estado == 'failed' }
               'failed'
             else
               'running'
             end
    
    # Actualizar/crear evento resumen (visible en Bitácora Web)
    Evento.find_or_create_by(flujo_id: flujo_id).update(
      descripcion: "Flujo #{flujo_id}: #{logs.count} drones - #{estado}",
      estado: estado
    )
  end
end

Bitácora Web: Lo que el Usuario Ve

La Bitácora Web (http://localhost:5174/bitacoras/) muestra:

Columna Contenido
ID ID del evento resumen
I Hora de inicio del flujo
F Hora de fin (si completado)
Descripción Resumen consolidado (ej: "Backup nocturno: 4 drones completados")
J Modo (P=Presencial, R=Remoto, A=Automático)
E Estado (, , )
Acc. Acciones (ver detalle, reintentar)

Al hacer clic en "ver detalle":

  • Muestra tabla con drones individuales y su estado
  • Muestra avances paso-a-paso del flujo
  • Muestra métricas de rendimiento (tiempo, reintentos)

📊 Estado Actual (Línea Base — Auditoría 2026-04-09)

Herramientas Existentes

Herramienta Ubicación Tipo Estado
dron.rb adn/tools/cli/dron.rb Dispatcher Refactorizado
dron/ejecutor.rb adn/tools/cli/dron/ejecutor.rb Ejecutor atómico ⚠️ Bugs en bitácora web
dron/vigilante.rb adn/tools/cli/dron/vigilante.rb Vigilante atómico ⚠️ Bugs en consolidación
dron/sanador.rb adn/tools/cli/dron/sanador.rb Sanador atómico ⚠️ Bugs en reintentos
dron/bitacora.rb adn/tools/cli/dron/bitacora.rb Bitácora atómica Implementado
dron/base.rb adn/tools/cli/dron/base.rb Módulo compartido Implementado
dron_db.rb adn/tools/db/core/dron_db.rb Acceso a DB ⚠️ 3 bugs críticos
dispatcher.rb adn/tools/cli/dron/dispatcher.rb Routing subcomandos ⚠️ 1 bug crítico

Capacidades Actuales

# Ejecutor (lanzar tarea con auto-bitácora)
./adn/tools/run dron lanzar --evento AUTO --nota "Backup" -- <cmd>

# Vigilante (ver flota)
./adn/tools/run dron flota
./adn/tools/run dron salud
./adn/tools/run dron estado <dron_id>

# Sanador (health check + limpiar + reintentar)
./adn/tools/run dron sanear --dry-run
./adn/tools/run dron limpiar
./adn/tools/run dron reintentar

🐛 Deuda Técnica Detectada (Auditoría 2026-04-09)

Bugs Críticos (crashes directos)

# Archivo Línea Bug Impacto
B1 dispatcher.rb 214 BitacorasDB::BitacoraDB.ejecutar() — método inexistente (debe ser .with_connection { |db| db.execute() }) dron estado <ID> crashea con NoMethodError
B2 dron_db.rb 178-179 incrementar_reintentos hace doble .first (.first.to_h → Hash, luego .first sobre Hash = par [k,v]) Sanador crashea al reintentar drones
B3 dron_db.rb 231-232 obtener_metricas mismo patrón de doble .first Consulta de métricas crashea

Bugs Medios (fallan en ciertos flujos)

# Archivo Línea Bug Impacto
B4 dron_db.rb 337-345 limpiar_antiguos — DELETE sin RETURNING intenta leer ['count'] inexistente Limpieza reporta 0 siempre
B5 dron_db.rb 253 actualizar_metricas — pasa Float donde PG espera INTERVAL Error de tipo al guardar métricas
B6 sanador.rb 27 Time.now - dron['completed_at'] — el campo viene como String, no Time TypeError al calcular backoff
B7 sanador.rb 41 Dron::Ejecutor.lanzar(...) sin require_relative 'ejecutor' NameError si se llama directamente

Bugs Bajos (edge cases)

# Archivo Línea Bug Impacto
B8 sanador.rb 41 dron.dig('metadata', 'nota') — metadata es String JSON, no Hash Nota de reintento siempre nil
B9 ejecutor.rb 42-75 Timeout nunca se aplica (falta Timeout.timeout() wrapping) Drones largos nunca se frenan
B10 vigilante.rb 141,143 BitacorasDB::BitacoraDB.crear_evento(...) como método de clase (es de instancia) Consolidación de flujos falla silenciosamente
B11 ejecutor.rb 82-131 registrar_bitacora con rescue genérico silencia todos los errores Fallos de bitácora web invisibles
B12 dron.rb / base.rb 30 / 27 Logger: .instance (singleton) vs .new (instancia separada) Logs inconsistentes

🔍 Brecha Principal: Visibilidad en Bitácora Web

Problema crítico: Los drones escriben en tablas internas (dron_logs) pero la Bitácora Web (bitacoras.events) no refleja su actividad.

  CAPA 1 (dron_logs)          CAPA 2 (events → Web)
  ┌─────────────────┐         ┌─────────────────┐
  │ ✅ Drones       │ ──?──→  │ ❌ La web no ve │
  │    escriben acá │         │    nada          │
  └─────────────────┘         └─────────────────┘
                      ↑
                La flecha "vigilante consolida"
                NO FUNCIONA por bugs B1, B10, B11

Causas raíz:

  1. El heartbeat solo actualiza dron_logs.heartbeat (tabla interna), nunca events
  2. El ejecutor tiene lógica de bitácora web pero falla silenciosamente (B11)
  3. El vigilante llama métodos de clase inexistentes (B10)
  4. Coexiste un sistema antiguo de scripts .sh (que sí funcionaba) con el nuevo Ruby (que no)

Limitaciones Futuras (post-corrección)

# Limitación Tipo Impacto Prioridad
1 Sin orquestación de flujos Orquestador No hay composición multi-paso Media
2 Sin planificación (cron) Planificador Requiere lanzamiento manual Alta
3 Sin notificaciones Mensajero Alertas manuales Media
4 Sin dashboard web dedicado Observabilidad Solo CLI + Bitácora Web Baja

🗺️ Fases de Implementación

Fase 0: Infraestructura de Datos (Dron 0.5) — COMPLETA

Objetivo: Crear tablas internas para registro detallado de drones.

ID Tarea Descripción Estado
F0.T1 Migración 003_dron_logs.sql Tabla bitacoras.dron_logs Completa
F0.T2 Migración 004_dron_avances.sql Tabla bitacoras.dron_avances Completa
F0.T3 Migración 005_dron_metricas.sql Tabla bitacoras.dron_metricas Completa
F0.T4 adn/tools/db/core/dron_db.rb Acceso a datos de drones Completa
F0.T5 Hooks DB-First Auto-registro en dron_logs al lanzar Completa

Criterio de éxito: Drones registran actividad en tablas internas sin tocar events directamente.


Fase 1: Atomicidad — Separación de Responsabilidades (Dron 1.0) — ⚠️ CON DEUDA TÉCNICA

Objetivo: Refactorizar el monolito dron.rb en módulos atómicos especializados.

ID Tarea Tipo de Dron Estado
F1.T1 dron/ejecutor.rb Ejecutor — lanza y monitorea comando ⚠️ Bugs B9, B11
F1.T2 dron/vigilante.rb Vigilante — escanea flota, detecta zombies ⚠️ Bug B10
F1.T3 dron/sanador.rb Sanador — re-intenta, limpia, notifica fallos ⚠️ Bugs B6, B7, B8
F1.T4 dron/bitacora.rb Registrador — auto-bitácora (inicio/fin) Completa
F1.T5 dron/dispatcher.rb Dispatcher — routing de subcomandos ⚠️ Bug B1
F1.T6 dron/base.rb Módulo compartido (atomisidad) Completa
F1.T7 dron_db.rb Capa de datos para drones ⚠️ Bugs B2, B3, B4, B5

Criterio de éxito original: Cada módulo tiene una única responsabilidad. ~40% menos código.
Criterio revisado: ⚠️ La estructura atómica es correcta pero 12 bugs impiden operación confiable. Corregidos en Fase 1.5.


Fase 1.5: Visibilidad en Bitácora Web + Corrección de Bugs (Dron 1.1) — COMPLETA

Objetivo: Que los drones sean visibles desde la Bitácora Web y corregir toda la deuda técnica de Fase 1.

Problema que resuelve: Actualmente los drones son "fantasmas" — ejecutan tareas pero el usuario no puede ver su progreso desde la interfaz web principal. Solo escriben en tablas internas (dron_logs) pero nunca actualizan bitacoras.events de forma confiable.

Lo que el usuario debería ver en Bitácora Web:

┌─────┬───────┬───────┬─────────────────────────────────────────────┬───┬───┐
│ ID  │  I    │  F    │ Descripción                                 │ J │ E │
├─────┼───────┼───────┼─────────────────────────────────────────────┼───┼───┤
│1420 │ 02:00 │       │ 🛸 Backup nocturno (dron_020000_123)        │ A │ ⏳│
│     │       │       │   ├─ Ejecutando: vzdump 103 --mode stop      │   │   │
│     │       │       │   └─ ❤️ Heartbeat: hace 45s                 │   │   │
├─────┼───────┼───────┼─────────────────────────────────────────────┼───┼───┤
│1419 │ 01:30 │ 01:45 │ 🛸 Sync rclone (dron_013000_456) — ✅ 15m  │ A │ ✅│
│     │       │       │   └─ Aterrizaje limpio — 2.3GB sincronizados │   │   │
└─────┴───────┴───────┴─────────────────────────────────────────────┴───┴───┘
ID Tarea Descripción Estado
F1.5.T1 Corregir bugs críticos Bugs B1, B2, B3 — crashes directos en dispatcher y dron_db
F1.5.T2 Corregir bugs medios Bugs B4, B5, B6, B7 — fallos en sanador y métricas
F1.5.T3 Simplificar registrar_bitacora Reescribir el método del ejecutor: crear evento → heartbeat → cierre limpio
F1.5.T4 Heartbeat visible en web Que el heartbeat cada 30s actualice events.descripcion con progreso
F1.5.T5 Eliminar sistema dual .sh/Ruby copiloto.rb y novato.rb deprecados, tmp/dron/ limpiado
F1.5.T6 Corregir bugs bajos Bugs B8-B12 — edge cases y consistencia
F1.5.T7 Heartbeat con output acumulado Mostrar últimas 15 líneas del output acumulado en lugar de solo la última línea (2026-04-15)

Flujo corregido del Ejecutor:

  Ejecutor.lanzar(cmd, nota)
         │
         ├─ 1. Registrar en dron_logs (tabla interna)     ──→ DronDB
         ├─ 2. Crear evento en events (Bitácora Web)      ──→ 🌐 VISIBLE
         │
         ├─ 3. Ejecutar comando con heartbeat thread:
         │      loop cada 30s:
         │        ├─ Actualizar dron_logs.heartbeat         ──→ DronDB
         │        └─ Actualizar events.descripcion          ──→ 🌐 VISIBLE
         │
         ├─ 4. Comando finaliza
         ├─ 5. Registrar fin en dron_logs                  ──→ DronDB
         └─ 6. Cerrar evento en events (estado + hora fin) ──→ 🌐 VISIBLE

Criterio de éxito:

  • Cada dron lanzado aparece inmediatamente en la Bitácora Web como evento
  • El heartbeat actualiza la descripción del evento en web cada 30 segundos
  • Al finalizar, el evento se cierra con o , hora de fin, y duración
  • dron estado <ID>, dron flota, y dron salud funcionan sin crashes
  • Cero bugs críticos, cero bugs medios

Mejora: Heartbeat con Output Acumulado (2026-04-15)

Problema: Los drones que ejecutan pipelines multi-paso (ej: dasuten_download_drive-dasu-sql4.rb) muestran progreso tipo [1/3], [2/3], [3/3], pero el heartbeat original solo capturaba la última línea (last_out), perdiendo el contexto del progreso acumulado.

Solución: Modificar Ejecutor#ejecutar_comando para acumular todo el output en un array accumulated_out[] y pasar el output completo al heartbeat web.

Cambios en adn/tools/cli/dron/ejecutor.rb:

# ANTES (solo última línea):
last_out = lineas.last&.strip || last_out
actualizar_heartbeat_web(..., last_out)

# AHORA (output acumulado completo):
accumulated_out = []
accumulated_out.concat(lineas) # Acumular todas las líneas
actualizar_heartbeat_web(..., accumulated_out.join("\n"))

Cambios en actualizar_heartbeat_web:

# ANTES (últimos 80 chars de una línea):
clean_out = last_out.gsub(/[^[:print:]]/, '').strip
descripcion += "\n\n> 🕵️ `#{clean_out[-80..-1] || clean_out}`"

# AHORA (últimas 15 líneas en bloque de código):
clean_out = accumulated_out.gsub(/[^[:print:]\n]/, '').strip
lineas = clean_out.split("\n")
lineas_mostrar = lineas.last(15)
descripcion += "\n\n```text\n#{lineas_mostrar.join("\n")}\n```"

Resultado en Bitácora Web:

🛸 Download desde Google Drive (`dron_152045_789`)
├─ Comando: `ruby dasuten_download_drive-dasu-sql4.rb`
└─ ❤️ 2m 30s — heartbeat #5

```text
[*] Dron: Download desde Google Drive
    Nodo: dasu-sql4
    Drive ruta: rmonla-GDrive:drive_bkps-dasu/sysdasuten.bak
    File ID: 1P0ci...
    Destino: F:\BACKUP\sysdasuten.bak

[1/3] Iniciando descarga con Rclone Nativo...
[2/3] Descargando...
[+] Descarga en 45.2s
[3/3] Verificando...
    Tamano: 1.33 MB

**Beneficio:** El usuario puede ver **todo el progreso acumulado** del dron en tiempo real, no solo la última línea. Esto es crítico para pipelines largos donde cada paso tiene su propio progreso numerado.

---

### Fase 2: Composición — Orquestación de Flujos (Dron 1.5)

**Objetivo:** Permitir composición de drones para flujos multi-paso. Aplicar "Menos es Más" a todos los procesadores — **1 Dron = 1 Unidad Atómica**.

| ID | Tarea | Descripción | Estado |
|:--:|-------|-------------|--------|
| F2.T1 | `bkps orquestar` | Orquestación 1-dron-por-archivo (scan → lanzar secuencial) | ✅ |
| F2.T1a | `proc_xen.rb` | `listar()` + `ejecutar_uno()` — 1 dron por `.xva` | ✅ |
| F2.T1b | `proc_proxmox.rb` | `listar()` + `ejecutar_uno()` — 1 dron por set VM | ✅ |
| F2.T1c | `sync_rclone.rb` | `listar()` + `ejecutar_uno()` — 1 dron por directorio de nodo | ⏳ |
| F2.T2 | Condicionales | `--on-error continue\|abort\|retry` + `--retries N` | ✅ |
| F2.T3 | Paralelismo | `--parallel` para pasos independientes | ⏳ |
| F2.T4 | Contexto compartido | Variables entre pasos (`$DRON_OUTPUT_N`) | ⏳ |

#### F2.T1c — Atomización de Rclone (1 dron por nodo)

**Problema actual:**
```bash
# MONOLITO: un solo rclone sync para ~260GB (17 nodos)
rclone sync /mnt/ns8Disco3/.../bkps-SERVIDORes/ rmOneDrive:/.../bkps-SERVIDORes/
# Si falla en el nodo 15 de 17, hay que reiniciar TODO

Solución AtomicDrones:

# ATÓMICO: 1 dron por directorio de nodo
# Dron 1: rclone sync .../srvv-DNS/    → rmOneDrive:.../srvv-DNS/     (1.5GB)
# Dron 2: rclone sync .../srvv-KOHA/   → rmOneDrive:.../srvv-KOHA/    (9.7GB)
# ...
# Dron 17: rclone sync .../srv-SysACADWeb/ → rmOneDrive:.../srv-SysACADWeb/ (46GB)

Implementación requerida en sync_rclone.rb:

Método Qué hace
listar(tarea) Escanea subdirectorios del origen, retorna lista de {nombre, ruta, tamaño}
ejecutar_uno(tarea, directorio) rclone sync origen/NODO/ destino/NODO/ para un solo nodo

Inventario de nodos (17 directorios, ~260GB total):

  8K  pcv-DASU3          474M srvv-SITIO2       17G pcv-DASU2
 648M srvv-UPTIME        966M srvv-N8N          18G pcv-SERVIIO
 1.5G srvv-DNS           6.9G srvv-SITIO        23G pcv-DASU1
 7.5G srvv-DOCs          9.7G srvv-KOHA         27G srvv-DTIC
  13G srvv-DATA           14G srvv-SITIO0       37G srv-MauriK
  38G srvv-FENIX          46G srv-SysACADWeb

Uso propuesto:

# Dry-run: ver nodos pendientes de sync
./adn/tools/run bkps orquestar T5 --dry-run

# Real: 1 dron por nodo, secuencial, con retry ante fallos de red
./adn/tools/run bkps orquestar T5 --on-error retry --retries 3

# Meta-orquestación: el orquestador mismo es un dron
./adn/tools/run dron lanzar --nota "Sync Nube Servidores" -- ./adn/tools/run bkps orquestar T5 --on-error retry

Implementado (F2.T1a, F2.T1b):

# Dry-run: ver archivos pendientes
./adn/tools/run bkps orquestar T1 --dry-run

# Real: 1 dron por .xva, secuencial, cada uno visible en Bitácora
./adn/tools/run bkps orquestar T2

# Meta-orquestación: el orquestador mismo es un dron
./adn/tools/run dron lanzar --nota "Orquestar PMOX" -- ./adn/tools/run bkps orquestar T2

Criterio de éxito: Flujos complejos se expresan como composición de drones simples. Cada procesador (xen, proxmox, rclone) soporta listar() + ejecutar_uno() para orquestación atómica.


Fase 3: Planificación — Drones Programados (Dron 2.0)

Objetivo: Ejecución automática según cron.

ID Tarea Tipo de Dron Estado
F3.T1 dron/planificador.rb Planificador — lee cron y lanza drones
F3.T2 Persistencia de schedules ~/.claude/scheduled_tasks.json
F3.T3 CLI schedules schedule add|list|remove
F3.T4 Integración CronCreate SDK de Claude Code

Criterio de éxito: Tareas recurrentes se lanzan automáticamente.


Fase 4: Inteligencia de Colmena — Comunicación entre Drones (Dron 3.0)

Objetivo: Drones se comunican y coordinan para resolver problemas.

ID Tarea Descripción Estado
F4.T1 Cola de eventos Redis o archivo compartido para eventos
F4.T2 Publicar/ Suscribirse Drones publican eventos, otros reaccionan
F4.T3 Patrones de reacción on failed → sanador, on completed → mensajero
F4.T4 Feromonas digitales Señales químicas virtuales (/tmp/dron_pheromones/)

Ejemplo de flujo emergente:

1. Ejecutor falla → publica "failed" en cola
2. Sanador escucha → re-intenta (máx 3 veces)
3. Si falla → publica "critical"
4. Mensajero escucha → notifica por Slack

Criterio de éxito: Soluciones complejas emergen sin orquestador central.


Fase 5: Observabilidad — Dashboard y Métricas (Dron 4.0)

Objetivo: Visibilidad completa de la colonia.

ID Tarea Tipo de Dron Estado
F5.T1 API /api/drones Endpoint para listar drones
F5.T2 API /api/drones/:id/avances Endpoint para avances de un dron
F5.T3 Dashboard web http://localhost:5174/drones
F5.T4 Gráficas Tareas por día, tasa de éxito, tiempos
F5.T5 Alertas proactivas dron_vigilante detecta anomalías

Criterio de éxito: Visibilidad completa vía web dashboard y métricas históricas.


🏗️ Arquitectura de Colmena

Diagrama de Flujo (Inteligencia de Colmena)

┌──────────────────────────────────────────────────────────────────┐
│  Dron::Base (módulo compartido - Atomisidad)                     │
│  - logger: Logger único                                          │
│  - db_query/db_exec: Acceso a DB                                 │
│  - utilidades: formato_duracion, slice_safe, blank?              │
└─────────────┬────────────────────────────────────────────────────┘
              │ extend Base (todos los drones)
    ┌─────────┼─────────┬─────────────────┐
    │         │         │                 │
    ▼         ▼         ▼                 ▼
┌────────┐ ┌────────┐ ┌────────┐  ┌──────────┐
│EJECUTOR│ │VIGILANTE│ │SANADOR│  │BITÁCORA  │
│🛠️     │ │👁️     │ │🩹     │  │📝        │
│lanzar  │ │flota  │ │sanear │  │iniciar   │
│        │ │salud  │ │limpiar│  │finalizar │
└───┬────┘ └───┬────┘ └───┬────┘  └────┬─────┘
    │          │         │             │
    │          │         │             │ registra
    │          │         │             ▼
    │          │         │      ┌─────────────┐
    │          │         │      │bitacoras.   │
    │          │         │      │events       │
    │          │         │      └─────────────┘
    │          │         │
    │          │         └─→ Re-intenta (máx 3, backoff)
    │          │
    │          └─→ Detecta zombies (heartbeat > 5min)
    │               Consolida en events
    │
    └─→ Ejecuta cmd
         Heartbeat cada 30s
         Registra en dron_logs + events

Principios de Diseño

Principio Aplicación
Single Responsibility Cada dron hace UNA cosa
Composability Drones se ensamblan en flujos
Event-Driven Comunicación vía cola de eventos
Stateless Drones no guardan estado (efímeros)
Observable Todo evento se registra en DB

🔧 Componentes Implementados

Base Compartido (adn/tools/cli/dron/base.rb) — Atomisidad

Componente Propósito
Base.logger Logger único para todos los drones
Base.db_query Ejecuta SQL y retorna resultados
Base.db_exec Ejecuta SQL sin retornar resultados
Base.formato_duracion Formatea segundos a "Xmin Ys"
Base.slice_safe Slice seguro de strings (evita nil)
Base.blank? Verifica si string es nil/vacío

Ejecutor (adn/tools/cli/dron/ejecutor.rb)

Componente Propósito
Ejecutor.lanzar Ejecuta comando, heartbeat, reporta resultado
Ejecutor#heartbeat Actualiza heartbeat cada 30s
Ejecutor#registrar_bitacora Crea/actualiza evento en bitácora

Vigilante (adn/tools/cli/dron/vigilante.rb)

Componente Propósito
Vigilante.escanear_flota Lee dron_logs, muestra resumen
Vigilante.salud Health check, detecta zombies y anomalías
Vigilante.dashboard Dashboard compacto de la flota
Vigilante.consolidar_flujo Consolidar estado de flujo en events

Sanador (adn/tools/cli/dron/sanador.rb)

Componente Propósito
Sanador.reintentar_fallidos Re-lanza failed (máx 3, backoff exponencial)
Sanador.limpiar_antiguos Elimina completados > N días
Sanador.sanear Saneamiento completo (reintentar + limpiar)

Bitácora (adn/tools/cli/dron/bitacora.rb)

Componente Propósito
Bitacora.iniciar Crea evento en bitácora
Bitacora.finalizar Actualiza evento con o
Bitacora.auto Modo AUTO: inicia, ejecuta, finaliza

Persistencia

Archivo/Tabla Tipo Propósito Visibilidad
bitacoras.dron_logs Tabla DB Logs detallados de cada dron 🔒 Interna
bitacoras.dron_avances Tabla DB Progreso paso-a-paso de flujos 🔒 Interna
bitacoras.dron_metricas Tabla DB Agregaciones y estadísticas 🔒 Interna
bitacoras.events Tabla DB Eventos consolidados (resumen) Bitácora Web
/tmp/dron_flota/*.json Archivo Estado de drones activos (runtime) 🔒 CLI
/tmp/dron_queue/*.json Archivo Cola de eventos (por tipo) 🔒 Drones
/tmp/dron_pheromones/ Directorio Señales químicas virtuales 🔒 Drones
~/.claude/scheduled_tasks.json Archivo Schedules persistentes (cron) 🔒 Planificador

Modificar

Archivo Cambio
adn/tools/cli/dron.rb Refactorizar → dispatcher que importa módulos
adn/tools/run Registrar subcomandos: dron lanzar, dron orquestar, etc.
adn/tools/db/core/bitacora_db.rb Hook para eventos AUTO (dron_bitacora)

📝 Comandos por Tipo de Dron

Ejecutor

# Lanzar tarea simple (auto-bitácora)
./adn/tools/run dron lanzar --evento AUTO --nota "Backup SQL" -- vzdump 103

# Lanzar sin registro (efímero)
./adn/tools/run dron lanzar --no-bitacora --nota "Test rápido" -- echo hola

# Lanzar con timeout
./adn/tools/run dron lanzar --timeout 300 --nota "Sync larga" -- rclone sync ...

Orquestador (Composición)

# Flujo secuencial (backup → sync → notificar)
./adn/tools/run dron orquestar \
  --step "dron lanzar --nota 'Backup' -- vzdump 103" \
  --step "dron lanzar --nota 'Sync' -- rclone sync /bkps oneDrive:" \
  --step "dron notificar --msg 'Backup completado'" \
  --on-error abort

# Flujo paralelo (múltiples backups simultáneos)
./adn/tools/run dron orquestar \
  --parallel \
  --step "dron lanzar --nota 'Backup SQL' -- vzdump 103" \
  --step "dron lanzar --nota 'Backup DC' -- vzdump 100" \
  --step "dron lanzar --nota 'Backup PCV' -- vzdump 104"

Planificador (Cron)

# Agendar backup nocturno
./adn/tools/run dron schedule add \
  --cron "0 2 * * *" \
  --cmd "dron lanzar --evento AUTO --nota 'Backup nocturno' -- ./adn/tools/run bkps run C4 --batch"

# Listar schedules
./adn/tools/run dron schedule list

# Eliminar schedule
./adn/tools/run dron schedule remove <id>

Vigilante (Monitoreo)

# Dashboard de flota
./adn/tools/run dron flota

# Estado detallado de un dron
./adn/tools/run dron estado <tarea_id>

# Métricas históricas
./adn/tools/run dron metricas [--desde 2026-04-01] [--hasta 2026-04-07]

Sanador (Reparación)

# Health check (detecta zombies)
./adn/tools/run dron salud

# Reintentar fallidos
./adn/tools/run dron sanear --reintentar

# Limpiar completados
./adn/tools/run dron limpiar --mayor-a 24h

Mensajero (Notificaciones)

# Notificar por Slack
./adn/tools/run dron notificar --slack "#backups" "Backup completado ✅"

# Notificar por email
./adn/tools/run dron notificar --email "admin@frlr.utn.edu.ar" "Alerta de fallo"

🎯 Criterios de Éxito

Atomicidad (Fase 1)

  • Cada dron tiene una única responsabilidad (Single Responsibility)
  • Sin código duplicado entre módulos
  • Tests independientes por tipo de dron

Composición (Fase 2)

  • Flujos multi-paso se expresan como composición de drones
  • Orquestador permite secuencial/paralelo/condicional

Inteligencia de Colmena (Fase 4)

  • Drones se comunican vía cola de eventos
  • Patrones de reacción emergen (failed → sanador → notificar)
  • Sin orquestador central para reacciones automáticas

Observabilidad (Fase 5)

  • Dashboard web muestra estado en tiempo real
  • Histórico consultable vía CLI y web
  • Alertas proactivas ante anomalías

📚 Referencias

Internas (ADN)

Externas (Inspiración)

  • Swarm Intelligence: Comportamiento emergente en colonias de insectos
  • Unix Philosophy: "Do one thing and do it well"
  • Microservices: Servicios pequeños, composables, independientes
  • Event-Driven Architecture: Comunicación asíncrona vía eventos
  • CronCreate: SDK de Claude Code para schedules persistentes

DB-First

  • Todas las métricas se almacenan en PostgreSQL (bitacoras.dron_logs)
  • Dashboard web consume API → DB

🔄 Armonía Integral

Última actualización: 2026-04-15 — Fase 1.5 completada + Heartbeat con output acumulado

Documento Estado Notas
A01_dtic-ADN.md 📍 Pendiente Agregar A01.P009 a tabla de planes
adn/07_proyectos.md 📍 Pendiente Referenciar nuevo plan
docs/contexto/IA.md 📍 Pendiente Actualizar sección de drones
adn/06_gobernanza.md Alineado Principio "Menos es Más" — AtomicDrones + Atomisidad

📦 Entregables

Fase 1 (Estructura atómica — con deuda técnica)

Archivo Líneas Descripción Estado
adn/tools/cli/dron/base.rb 66 Módulo compartido (atomisidad)
adn/tools/cli/dron/dispatcher.rb 284 Routing de subcomandos ⚠️ B1
adn/tools/cli/dron/ejecutor.rb 171 Ejecutor atómico ⚠️ B9, B11
adn/tools/cli/dron/vigilante.rb 151 Vigilante atómico ⚠️ B10
adn/tools/cli/dron/sanador.rb 85 Sanador atómico ⚠️ B6, B7, B8
adn/tools/cli/dron/bitacora.rb 76 Bitácora atómica
adn/tools/db/core/dron_db.rb 351 Acceso a datos DB ⚠️ B2-B5
adn/tools/db/migrations/003_create_dron_tables.rb 232 Migración DB

Fase 1.5 (Pendiente — Visibilidad + Corrección)

Entregables esperados:

  • Corrección de los 12 bugs detectados
  • Heartbeat visible en Bitácora Web
  • Eliminación del sistema dual .sh/Ruby
  • Flujo ejecutor→events→web funcional y probado