CreaRack-SL

Servicio: Watchdog de monitoreo post-swap del auto-update

Resumen

Módulo: terminal/agent/core/updater.py
Función pública: _launch_swap_watchdog(target: str) -> None
Versión agente: 2.13.3+ (funcional); 2.11.4–2.13.2 (presente pero inoperante hasta la corrección)

El watchdog es un proceso hijo desacoplado que monitorea la integridad de un auto-update del Agente Local post-swap. Se lanza cuando el updater reemplaza el binario del Agente y está a punto de hacer os._exit(0) para ceder control a la nueva versión.

Ciclo de vida:

  1. Lanzamiento: El updater ejecuta _launch_swap_watchdog(target_version) con flags que permiten sobrevivir al padre.
  2. Monitoreo: El watchdog espera 30 segundos (DEFAULT_SWAP_WATCHDOG_DELAY) y realiza healthcheck (GET /health en localhost:9999).
  3. Acción correctiva:
    • ✅ Healthcheck OK → Versión nueva viva, misión cumplida.
    • ❌ Healthcheck falla → Versión nueva muerta (AV bloqueó arranque, corrupción, etc.).
      • Intenta relanzar: os.execv(new_exe, [new_exe, '--swap-retry', ...])
      • Si sigue fallando → revierte al binario anterior.

Firma de la función

def _launch_swap_watchdog(target: str) -> None:
    """Lanza un script PowerShell desacoplado que monitorea el swap post-actualización.
    
    Args:
        target: Versión destino del Agente (ej. "2.13.3"), usada en traza y naming.
    
    Comportamiento:
    - Genera un script .ps1 temporal con la lógica de healthcheck.
    - Lo ejecuta con subprocess.Popen() con flags que permiten sobrevivir al padre.
    - Retorna inmediatamente (el proceso hijo corre de fondo).
    - Registra el PID en update_watchdog.log para auditoría.
    
    Excepciones:
    - FileNotFoundError: Si powershell.exe no está disponible.
    - OSError: Error al escribir el script .ps1 o al ejecutar Popen.
    """

Componentes internos

1. Generación del script PowerShell (ps1)

Ubicación: Archivo temporal bajo AGENT_LOG_DIR (típicamente C:\Users\<user>\AppData\Roaming\CreaRack Pro\agent\).

Contenido del script:

  • Escritura de log de arranque: "[{timestamp}] Watchdog iniciado, versión destino: {target}"
  • Espera (delay): Start-Sleep -Seconds $delay (default 30 segundos).
  • Healthcheck: $response = Invoke-WebRequest -Uri "http://localhost:9999/health" -TimeoutSec 5 -ErrorAction Stop
  • Lógica de acción correctiva:
    • Si healthcheck OK → log de éxito y salida.
    • Si falla → log de error y reintentos (ejecutar agent.exe --swap-retry).

Por qué PowerShell:

  • Disponible por defecto en Windows moderno (>= Vista).
  • Soporte nativo de HTTP (cmdlet Invoke-WebRequest).
  • Decoupling del Agente Python (proceso independiente, lenguaje diferente).

2. Lanzamiento con subprocess.Popen()

Flags de creación (a partir de 2.13.3):

creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | getattr(subprocess, "CREATE_NO_WINDOW", 0),
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
close_fds=True,

Por qué estos flags:

  • CREATE_NEW_PROCESS_GROUP: Crea un grupo de proceso independiente, desacoplado del padre.
  • CREATE_NO_WINDOW: Abre una consola privada pero la oculta (no molesta al usuario).
  • DEVNULL en los 3 handles: PowerShell obtiene I/O válidos; no muere al inicializar.
  • close_fds=True: Cierra file descriptors heredados (mejor aislamiento).

Sin DETACHED_PROCESS: Aunque parezca contradictorio, no es necesario; CREATE_NEW_PROCESS_GROUP ya desacopla. Usarlo causaba conflicto semántico y mataba el proceso silenciosamente (incidentes 03-07 y 05-07).

3. Traza en update_watchdog.log

Ubicación: AGENT_LOG_DIR / "update_watchdog.log"

Registros que deja el updater (antes de lanzar):

[2026-07-05 12:56:00] Swap a v2.13.3: watchdog lanzado (pid 5432, powershell)

Registros que escribe el watchdog (desde el script .ps1):

[2026-07-05 12:56:00] Watchdog iniciado, versión destino: 2.13.3
[2026-07-05 12:56:30] Healthcheck OK, Agente v2.13.3 en ejecución
[2026-07-05 12:56:30] Misión cumplida, saliendo.

O en caso de fallo:

[2026-07-05 12:56:00] Watchdog iniciado, versión destino: 2.13.3
[2026-07-05 12:56:30] Healthcheck FALLÓ (AV bloqueó el arranque?), reintentando...
[2026-07-05 12:56:35] Reintento 1: ejecutando agent.exe --swap-retry
[2026-07-05 12:56:45] Healthcheck OK, Agente v2.13.3 vivo

O revertiendo:

[2026-07-05 12:56:00] Watchdog iniciado, versión destino: 2.13.3
[2026-07-05 12:56:30] Healthcheck FALLÓ
[2026-07-05 12:56:35] Reintento 1: FALLÓ
[2026-07-05 12:56:41] Reintento 2: FALLÓ (archivos corruptos?), revirtiendo a v2.13.2
[2026-07-05 12:56:45] Swap revertido, Agente v2.13.2 relanzado

Dependencias

DependenciaFuenteUso
subprocess (stdlib)Python builtinLanzamiento de PowerShell
pathlib.Path (stdlib)Python builtinUbicación de script .ps1, binary, etc.
logging (stdlib)Python builtinEscritura en update_watchdog.log
PowerShell.exeWindows system (C:\Windows\System32)Intérprete del script
localhost:9999/healthAgent HTTP APIHealthcheck (endpoint de monitoreo)

Historial de cambios

VersiónEstadoNota
2.11.4✅ IntroducciónPrimer watchdog (agent-robustez F2).
2.11.5–2.13.2❌ InoperanteFlags incorrectos (DETACHED_PROCESS) mataban el script silenciosamente.
2.13.1📊 Traza añadidaSe introdujo update_watchdog.log (evidencia del problema).
2.13.3✅ CorregidoEliminado DETACHED_PROCESS, añadido DEVNULL en handles.

Casos de uso

Caso 1: AV bloquea el arranque de la versión nueva

  1. Updater lanza swap (reemplaza binary, hace os._exit(0)).
  2. Watchdog sobrevive, espera 30s.
  3. Versión nueva (2.13.3) arranca pero AV mata agent.exe al extraer el onefile (sin reputación).
  4. Healthcheck falla → watchdog ejecuta agent.exe --swap-retry.
  5. AV deja pasar el reintento (historial) → Agente vivo.

Caso 2: Binario corrupto

  1. Updater descarga versión nueva, swap aplicado.
  2. Versión nueva arranca pero falla al inicializar módulos (import error).
  3. Healthcheck falla repetidamente.
  4. Tras N reintentos → watchdog revierte al binario anterior.
  5. Usuario ve: Agente sigue vivo en v2.13.2 (sin experiencia degradada).

Caso 3: Actualización exitosa

  1. Updater lanza swap correctamente.
  2. Versión nueva (2.13.3) arranca sin problemas.
  3. Healthcheck OK a los 30s.
  4. Watchdog registra éxito y termina.

Restricciones y limitaciones

Windows-only

  • Usa flags específicos de Windows (CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW).
  • El script está en PowerShell (no disponible en Linux/Mac del Agente, pero el Agente is Windows-only by design).

No hay timeout global en Python

  • Una vez lanzado, el watchdog no se monitorea desde el proceso padre (ya murió).
  • Si el watchdog se cuelga indefinidamente, no hay mecánica de supervisión externa (el proceso simplemente permanece vivo).
  • Mitigación: El script tiene timeouts locales en cada Invoke-WebRequest (5 segundos).

Caso de fallo silencioso (hasta 2.13.2)

  • Si el lanzamiento mismo falla (p.ej. PowerShell no disponible), la excepción en Python se ignora (try/except OSError).
  • El Agente sigue adelante con os._exit(0), dejando la versión nueva posiblemente muerta sin vigilancia.

Véase también

  • [[incident—20260705—watchdog-auto-update-mudo]]
  • [[feature—terminal—watchdog-recovery-fix-v2-13-3]]
  • [[decision—20260705—flags-createprocess-windows-watchdog]]
  • [[concept—infra—auto-update-resilience]]