TestLoadConversationAbsenceLegitimeResteRapide vérifie qu'une absence
légitime — première installation, rien d'enregistré — ne déclenche aucune
reprise. Il le mesurait au chronomètre : « moins de 250 ms », la durée
d'une attente de reprise.
Le temps de mur ne dit pas ça. Il mêle le coût du PREMIER essai (créer et
ouvrir la base bbolt, fsync compris) à l'attente qu'on cherche à exclure.
Sur un runner CI chargé, cet essai unique a pris 435 ms : le test a échoué
alors qu'aucune reprise n'avait eu lieu, et le même commit passait une
minute plus tôt sur un autre runner.
LoadConversation compte désormais ses reprises (loadConvRetries), et le
test lit ce compteur. Ce qu'il affirme est enfin ce qu'il mesure, et il ne
dépend plus de la charge de la machine.
Le test de la base illisible vérifie le compteur dans l'autre sens (quatre
reprises attendues) : sans ça, un compteur bloqué à zéro rendrait la
première assertion vraie sans rien prouver.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129sffVC43rezAXUMQuzUog
- Au démarrage, une lecture ratée de la base (verrou bbolt transitoire pendant
le chevauchement des process au redémarrage du conteneur) était confondue
avec une conversation absente. Pire que l'amont chez nous : getStr rend ""
dans les deux cas, donc convEnsureActive forgeait un NOUVEL identifiant et
l'écrasait — le fil en cours devenait orphelin, en silence. On sonde
désormais la base avec son erreur AVANT toute écriture, on réessaie quatre
fois, et un échec durable est journalisé sans que rien ne soit touché.
- Une capture d'écran disparaissait sans laisser de trace : stripImageParts
aplatissait le message en ne gardant que sa légende. Le marqueur
imageLostMarker rend la perte VISIBLE, pour que le modèle sache reprendre
une capture au lieu de la redécrire de mémoire.
- maxLogEvents 20000 → 200000 : un seul tour à très long raisonnement
tronquait déjà le journal de rejeu, et l'utilisateur perdait le début de sa
conversation à l'écran.
- Keepalive WebSocket des postes distants (ping toutes les 25 s, des DEUX
côtés). Sans lui, un poste au repos était coupé au bout de ~60-100 s par les
intermédiaires qui ferment les canaux inactifs, puis reconnecté après
backoff — les déconnexions à répétition sur tous les postes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>