Deux défauts vécus sur le poste, tous deux « invisibles » jusqu'à ce qu'on regarde.
1. Une console Claude ouverte depuis l'app affichait « Transcript saving is off,
inherited CLAUDE_CODE_CHILD_SESSION marker ». Le daemon avait été lancé depuis
une session Claude Code, il héritait donc de ses marqueurs d'exécution et les
repassait à CHAQUE session qu'il lance. Le CLI se croyait sous-session et
coupait la sauvegarde de son transcript : plus d'historique, plus de --resume,
claudeSessionId restant null (et avec lui l'état fin busy/waiting/idle, ce qui
explique les sessions sans activité détectée). L'environnement des PTY est
desormais assaini de ces marqueurs, pour `claude` comme pour les shells (un
`claude` tapé à la main en héritait aussi). La configuration légitime de
l'utilisateur (CLAUDE_CONFIG_DIR, ANTHROPIC_*, proxies) passe intacte.
Vérifié par acceptance-p17 : le daemon de test est lancé avec un environnement
volontairement pollué, et le PTY n'en voit plus rien.
2. Une mise à jour installée à chaud demandait encore une manipulation. La 0.2.4
détectait le remplacement du binaire et proposait un dialogue « Restart now » :
le travail restait à la charge de l'utilisateur. L'app redémarre maintenant
d'elle-même quand cela ne coûte rien, c'est-à-dire le cas courant, et ne
demande que s'il y a quelque chose à perdre : des sessions vivantes à
interrompre (le dialogue dit combien) ou un daemon injoignable. Un « Later »
reste définitif pour cette version : rien ne redémarre dans le dos de
personne. La détection ne dépend plus d'un retour par le tray ou le Dock : un
`stat` toutes les 30 s la couvre même fenêtre ouverte, par poll et non par
`fs.watch`, qui ne voit souvent rien quand un paquet remplace un binaire ou
tout un répertoire.
Installer une nouvelle version remplace les fichiers sur disque mais ne touche
pas le process en cours : l'ancienne instance gardait le port 7317, la version
fraîchement installée mourait sur EADDRINUSE avant son handshake, et le shell se
contentait d'un console.error suivi d'un app.quit(). Depuis le lanceur, cliquer
l'icône ne produisait donc rien du tout.
- Tout échec de démarrage ouvre un dialogue Retry / Show log / Quit
(start-failure.ts, texte pur et testé) et la sortie du daemon est conservée
dans <userData>/logs/daemon.log. Une mort du daemon APRÈS le handshake propose
de le relancer, au lieu de laisser une fenêtre morte à l'écran.
- Le port est diagnostiqué avant le spawn (port-guard.ts, empreinte
{pid, ownerPid, port}) : un daemon orphelin, dont l'Electron est mort, est
repris (SIGTERM puis SIGKILL, en attendant un bind réellement possible) ;
une instance vivante ou un tiers (service, npx) est annoncé avec l'action qui
débloque, et jamais tué. La reprise exige deux preuves, l'empreinte orpheline
ET l'identité du process (ps -ww), car un pidfile périmé peut désigner un pid
recyclé entre-temps par un programme quelconque.
- Une mise à jour installée à chaud est signalée avec « Restart now »
(upgrade-watch.ts), qui arrête le daemon avant app.relaunch() ; sans quoi le
lock d'instance unique renvoyait silencieusement sur la fenêtre de l'ancienne
version, et on croyait avoir migré.
- ARBORETUM_DESKTOP_PORT pour cohabiter avec un Arboretum qui occupe 7317 en
permanence (service installé, ou daemon lancé en terminal).
24 tests dans packages/desktop/test, et quatre scénarios rejoués en dev sous
xvfb-run avec profil isolé : port tenu par un tiers, orphelin repris puis SPA
servie, instance vivante laissée intacte, pid recyclé épargné.