Retour à la liste

Blog

Comment nous gérons les interruptions réseau avec élégance

Les réseaux tombent en panne constamment. Voici comment Tactic Remote garantit que vous ne perdez jamais votre travail ni votre contexte quand cela arrive.

Publié le Tags: engineering / reliability / networking
Blog

Suivez les mises à jour produit et ingénierie de cette catégorie.

Voir la catégorie

Si vous avez déjà utilisé un outil distant qui affiche un spinner après une brève coupure réseau et ne se rétablit jamais, vous comprenez pourquoi nous sommes obsédés par la fiabilité de la reconnexion. Dans le monde des outils de développement mobile, les interruptions réseau ne sont pas des cas limites — c'est le cas courant. Les téléphones basculent entre Wi-Fi et cellulaire. Les ascenseurs bloquent les signaux. Les réseaux de cafés sont peu fiables par nature.

Le moteur de reconnexion de Tactic Remote est conçu autour d'un principe : les interruptions réseau doivent être invisibles pour l'utilisateur autant que possible, et communiquées avec élégance quand elles ne peuvent pas l'être.

La machine à états

Au cœur de notre logique de reconnexion se trouve une machine à états finie avec six états :

┌──────────┐     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 : Fonctionnement normal. Les données circulent librement.

UNSTABLE : Une tentative de connexion a échoué, mais nous suspectons que c'est transitoire. Les tentatives se font à des intervalles de 1-3 secondes. L'UI affiche un indicateur d'avertissement discret mais n'interrompt pas l'utilisateur.

RECONNECTING : Des échecs multiples suggèrent un vrai problème. Les intervalles de nouvelle tentative augmentent exponentiellement (5s, 10s, 20s, plafond 30s). L'UI affiche une bannière claire « reconnexion en cours ».

RECOVERING : La connexion est rétablie, mais nous devons synchroniser l'état. Le client demande un delta d'état au serveur, l'applique et passe à CONNECTED une fois la réconciliation terminée.

DISCONNECTED : Le timeout de nouvelle tentative (par défaut : 5 minutes) a été dépassé. L'utilisateur doit déclencher manuellement la reconnexion. Cela empêche la consommation infinie de batterie sur les appareils véritablement hors ligne.

Le protocole de réconciliation

La reconnexion est facile. La réconciliation — remettre le client et le serveur en synchronisation — est la partie difficile.

Lorsque l'iPhone se reconnecte après une interruption, il envoie au serveur son dernier vecteur d'état connu : le numéro de séquence de la dernière mise à jour du terminal traitée, l'ID de session et un horodatage. Le serveur utilise cela pour calculer exactement ce que le client a manqué.

Scénario 1 : Interruption courte (< 30 secondes). Le buffer de sortie du serveur contient encore toutes les mises à jour manquées. Il les rejoue dans l'ordre et le client les applique séquentiellement. Du point de vue de l'utilisateur, le terminal « rattrape » en moins d'une seconde — un bref défilement rapide pendant que la sortie manquée apparaît.

Scénario 2 : Interruption moyenne (30 secondes - 5 minutes). Le buffer de sortie a peut-être partiellement tourné. Le serveur envoie un rejeu partiel suivi d'un instantané complet du terminal. Le client applique le rejeu, puis valide contre l'instantané pour assurer la cohérence.

Scénario 3 : Interruption longue (> 5 minutes). Le serveur n'envoie qu'un instantané complet. Le client remplace l'intégralité de son état. C'est le chemin le moins efficace mais il garantit l'exactitude quel que soit ce qui a été manqué.

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

Notez que la réconciliation inclut aussi pendingApprovals. Si Claude Code a demandé une approbation pendant que le téléphone était hors ligne, cette demande est livrée dans le cadre de la reconnexion. C'est crucial — sans cela, Claude Code resterait bloqué en attente d'une approbation que l'utilisateur n'a jamais vue.

Les cas limites qui ont failli nous briser

La déconnexion en pleine approbation

Un utilisateur reçoit une notification d'approbation, appuie sur « Approuver » et perd la connectivité avant que l'approbation n'atteigne le serveur. L'action s'exécute-t-elle ?

Notre solution : les approbations utilisent un protocole d'accusé de réception. Le serveur n'exécute pas l'action approuvée tant qu'il n'a pas reçu le message d'approbation du client ET confirmé la réception au client. Si la connexion tombe pendant l'approbation, l'approbation est retransmise à la reconnexion. Les approbations en double sont idempotentes — envoyer « approuver demande #47 » deux fois a le même effet qu'une seule fois.

Le cas de la session terminée pendant l'absence

Votre tâche Claude Code se termine pendant que votre téléphone est hors ligne. Quand vous vous reconnectez, la session est dans un état terminé. Le client doit-il afficher la sortie finale ? La notification de complétion ? Les deux ?

Nous gérons cela en incluant un sessionEndState dans la réponse de réconciliation. Le client affiche l'état final du terminal et montre une bannière « Tâche terminée pendant l'absence » avec un résumé de ce qui s'est passé. L'utilisateur voit le résultat immédiatement plutôt que de fixer une sortie en cours obsolète.

Le cas du Mac redémarré

Le scénario le plus difficile : votre Mac redémarre pendant que le téléphone est hors ligne (suite à une mise à jour système, une coupure de courant ou un crash). Quand le téléphone se reconnecte, le serveur n'a aucune mémoire des sessions précédentes.

Notre solution : l'application compagnon Mac persiste les métadonnées de session (IDs de session, chemins de projet, derniers états connus) sur le disque. Au redémarrage, elle lit ces métadonnées et, si possible, se rattache aux sessions tmux survivantes. Le protocole de réconciliation détecte le redémarrage du serveur via un compteur d'epoch serveur et initie une reconstruction complète de l'état plutôt qu'une réconciliation incrémentale.

Caractéristiques de performance

Nous mesurons la performance de reconnexion selon trois métriques :

Temps de récupération visuelle — le temps entre la restauration du réseau et le moment où l'utilisateur voit l'état actuel du terminal. Notre P95 est de 1,8 secondes pour le réseau local et 3,2 secondes pour Cloudflare Tunnel.

Efficacité des données — la quantité de données transférées pendant la réconciliation par rapport à un rafraîchissement complet. Pour les interruptions de moins de 60 secondes, notre protocole de réconciliation transfère 85% moins de données qu'un rafraîchissement complet.

Exactitude — tolérance zéro pour les incohérences d'état. Nous exécutons des tests d'intégration continue qui simulent des milliers de cycles de déconnexion/reconnexion avec un timing aléatoire et vérifient que l'état final du client correspond octet par octet à l'état du serveur.

Leçons pour les développeurs

Si vous construisez tout type d'application mobile en temps réel, notre expérience suggère trois principes :

  1. Concevez pour la déconnexion dès le premier jour. Ajouter la reconnexion à un système conçu pour une connectivité permanente est des ordres de grandeur plus difficile que de construire avec la connectivité intermittente comme préoccupation de première classe.

  2. L'idempotence est votre meilleure alliée. Chaque message client-serveur devrait pouvoir être rejoué en toute sécurité. Cette seule propriété élimine des catégories entières de bugs de reconnexion.

  3. Testez avec de vrais réseaux, pas des simulateurs. Les simulateurs de conditions réseau ne peuvent pas reproduire toute la diversité des pannes du monde réel — livraison partielle de paquets, délais de résolution DNS, timeouts de handshake TLS. Nous testons sur de vrais réseaux cellulaires, dans des ascenseurs et sur du Wi-Fi de conférence encombré.

La reconnexion fiable n'est pas un travail glamour. Les utilisateurs ne la louent pas quand elle fonctionne — ils ne remarquent tout simplement pas les interruptions. Mais quand elle échoue, la confiance s'évapore instantanément. Nous pensons que l'investissement en vaut la peine, et la volonté de nos utilisateurs de s'appuyer sur Tactic Remote pour des workflows de production suggère qu'ils sont d'accord.

Essayez Tactic Remote

Pilotez vos agents de programmation depuis votre téléphone

Connectez-vous à Claude Code, Codex et aux autres agents sur votre Mac, PC Windows ou Linux. Consultez leur progression et envoyez la prochaine instruction depuis votre iPhone ou iPad.