Pourquoi 90 Go de votre disque ont disparu : l'état de dépendances laissé derrière par les worktrees supprimés


Votre ordinateur portable indique que le disque est presque plein. Vous videz le dossier des téléchargements, supprimez quelques images Docker, redémarrez, et deux semaines plus tard le même message revient. Le vrai coupable est un répertoire que vous n’avez probablement jamais ouvert : ~/.hermes/installs. Il n’apparaît jamais comme un gros processus et ne se montre jamais dans du -sh ~/Downloads — il grandit simplement en silence.

La raison est simple et ce n’est pas un bug. Chaque fois que vous démarrez l’agent dans un worktree hermes -w, un arbre de revue ou un clone jetable, Hermes prépare un environnement de dépendances dédié pour ce checkout — un environnement Python, un environnement de test, le runtime du PM, environ 200 Mo par arbre. Quand l’arbre disparaît, cet environnement ne s’en va pas. La PR #133808, fusionnée le 7 octobre 2026, comble cet écart.

L’état est indexé par chemin

D’abord, ce qu’est réellement la chose. L’état de dépendances vit sous <hermes home>/installs/<install_key>/, où install_key n’est pas un nom de projet mais un hachage du chemin :

# pm/environments.py
def install_key(project_root: Path) -> str:
    canonical = str(Path(project_root).resolve())
    return hashlib.sha256(canonical.encode("utf-8")).hexdigest()[:16]

Donc deux worktrees du même dépôt obtiennent des clés et des environnements différents, parce que leurs chemins diffèrent. C’est délibéré : tout l’intérêt d’un worktree est l’isolation, et partager un unique environnement de dépendances entre les arbres l’entamerait.

Le coût, c’est que cette isolation a une durée de vie. git worktree remove, le pruneur au démarrage, hermes worktree prune, et même un rm -rf manuel sur l’arbre laissent tous installs/<key>/ intact. L’état de dépendances existe par chemin de checkout ; le checkout a disparu, l’état non, et plus rien ne le référence.

Les chiffres de la PR rendent l’échelle concrète. Sur un hôte de développeur, après quinze jours de campagnes de worktrees, ~/.hermes/installs contenait 499 entrées totalisant 92 Go, dont 402 entrées (80 Go) enregistraient un checkout qui n’existe plus — 360 d’entre elles sous .worktrees/ — avec un âge médian de quatre jours. Seules 76 entrées appartenaient à des checkouts vivants. Plus des quatre cinquièmes de l’espace était de l’état fantôme.

Le correctif : un seul enregistrement décide de ce qui est orphelin

Le nouveau pm/install_states.py fait quelque chose de volontairement simple. Chaque répertoire d’état qui s’est synchronisé avec succès contient un inputs/.project-root, écrit par record_activation_inputs, nommant le checkout auquel il appartient. La vérification consiste simplement à savoir si ce chemin existe encore.

La partie subtile est que « absent » ne signifie pas « supprimé ». Une racine de données partagée avec un conteneur (-v ~/.hermes:/opt/data) ou un volume non monté enregistrera aussi des chemins que le processus courant ne peut pas voir. Le verdict est donc restreint à prouvablement supprimé : le checkout doit avoir été situé quelque part que ce processus peut observer — sous la racine de données qui possède installs/, ou directement dans un répertoire .worktrees/ qui existe encore. Les répertoires d’état sans aucun enregistrement ne sont jamais touchés.

La récupération est tout aussi prudente. Tout ce qui est encore détenu est conservé :

  • le .install.lock ne peut pas être pris (un processus l’utilise) ;
  • toute génération sous environments/ a encore un bail actif (quelque chose la lit) ;
  • un verrou qui ne peut même pas être ouvert compte comme détenu.

Cette prudence est correcte. Supprimer un environnement de dépendances en cours d’utilisation casse la session en cours ; en garder un qui aurait dû partir ne coûte que du disque.

Comment le récupérer dès maintenant

Il n’y a aucune migration à effectuer. Deux chemins font le travail automatiquement :

hermes pm gc          # la sortie gagne une entrée finale : N install state dir(s) of deleted checkouts
hermes worktree prune # récupère l'état après le passage sur les branches, pour les arbres disparus

Le pruneur qui s’exécute à chaque démarrage de Hermes le fait aussi, donc en usage normal l’état d’un worktree supprimé disparaît au prochain lancement de hermes ou au prochain pm gc. La récupération au démarrage est fail-soft — un échec est journalisé au niveau debug et ne bloque jamais le lancement.

Pour voir d’abord ce que vous portez :

du -sh ~/.hermes/installs           # total
ls ~/.hermes/installs | wc -l       # nombre d'entrées
hermes worktree list                # âge, taille, verdict et raison pour chaque arbre dans .worktrees/

hermes worktree list est la vérification préalable sûre : il imprime un verdict et la raison pour chaque arbre avant que vous ne fassiez le prune. Il ne supprime jamais de modifications suivies non commitées, de commits uniques non poussés, ou d’arbres en cours d’utilisation, et les arbres jetables purement non suivis sont archivés dans ~/.hermes/archive/worktree-prune/ avant suppression.

En quoi cela diffère des autres histoires de disque

Nous avons couvert ici quelques problèmes liés au disque, et les mécanismes sont assez différents pour être gardés distincts :

  • une mise à jour qui fait exploser le disque concerne les artefacts temporaires pendant hermes update ;
  • la perte de données d’un worktree sale concerne un pruneur qui a supprimé des arbres qu’il aurait dû conserver ;
  • celui-ci va dans la direction opposée : quelque chose qui aurait dû être supprimé ne l’a pas été. Le nettoyage atteignait l’arbre mais pas l’état de dépendances que l’arbre laissait derrière.

Les deux premiers sont des bugs de garde-fou ; celui-ci est une lacune de couverture. Ensemble, ils dressent le tableau du côté des dépendances, et réduire le stockage à 78 % de sa taille précédente couvre le côté données — sessions et caches — si vous voulez les deux moitiés.

En bref

  • ~/.hermes/installs/ stocke l’état de dépendances indexé par le hachage du chemin de checkout, un environnement d’environ 200 Mo par worktree ou clone jetable ;
  • supprimer l’arbre ne supprimait jamais cet état — ni pm gc, ni worktree prune, ni rm -rf ;
  • désormais hermes pm gc, hermes worktree prune et le pruneur au démarrage récupèrent tous l’état dont le checkout est prouvablement supprimé, et laissent tranquille tout ce qui se trouve derrière un verrou ou un bail encore détenu ;
  • mesuré sur un hôte : 499 entrées à 92 Go, dont 402 orphelines (80 Go), 92 Go ramenés à 18 Go après récupération.

Si vous ouvrez régulièrement des worktrees ou lancez des clones jetables, exécutez un du -sh ~/.hermes/installs aujourd’hui. Si le nombre est plus grand que prévu, hermes pm gc est toute la réponse.