# Post-mortem — la première touche frappée coupait la console de l'élève - **Date** : 2026-10-08 (défaut introduit à l'étape 9, découvert à l'étape 11) - **Durée** : défaut latent depuis l'étape 9 ; corrigé dans l'heure suivant sa découverte - **Gravité** : élevée si elle avait atteint une classe — aucun élève n'aurait pu taper une seule commande dans un vrai navigateur. Impact réel nul : aucun utilisateur. - **Impact** : avec xterm.js, la première frappe de l'élève faisait fermer la connexion à la VM ; la console restait affichée mais muette. ## Résumé `nova-serialproxy` (sous-protocole WebSocket `binary`) ferme sa connexion dès qu'il reçoit une trame **texte**. Or xterm.js, dans le navigateur, envoie les frappes en trames texte (`ws.send(chaîne)`), et le proxy Go les relayait vers l'amont avec leur type d'origine. Le test de l'étape 9 (`proxy-console/test-manuel.py`) envoyait des trames **binaires** (`send_binary`) : il passait, et le critère « une élève tape `ip a` et voit le résultat » a été déclaré vérifié sans qu'aucun vrai navigateur n'ait tapé une touche. ## Chronologie 1. Étape 9 : proxy écrit et testé avec un script qui envoie du binaire. Critère validé. 2. Étape 11 : le script de parcours (`portail/tests/Reels/parcours-lab.py`) imite xterm.js et envoie `"\r"` en trame texte pour réveiller la console : aucune invite `login:`. 3. Journaux : `proxy-console` → « connexion amont fermée » 2 s après l'ouverture, à l'instant de la trame texte ; `nova-serialproxy` ne dit rien de plus. 4. Diagnostic par comparaison directe (`/tmp/diag-trames.py`) : trame texte → rien puis connexion perdue. 5. Correctif : `proxy-console/hub.go`, les frappes partent toujours en `websocket.BinaryMessage` vers l'amont. Redéployé ; texte et binaire font apparaître `login:`. ## Cause racine Le test de l'étape 9 ne reproduisait pas le vrai client : il prenait un raccourci (binaire) que le navigateur ne prend pas. La différence entre « le chemin fonctionne » et « le chemin fonctionne avec le vrai client » n'était pas testée. ## Ce qui a bien fonctionné - Écrire le test de l'étape 11 comme un vrai navigateur (connexion, CSRF, jeton de console, trames texte) a fait remonter le défaut avant tout pilote. - Les journaux du proxy, horodatés à la seconde, ont permis de relier la fermeture à la trame en quelques minutes. ## Actions correctives - [x] Le proxy normalise : toute frappe part en binaire vers `nova-serialproxy`, quel que soit le client. - [x] `parcours-lab.py` envoie des trames texte, comme xterm.js : il protège désormais contre ce défaut. - [ ] Étape 12 : vérifier une fois le parcours dans un vrai navigateur (Playwright), la vérification visuelle de l'étape 8 n'ayant pas pu être faite. ## Comprendre Un test qui passe ne prouve que ce qu'il teste vraiment. Le script de l'étape 9 parlait au proxy « comme un outil », pas « comme un navigateur » : il envoyait des octets bruts, alors qu'un navigateur envoie du texte. Les deux arrivent au même proxy, mais `nova-serialproxy` les traite différemment. La leçon générale : pour valider un critère utilisateur, le test doit imiter le vrai client jusque dans les détails qui paraissent anodins — ici, le type de trame WebSocket.