Comment fonctionne Agents Never Sleep

Le parcours d'une exécution non supervisée, du début à la fin — le mécanisme, pas un argumentaire de vente.

Définition

Qu'est-ce que l'exécution d'agent non supervisée ?

L'exécution d'agent non supervisée, c'est un agent de code qui traite un backlog de tickets sans que personne ne surveille chaque étape — aucun humain présent pour répondre à une question de clarification. Cela ne fonctionne que si l'agent dispose d'une règle permanente pour savoir quoi faire dès qu'il n'est pas sûr : supposer et continuer, différer cette décision et passer au ticket suivant, ou arrêter toute l'exécution. Agents Never Sleep (ANS) est la couche de gouvernance qui fournit cette règle sous la forme d'un contrat durable et appliqué, afin qu'un ticket ambigu à 2 h du matin ne gèle jamais les trente-neuf autres.

1. Préflight au lancement — avant qu'un seul jeton ne soit dépensé

Une exécution headless démarre via le lanceur (bin/ans-run), une porte déterministe GO/NO-GO qui s'exécute avant le démarrage du CLI de l'agent — car le temps que les propres vérifications préflight de l'agent s'exécutent, les premiers jetons sont déjà dépensés pour une exécution peut-être vouée à l'échec.

Le lanceur vérifie, dans l'ordre : la confiance de la configuration (le fichier .claude/agents-never-sleep.json du dépôt doit avoir été explicitement approuvé une fois par utilisateur, rattaché à son SHA-256 — une configuration nouvelle ou modifiée sans personne à proximité pour l'approuver est un NO-GO) ; l'identité (un utilisateur cible configuré, jamais une exécution non supervisée laissée tourner en tant que root) ; la sélection de l'agent (un préréglage nommé, vérifié par une vraie sonde --version pour détecter une dérive des options avant la dépense, pas après) ; et un verrou sur l'arbre de travail — un flock(2) non bloquant, afin que deux lancements simultanés sur le même dépôt produisent toujours exactement un gagnant, libéré automatiquement par le noyau en cas de plantage.

Les codes de sortie sont sans ambiguïté : 0 démarré, 64 NO-GO, 65 l'arbre de travail est déjà occupé. Les indicateurs d'autonomie — le mode de permission qui permet à une exécution détachée de réellement continuer sans se bloquer sur une invite d'approbation — ne sont jamais activés par défaut ; un préréglage ne devient lançable qu'une fois qu'un humain a confirmé exactement ce que cet indicateur autorise.

Les tickets traversent la machine, point de contrôle par point de contrôle.
Les tickets traversent la machine, point de contrôle par point de contrôle.

2. La machine à états par ticket

Une fois en cours d'exécution, l'agent pilote une boucle à deux commandes contre le harness : next demande un ticket à traiter, complete consigne ce qui s'est passé. Chaque appel est un sous-processus neuf s'appuyant sur un état durable, écrit de façon atomique — un plantage entre les deux ne perd rien, car rien ne vit uniquement en mémoire.

Chaque ticket se termine dans exactement un des sept états de résultat, choisis pour que la réaction du lendemain matin ne soit jamais ambiguë :

  • DONE — implémenté, porte déterministe verte.
  • DONE_LOW_CONFIDENCE — porte verte, mais la révision déléguée d'un diff à haut risque a soulevé des préoccupations, a échoué, ou n'a jamais eu lieu ; nécessite une révision de jour.
  • PARKED_DECISION — une décision différée à un humain ; l'exécution a continué.
  • PARKED_FOUNDATIONAL — une ambiguïté fondamentale ; les tickets dépendants sont mis en quarantaine jusqu'à sa résolution.
  • BLOCKED_ENV — l'environnement a bloqué la progression (un verrou git, une porte inexécutable) — ce n'est pas la faute de l'agent.
  • FAILED_RETRYABLE — une porte a détecté un bug introduit par le diff ; la modification a été annulée ; il est sûr de réessayer.
  • FAILED_BUG_IN_AGENT — des échecs répétés suggèrent un problème systématique ; nécessite un regard humain.

3. Décider — puis implémenter

Avant de toucher un fichier, le harness classe le rayon d'impact du ticket et décide PROCEED, PARK ou HALT. Seul un ticket PROCEED atteint l'agent pour l'implémentation ; un PARK est consigné avant toute modification, car la décision elle-même est le produit du travail.

Une fois qu'un ticket PROCEED est remis, l'agent — l'exécutant dans cette conception — modifie exactement les fichiers de ce seul ticket. Le harness capture d'abord un instantané de l'arbre de travail, afin que tout ce que fait l'agent puisse être annulé.

La porte décide : le tampon ne tombe que si les tests passent.
La porte décide : le tampon ne tombe que si les tests passent.

4. La porte déterministe — le seul blocage strict

Une porte est une commande shell — votre suite de tests — exécutée après la modification. Sortie 0 est vert ; non nul est rouge. C'est la seule chose dans toute l'exécution capable de bloquer strictement un ticket ; tout le reste (une révision de modèle déléguée, un regard spécialisé) est consultatif et ne peut que refuser un tampon de confiance, jamais annuler le travail ni arrêter l'exécution.

Une porte rouge est classée, pas seulement rapportée : un échec clairement introduit par le diff revient au dernier commit vert et est consigné comme FAILED_RETRYABLE ; un échec qui semble préexistant, instable ou lié à l'environnement abaisse la confiance mais conserve le travail ; une porte qui ne peut pas s'exécuter du tout (un verrou, un délai dépassé, une invite non interactive à laquelle elle ne peut pas répondre) est consignée comme BLOCKED_ENV — jamais un arrêt silencieux. ANS ne supprime ni ne saute jamais un test en échec pour forcer un résultat vert.

---
id: fix-flaky-webhook-retry
title: Retry webhook delivery on 5xx with backoff
blast_radius: medium      # optional hint — the harness auto-classifies from the diff
gate: pytest tests/webhooks/ -x
---

The webhook sender should retry on 5xx responses with exponential backoff
(max 3 attempts). Cover it with a test that simulates two 500s then a 200.

Exemple illustratif — un ticket est un fichier Markdown avec un bloc de front-matter YAML optionnel ; le corps est la seule partie obligatoire, et gate nomme la commande qui décide de la réussite ou de l'échec de ce ticket.

5. ASK / PARK / HALT — les points de décision

Trois réponses distinctes à l'incertitude, jamais fusionnées en une seule. ASK est interdit dès qu'une exécution est non supervisée — il n'y a personne pour répondre, donc c'est automatiquement converti en PARK. Il reste deux vrais choix :

  • PARK — différer ce ticket ou cette décision, et passer directement au prochain ticket indépendant. C'est normal et sain, pas un arrêt. Une mise de côté consigne toujours pourquoi, les interprétations candidates, et la décision humaine exacte qui attend le lendemain matin. Exemples réels de Hard-PARK : dans quelle direction une migration de base de données doit aller, une modification d'un contrat d'API public ou partagé, tout ce qui touche une limite de sécurité ou d'isolation des locataires, et tout ce qui concerne l'argent, la facturation ou la tarification.
  • HALT — arrêter toute l'exécution. Réservé aux dangers véritablement irréversibles sans aucun filet de sécurité — par exemple, aucun contrôle de version présent et aucun qui puisse être créé. HALT est rare par conception ; la plupart des incertitudes relèvent d'un PARK, pas d'un HALT.

La discipline derrière le choix, c'est le rayon d'impact : le nommage, la structure interne, la formulation des journaux, ou un choix entre deux implémentations locales équivalentes peuvent PROCEEDer (supposer, journaliser, continuer, de façon réversible). Tout ce qui a un grand rayon d'impact, ou tout ce qui est véritablement inclassable, est mis de côté. Un petit élément mal mis de côté coûte une décision de cinq secondes le matin ; une grande chose mal supposée coûte une nuit de travail dans la mauvaise direction.

Le chien de garde veille ; une exécution figée redémarre, reprenable.
Le chien de garde veille ; une exécution figée redémarre, reprenable.

6. Le chien de garde — gel, puis un redémarrage reprenable

Un blocage est un type d'échec différent d'un arrêt : le processus est vivant, ne fait rien, et aucun hook surveillant un arrêt prématuré ne peut le voir. Une vague soutenue de surcharge chez un fournisseur en est la cause réaliste — l'exécution est toujours là, mais son heartbeat est devenu périmé.

Le chien de garde est un side-car qui exécute la commande non supervisée comme processus enfant et interroge son fichier de heartbeat. Quand le heartbeat dépasse un seuil configuré, il tue le processus enfant et le redémarre — et comme l'état d'ANS est durable, le redémarrage reprend exactement là où l'exécution en était ; les modifications partielles du ticket en cours sont annulées jusqu'à son dernier instantané, si bien que rien n'est perdu ni compté deux fois. Après un nombre plafonné de redémarrages, il déclenche une alerte et s'arrête, plutôt que de redémarrer indéfiniment. ans-run encapsule chaque lancement détaché dans le chien de garde par défaut.

Le même side-car nettoie aussi ses propres processus enfants abandonnés — par exemple un serveur MCP que l'agent a lancé et jamais fermé — strictement par filiation en chaîne parentale depuis le propre identifiant de processus de l'exécution, jamais en faisant correspondre un nom de processus (une correspondance par nom pourrait tuer une exécution non liée sur la même machine). Limite honnête : si le superviseur lui-même est tué de force, il ne peut pas nettoyer après coup, donc cela réduit la fuite de processus, sans l'éliminer.

7. Drain — continuer jusqu'à ce qu'il n'y ait plus rien à faire

L'agent continue d'appeler next puis complete jusqu'à ce que next renvoie un statut terminal : DRAINED (le backlog est vide), HALTED (une condition HALT a été rencontrée), ou LOW_YIELD (un disjoncteur s'est déclenché parce que la plupart des tickets récents se mettent de côté ou se bloquent plutôt que de se terminer — un signal que le backlog lui-même a besoin d'un regard humain, pas de plus de tentatives). Des plafonds de tentatives et de boucles forcent la mise de côté de tout ticket unique qui consommerait sinon toute l'exécution, afin qu'un élément maudit ne puisse pas répéter précisément l'échec que toute cette conception vise à éviter.

8. Le rapport du matin

Quand l'exécution se termine, elle écrit un seul rapport classé (night-report.md) plutôt que de vous laisser reconstruire ce qui s'est passé à partir des journaux. Il indique : ce qui est terminé et fiable, ce qui est terminé mais nécessite une révision de jour (une modification à haut risque dont la révision déléguée ne l'a pas validée), ce qui est mis de côté — chacun avec ses interprétations candidates et l'action suivante exacte —, ce qui est bloqué par l'environnement, et tout angle mort : une garantie dégradée, une capacité indisponible, un identifiant que l'exécution n'a pas pu lire. Une nuit à faible rendement est signalée bruyamment dans le rapport, afin que « l'exécution s'est terminée » ne soit jamais confondu avec « le travail a été fait ».