10f0614fc0
El launcher salia con status=0 cuando todos los runners (Agent/Robot) terminaban su Run() de forma natural — por ejemplo tras una rotacion de token de Matrix o un drop del sync. systemd, configurado con Restart=on-failure, no relanzaba el proceso al ver salida limpia y los bots quedaban caidos hasta una intervencion manual. Solucion: nueva rutina superviseUntilCanceled en agentRegistry que bloquea sobre waitAll, y si el ctx padre sigue vivo, espera un backoff y llama reloadAll para recrear los runners. Solo cuando el ctx padre se cancela (SIGINT/SIGTERM) la rutina retorna y el launcher sale. main.go pasa a invocar este supervisor en lugar de waitAll directo. Tests: - TestSuperviseUntilCanceled_ReturnsWhenCtxCanceledFirst — empty registry - TestSuperviseUntilCanceled_ReturnsAfterCtxCancelDuringBackoff — cancel durante el backoff debe desbloquear inmediatamente - TestSuperviseUntilCanceled_CallsReloadOnAgentExit — supervisor sigue vivo todo el deadline aunque reload falle por cfgPath invalido Diagnostico: tras varias horas el journalctl mostraba "Deactivated successfully" sin "Stopping" previo (Apr 13 18:22 tras 23h corriendo) y el log del agent registraba "context canceled" tras "starting matrix sync" — sintoma de que mautrix.SyncWithContext salio limpiamente y el ctx.cancel se propago al cerrar la goroutine sin que systemd hubiera enviado SIGTERM. El bucle supervisado lo arregla recreando los runners sin tocar la unit ni depender del Restart de systemd.