Volver a la lista

Blog

Cómo manejamos las interrupciones de red con elegancia

Las redes fallan constantemente. Así es como Tactic Remote garantiza que nunca pierdas trabajo ni contexto cuando esto sucede.

Publicado Etiquetas: engineering / reliability / networking
Blog

Sigue las novedades de producto e ingeniería de esta categoría.

Ver categoría

Si alguna vez usaste una herramienta remota que muestra un spinner después de un breve corte de red y nunca se recupera, entiendes por qué nos obsesionamos con la confiabilidad de la reconexión. En el mundo de las herramientas de desarrollo móvil, las interrupciones de red no son casos extremos — son el caso común. Los teléfonos alternan entre Wi-Fi y red celular. Los ascensores bloquean señales. Las redes de cafeterías son poco confiables por naturaleza.

El motor de reconexión de Tactic Remote está diseñado alrededor de un principio: las interrupciones de red deben ser invisibles para el usuario siempre que sea posible, y comunicadas con elegancia cuando no puedan serlo.

La máquina de estados

En el corazón de nuestra lógica de reconexión hay una máquina de estados finita con seis estados:

┌──────────┐     connect     ┌───────────┐
│ INITIAL  │───────────────►│ CONNECTED │◄─────────────┐
└──────────┘                 └─────┬─────┘              │
                                   │                    │
                              disconnect           reconnect
                                   │              success │
                              ┌────▼─────┐              │
                              │ UNSTABLE │──── retry ───►│
                              └────┬─────┘    (1-3s)    │
                                   │                    │
                              3 failures           ┌────┴─────┐
                                   │               │RECOVERING│
                              ┌────▼─────┐        └────┬─────┘
                              │RECONNECT │── retry ────►│
                              │  ING     │  (5-30s)     │
                              └────┬─────┘              │
                                   │                    │
                              timeout (5min)            │
                                   │                    │
                              ┌────▼─────┐              │
                              │DISCONNEC │─── manual ───┘
                              │  TED     │   reconnect
                              └──────────┘

CONNECTED: Operación normal. Los datos fluyen libremente.

UNSTABLE: Un intento de conexión falló, pero sospechamos que es transitorio. Los reintentos ocurren a intervalos de 1-3 segundos. La UI muestra un indicador de advertencia sutil pero no interrumpe al usuario.

RECONNECTING: Múltiples fallos sugieren un problema real. Los intervalos de reintento aumentan exponencialmente (5s, 10s, 20s, tope de 30s). La UI muestra un banner claro de "reconectando".

RECOVERING: La conexión se restableció, pero necesitamos sincronizar el estado. El cliente solicita un delta de estado al servidor, lo aplica y transiciona a CONNECTED una vez que la conciliación se completa.

DISCONNECTED: Se excedió el timeout de reintentos (por defecto: 5 minutos). El usuario debe activar la reconexión manualmente. Esto previene el consumo infinito de batería en dispositivos realmente desconectados.

El protocolo de conciliación

La reconexión es fácil. La conciliación — lograr que cliente y servidor vuelvan a estar sincronizados — es la parte difícil.

Cuando el iPhone se reconecta después de una interrupción, envía al servidor su último vector de estado conocido: el número de secuencia de la última actualización de terminal procesada, el ID de sesión y una marca de tiempo. El servidor usa esto para calcular exactamente lo que el cliente se perdió.

Escenario 1: Interrupción corta (< 30 segundos). El buffer de salida del servidor aún contiene todas las actualizaciones perdidas. Las reproduce en orden y el cliente las aplica secuencialmente. Desde la perspectiva del usuario, la terminal "se pone al día" en menos de un segundo — ven una ráfaga breve de desplazamiento mientras aparece la salida perdida.

Escenario 2: Interrupción media (30 segundos - 5 minutos). El buffer de salida puede haber rotado parcialmente. El servidor envía una reproducción parcial seguida de una instantánea completa de la terminal. El cliente aplica la reproducción y luego valida contra la instantánea para asegurar consistencia.

Escenario 3: Interrupción larga (> 5 minutos). El servidor envía solo una instantánea completa. El cliente reemplaza todo su estado. Este es el camino menos eficiente pero garantiza corrección sin importar cuánto se haya perdido.

type ReconciliationResponse = {
  method: 'replay' | 'replay_and_snapshot' | 'snapshot'
  replay?: TerminalUpdate[]
  snapshot?: TerminalSnapshot
  sessions: SessionState[]
  pendingApprovals: ApprovalRequest[]
}

Noten que la conciliación también incluye pendingApprovals. Si Claude Code solicitó aprobación mientras el teléfono estaba desconectado, esa solicitud se entrega como parte de la reconexión. Esto es crítico — sin esto, Claude Code quedaría atascado esperando una aprobación que el usuario nunca vio.

Casos extremos que casi nos quiebran

La desconexión a mitad de aprobación

Un usuario recibe una notificación de aprobación, toca "Aprobar" y pierde conectividad antes de que la aprobación llegue al servidor. ¿Se ejecuta la acción?

Nuestra solución: las aprobaciones usan un protocolo de confirmación. El servidor no ejecuta la acción aprobada hasta que recibe el mensaje de aprobación del cliente Y confirma la recepción de vuelta al cliente. Si la conexión se cae a mitad de la aprobación, la aprobación se retransmite en la reconexión. Las aprobaciones duplicadas son idempotentes — enviar "aprobar solicitud #47" dos veces tiene el mismo efecto que enviarla una vez.

El caso de sesión terminada mientras estaba desconectado

Tu tarea de Claude Code se completa mientras tu teléfono está desconectado. Cuando te reconectas, la sesión está en estado terminado. ¿Debería el cliente mostrar la salida final? ¿La notificación de completado? ¿Ambas?

Lo manejamos incluyendo un sessionEndState en la respuesta de conciliación. El cliente renderiza el estado final de la terminal y muestra un banner "Tarea completada mientras estabas desconectado" con un resumen de lo que ocurrió. El usuario ve el resultado inmediatamente en lugar de quedarse mirando una salida obsoleta en progreso.

El caso de Mac reiniciado

El escenario más desafiante: tu Mac se reinicia mientras el teléfono está desconectado (por una actualización del sistema, falla de energía o crash). Cuando el teléfono se reconecta, el servidor no tiene memoria de las sesiones anteriores.

Nuestra solución: la app compañera del Mac persiste los metadatos de sesión (IDs de sesión, rutas de proyecto, últimos estados conocidos) en disco. Al reiniciar, lee estos metadatos y, donde es posible, se reconecta a las sesiones tmux sobrevivientes. El protocolo de conciliación detecta el reinicio del servidor mediante un contador de epoch del servidor e inicia una reconstrucción completa del estado en lugar de intentar una conciliación incremental.

Características de rendimiento

Medimos el rendimiento de reconexión con tres métricas:

Tiempo de recuperación visual — cuánto tiempo desde la restauración de la red hasta que el usuario ve el estado actual de la terminal. Nuestro P95 es de 1.8 segundos para red local y 3.2 segundos para Cloudflare Tunnel.

Eficiencia de datos — cuántos datos se transfieren durante la conciliación versus una actualización completa. Para interrupciones de menos de 60 segundos, nuestro protocolo de conciliación transfiere 85% menos datos que una actualización completa.

Corrección — tolerancia cero para inconsistencias de estado. Ejecutamos pruebas de integración continua que simulan miles de ciclos de desconexión/reconexión con tiempos aleatorios y verificamos que el estado final del cliente coincida byte a byte con el estado del servidor.

Lecciones para desarrolladores

Si estás construyendo cualquier tipo de aplicación móvil en tiempo real, nuestra experiencia sugiere tres principios:

  1. Diseña para la desconexión desde el día uno. Agregar reconexión a un sistema diseñado para conectividad permanente es órdenes de magnitud más difícil que construir con la conectividad intermitente como preocupación de primera clase.

  2. La idempotencia es tu mejor amiga. Cada mensaje de cliente a servidor debería ser seguro de reproducir. Esta única propiedad elimina categorías enteras de bugs de reconexión.

  3. Prueba con redes reales, no simuladores. Los simuladores de condiciones de red no pueden reproducir toda la diversidad de fallas del mundo real — entrega parcial de paquetes, retrasos en la resolución DNS, timeouts de handshake TLS. Nosotros probamos en redes celulares reales, en ascensores y en Wi-Fi congestionado de conferencias.

La reconexión confiable no es un trabajo glamoroso. Los usuarios no la elogian cuando funciona — simplemente no notan las interrupciones. Pero cuando falla, la confianza se evapora instantáneamente. Creemos que la inversión vale la pena, y la disposición de nuestros usuarios a confiar en Tactic Remote para flujos de trabajo de producción sugiere que están de acuerdo.

Prueba Tactic Remote

Controla tus agentes de programación desde el móvil

Conéctate a Claude Code, Codex y otros agentes en tu Mac, Windows o Linux. Consulta el progreso y envía la siguiente instrucción desde el iPhone o iPad.