hermes profile update supprimait vos skills et vos tâches cron

Votre équipe a packagé un agent Hermes sous forme de dépôt git : quiconque en a besoin lance hermes profile install et récupère l’ensemble — personnalité, skills, tâches cron, config. Vous avez ajouté deux touches personnelles à ce profile : un skill qui répond aux questions de conformité, et une tâche cron qui publie un résumé de statut chaque matin. Une release amont arrive, vous suivez la documentation et lancez hermes profile update. La mise à jour réussit, et votre skill et votre tâche cron ont disparu.
Ce n’était pas une erreur de l’utilisateur. La PR #111344 corrige un bug P1 de perte de données : la mise à jour d’une distribution traitait skills/ et cron/ comme des répertoires qu’elle pouvait remplacer en bloc.
Deux façons de partager un profile, et une seule qui mord
Un profile Hermes est un agent autonome : sa propre config, ses propres credentials, sa mémoire, ses skills et ses tâches cron. Le partager avec quelqu’un d’autre se fait par deux chemins, qui répondent à des questions différentes :
- Une distribution (dépôt git) : l’auteur écrit un
distribution.yaml, les autres lancenthermes profile install <repo>et suivent l’amont avechermes profile update. À choisir quand l’agent est un produit que vous allez continuer à améliorer — l’agent de conformité interne d’une équipe, une release communautaire, le même agent sur cinq machines. - Un fichier d’export (instantané
.tar.gz) :/exportet/import, ouhermes profile export/import. À choisir quand vous voulez simplement que quelqu’un ait votre configuration tout de suite. Il n’y a pas de versionnage.
Le bug ne touchait que le premier chemin, car seule une distribution fait des mises à jour qui écrasent. Notre guide des profiles multi-instances couvre les frontières entre les deux, et la référence de la commande hermes profile contient le détail au niveau des commandes.
Pourquoi ça supprimait : remplacer tout le répertoire
L’ancienne logique de mise à jour traitait les répertoires appartenant à l’auteur comme des unités à remplacer sur place : supprimer le skills/ cible, copier par-dessus le skills/ du nouveau payload. Du point de vue de l’auteur, c’est correct — cela garantit que les fichiers que l’auteur a retirés disparaissent vraiment au lieu de subsister indéfiniment.
Ce qu’elle ne savait pas distinguer, c’est quelles racines appartiennent à l’auteur et lesquelles l’utilisateur a ajoutées. skills/curator-custom/ est à vous, mais du point de vue du programme de mise à jour il est indiscernable de skills/shipped/, donc un remplacement de tout le répertoire l’a emporté avec le reste. Idem pour cron/mine.json. Et hermes profile update signifie « mettre à jour », pas « réinitialiser » — personne ne s’attend à voir ses propres ajouts disparaître.
La nouvelle règle : traiter un répertoire de premier niveau comme un ensemble de racines, et fusionner chacune d’elles
Le correctif traite chaque répertoire de premier niveau appartenant à l’auteur (skills/, cron/, et tout répertoire déclaré par le manifeste) comme un conteneur de racines plutôt que comme une unité (_copy_dist_payload dans hermes_cli/profile_distribution.py) :
- une racine que le nouveau payload livre bien remplace sa contrepartie en bloc — les fichiers que l’auteur a retirés disparaissent bien ;
- une racine que le payload ne livre pas est laissée intacte, car elle n’a jamais fait partie de cette release ;
- la même logique s’applique à
updatecomme àinstall --force, pour l’ensemble détenu par défaut et pour undistribution_owned: [skills]explicite.
La preuve la plus claire est le tableau avant/après issu de la PR elle-même :
| Scénario | Avant | Après |
|---|---|---|
votre skills/curator-custom/ |
supprimé | préservé |
votre cron/mine.json |
supprimé | préservé |
| le skill livré par l’auteur mis à jour en v2 | mis à jour | mis à jour |
| un fichier que l’auteur a retiré | disparu | disparu |
Ces quatre lignes ensemble décrivent le comportement réel : protéger vos ajouts et suivre la release de l’auteur tiennent maintenant en même temps, au lieu de forcer un choix.
Trois cas limites à connaître
Les entrées imbriquées de l’allowlist remplacent toujours exactement ce chemin. Si le manifeste liste skills/compliance/, seul ce chemin est remplacé — les racines voisines ne sont pas touchées.
Un conteneur sous forme de lien symbolique est refusé au lieu d’être écrit au travers. Si skills/ est un lien symbolique vers un répertoire partagé, la mise à jour lève une DistributionError avant la première écriture. Cela compte plus qu’on ne le croit : écrire à travers le lien écraserait le répertoire partagé et pourrait laisser le profile à moitié mis à jour, ce qui est bien plus difficile à diagnostiquer qu’un échec franc.
.env.template atterrit toujours en .env.EXAMPLE. Il est d’abord délié pour qu’il ne puisse pas être écrit à travers un symlink, et il ne masque jamais votre vrai .env.
Ce que vous devez faire
Sur une version qui contient ce correctif, relancez simplement la mise à jour — il n’y a rien à migrer :
hermes profile update
Pour connaître la surface détenue par l’auteur d’un profile donné, lisez distribution_owned dans son manifeste ; les chemins imbriqués qui s’y trouvent (comme skills/compliance/) limitent le remplacement à ce chemin exact. La liste complète des flags, y compris --force, est ici :
hermes profile update --help
Si vous avez déjà perdu des skills ou des tâches cron ajoutés par vous à cause de l’ancien remplacement de tout le répertoire, ils ne reviennent pas automatiquement — restaurez-les depuis une sauvegarde ou une autre machine. Notre guide sur l’emplacement des skills explique comment les reconstruire à la main, et les tâches cron qui suivent un profile indique où doivent vivre les tâches planifiées.
La version courte
hermes profile update ne remplace désormais que ce que l’auteur a livré ; les skills et les tâches cron que vous avez ajoutés au profile restent où ils sont. Si vous maintenez un agent d’équipe partagé, cela signifie que vous pouvez personnaliser en local sans vous demander si la prochaine release amont va faire le ménage.