L'Archyl Harness : des agents de code qui déclarent leur travail avant de commencer

La semaine dernière, j'ai écrit à propos de trois agents, trois pull requests et un système incohérent. Le billet se terminait par un exercice : prenez la dernière semaine où votre équipe a mergé plus d'une pull request écrite par un agent, lisez-les côte à côte, et demandez-vous ce qui, dans votre installation actuelle, vous aurait dit qu'elles n'étaient pas d'accord.

Je l'ai fait sur notre propre dépôt et la réponse a été : rien. Pas "le relecteur a fini par le remarquer", pas "la CI en a attrapé la moitié". Rien, parce qu'aucun des agents n'a jamais dit ce qu'il s'apprêtait à faire. Chacun a lu le dépôt, écrit du code, et ouvert une pull request. Le premier moment où un humain pouvait voir deux d'entre eux travailler sur le même service, c'était la revue, qui est le dernier moment, et à ce stade les deux avaient fini d'être sûrs d'eux.

Alors on a construit l'étape manquante. L'Archyl Harness est sorti cette semaine. Ce n'est pas un agent de code de plus. Il se place au-dessus des agents que vous faites déjà tourner, et il oblige chacun d'eux à annoncer une unité de travail, contre l'architecture documentée, avant de toucher à quoi que ce soit.

Une session de travail, vue de l'intérieur

La boucle compte quatre appels, exposés comme des tools MCP. Un agent planifie, ouvre une session, travaille en envoyant des heartbeats, puis ferme la session avec ce qui s'est réellement passé.

Voici le deuxième de ces appels, tiré d'une vraie session sur le projet Archyl lui-même, raccourci :

▶ start_work_session(
    task: "rank recalled memories by freshness so stale facts stop winning",
    agentName: "claude-code/vincent")

# Harness Session

- **Session ID**: `24643fa6…`
- **Gate**: warn
  - 1 target element(s) are being worked on by other active sessions — coordinate before changing them
- **Leased elements** (you are the announced worker on these):
  - component `Harness Service`
  - container `MCP Server`
- **Conflicts** (someone else is already working here):
  - MCP Server — held by claude-code/memory-ui: render the memory graph with element clusters

**Protocol**: call `heartbeat_work_session` at least every 30 minutes while working,
and `finish_work_session` with a summary (and decisions worth recording) when done.

## Most relevant elements

- **Harness Service** (component) — `backend/internal/service/harness`
  Work sessions, leases, preflight gate, element memory.

## Related decisions (respect these)

- ADR-5: Agents propose, humans merge [accepted]

## What previous sessions did here

- **MCP Server** [PITFALL] (claude-code/vincent, 1 day ago): every new ID argument
  name must be added to idArgumentResolvers in authz.go, or the cross-org check
  silently skips it.

Quatre choses se sont produites dans ce seul appel, et aucune n'est à la portée d'un fichier de règles.

La tâche a été résolue contre le modèle C4, donc l'agent a reçu la tranche d'architecture qui compte plutôt que l'ensemble. Des leases consultatifs ont été pris sur les éléments qu'il s'apprête à modifier, et c'est comme ça que le prochain agent apprend l'existence de celui-ci. Le preflight gate a renvoyé un verdict. Et le briefing portait les décisions qui contraignent le travail, plus ce que le dernier agent passé par ici a appris à la dure.

Cette dernière ligne, c'est la mémoire, et elle mérite son propre billet plutôt qu'un paragraphe dans celui-ci. La version courte : les sessions laissent des notes, des conventions et des pièges accrochés aux éléments d'architecture, et la session suivante les récupère automatiquement.

Le gate a trois verdicts, et deny est le rare

Le preflight gate est délibérément petit. Il répond à une question, avant que le travail commence, avec un verdict sur lequel l'agent peut agir.

allow veut dire qu'aucune autre session ne détient de lease sur vos éléments cibles et qu'aucun guardrail de niveau erreur ne s'applique à la tâche. Continuez.

warn est le verdict courant, et il vient avec des raisons. Une autre session travaille déjà sur un élément que vous vous apprêtez à modifier, ou une règle de conformité de sévérité error couvre cette tâche. La chaîne exacte dans le premier cas est celle que vous avez vue plus haut : N target element(s) are being worked on by other active sessions — coordinate before changing them. L'agent continue, mais il doit traiter chaque raison listée, et les raisons citent des noms.

deny n'arrive que quand une session le demande. Passez exclusive: true et un conflit de lease arrête la session au lieu de l'avertir. C'est le flag pour le travail qui ne doit courir contre personne : une migration de schéma, un changement de contrat, un rename qui touche tous les appelants. La session ne s'ouvre jamais, et on dit à l'agent d'en référer à l'utilisateur plutôt que de contourner.

Être précis là-dessus compte plus que de faire passer le gate pour malin. deny n'est pas un moteur de politiques. Il ne lit pas votre plan pour le refuser par principe. Il refuse que deux agents revendiquent le même élément quand vous avez dit que cet élément est exclusif, et tout le reste est un avertissement dont l'agent doit répondre.

Le Guard surveille les écritures

La session couvre l'intention. Le Guard couvre ce qui est réellement écrit.

C'est un hook PreToolUse pour Claude Code, installé avec le plugin. Avant que l'agent n'écrive ou n'édite un fichier, le hook reconstruit le fichier tel qu'il serait après l'édition, l'envoie aux règles de conformité de votre projet, et lit le verdict. Une violation critique bloque l'écriture et renvoie la raison à l'agent :

Archyl Guard: this change violates the project's architecture rules for
backend/internal/adapter/http/handlers/report.go:
- [critical] No direct database access from HTTP handlers — move the query
  behind a service

Adjust the change to respect these rules, or ask the user whether to override them.

L'agent lit ça, corrige le layering, et poursuit. Aucun humain n'a été interrompu, et la violation n'a jamais atteint une branche.

Deux choix de conception méritent d'être dits clairement. ARCHYL_GUARD_BLOCK contrôle le seuil : critical par défaut, high pour bloquer davantage, off pour se contenter d'avertir. Et le hook est fail-open partout. Pas de clé d'API, pas de réseau, pas de jq installé, une réponse lente : l'édition passe sans être touchée. Un outil de governance capable de casser la session d'édition de quelqu'un est désinstallé en une semaine, donc il ne peut pas.

Fermer la boucle

finish_work_session prend un résultat honnête : un résumé, les décisions qui méritent d'être enregistrées, les suites laissées en plan. Les leases sont relâchés, le résumé est épinglé aux éléments que la session détenait, et si le travail a changé l'architecture, createChangeRequest: true ouvre un Architecture Change Request à l'état de brouillon.

C'est la partie qui empêche le modèle de dériver en silence. Un agent qui restructure un service ne va pas éditer discrètement le modèle C4. Il dépose une proposition, une personne lit comment la documentation devrait rattraper son retard, et le merge passe par la vérification de version dont on a parlé la semaine dernière. Les agents proposent. Les gens mergent. On ne prévoit pas de retirer cette frontière.

Au-dessus de tout ça, la Fleet console de l'Agent Hub montre en direct chaque session de l'organisation : qui travaille, sur quoi, en tenant quels éléments, derrière quel gate, à quel point son dernier heartbeat est frais. Les éléments sous lease actif affichent aussi un indicateur de travail en cours directement sur le diagramme C4, qui est la vue où "quelqu'un d'autre est là-dedans" est réellement utile.

On l'a construit sous lui-même

Le Harness a été construit par des agents travaillant sous le Harness, sur un projet Archyl qui documente Archyl.

Ce n'était pas une démo. C'était la seule façon de savoir si la boucle survit au contact du vrai travail, et ça a changé le produit plusieurs fois. Des sessions ont tapé warn pour de vrai, sur de vrais conflits, parce que deux agents éditaient réellement le même container à la même heure. Le piège dans le transcript ci-dessus est une mémoire écrite par une session après y avoir perdu un après-midi, et une session ultérieure l'a récupérée dans son briefing avant de toucher le même fichier. Trois Architecture Change Requests sont sortis de ces sessions, chacun étant une personne relisant comment le modèle devait rattraper ce qu'un agent venait de faire.

Ça a aussi produit des corrections plus petites que seul le dogfooding fait remonter. Le badge de gate dans la console rendait autrefois une pastille neutre pour allow, jusqu'à ce qu'on fasse remarquer qu'un badge affichant "rien ne va mal" sur chaque ligne, c'est du bruit. Il ne rend plus rien du tout quand le verdict est allow sans raisons, et le raisonnement derrière ce choix a été stocké comme convention sur le projet, pour que le prochain agent qui touche ce component ne le remette pas serviablement en place.

L'installer tient en une commande

Depuis la racine de votre dépôt :

curl -fsSL https://raw.githubusercontent.com/archyl-com/agent-skills/main/templates/setup.sh | bash

Elle demande votre projet et une clé d'API, puis écrit trois choses : un .mcp.json pointant vers le serveur MCP d'Archyl avec ?profile=coding, un .archyl.json committable qui lie le dépôt au projet (la clé reste dans votre environnement), et la boucle du harness ajoutée à CLAUDE.md et AGENTS.md :

# Architecture — Archyl Harness

This project's architecture is documented in Archyl. Work under the harness loop:

1. For any non-trivial task, call `plan_work` first — it returns an implementation
   plan grounded in the documented architecture.
2. BEFORE changing code, call `start_work_session` (task + your agent name).
   Read the briefing: gate verdict, leased elements, conflicts, decisions, guardrails.
...

Ensuite /plugin marketplace add archyl-com/agent-skills et /plugin install archyl-developer@archyl-marketplace dans Claude Code, ce qui apporte le skill archyl-harness et le hook Guard. La version 0.7.0 du plugin est en ligne.

?profile=coding est le petit détail qui fait fonctionner le reste. Le serveur MCP d'Archyl expose 189 tools, ce qui est le bon nombre pour gérer une architecture et le mauvais nombre à mettre devant un agent qui essaie d'ajouter du rate limiting. Le profil coding en annonce 16 : orientation, contexte cadré sur la tâche, les quatre tools de session, la mémoire, et les vérifications de conformité et de diff. Rien qui édite le modèle directement, parce que ce chemin passe par les Change Requests. Dans nos propres tests, un agent à qui on donne le catalogue complet l'explore. Un agent à qui on donne seize tools suit la boucle.

Ce qu'il ne fait pas

Les leases sont consultatifs. Rien ne verrouille. Un lease dit au deuxième agent que le premier est là-dedans, dans son briefing, dans la console et sur le diagramme. Il ne l'arrête pas. C'est délibéré pour l'instant, parce qu'un verrou dur sur un modèle de votre architecture est une façon très efficace d'empêcher votre équipe de travailler quand un agent meurt en pleine session, mais vous ne devriez pas décrire les leases à votre équipe comme de l'exclusion mutuelle.

Un agent qui n'ouvre jamais de session est invisible. Chaque garantie ici commence par l'agent qui appelle start_work_session. Rien dans le protocole ne force cet appel. Le skill et le snippet CLAUDE.md en font le comportement par défaut ; un agent déterminé, ou un agent connecté sans le skill harness, écrit simplement du code comme il l'a toujours fait. Le hook Guard est la seule partie qui se déclenche sans coopération, et uniquement dans Claude Code.

deny ne vaut que ce que vous avez écrit. Le gate lit vos règles de conformité et vos leases. Un jeu de règles vide et un seul agent produisent allow pour toujours, ce qui est techniquement correct et complètement dénué d'information.

Les plans sont ancrés, pas justes. plan_work est un plan IA construit à partir de votre modèle C4, de vos ADR et de vos guardrails, avec un repli déterministe qui renvoie la vérité terrain ordonnée quand aucun fournisseur d'IA n'est configuré ou que le modèle renvoie quelque chose d'inexploitable. Il respecte l'architecture documentée. Il ne sait pas si l'architecture documentée est une bonne idée.

Une Change Request a besoin d'un auteur connu. Les sessions démarrées avec un identifiant qui n'est pas rattaché à un utilisateur ne peuvent pas en ouvrir une, et finish_work_session le dit dans sa réponse plutôt que d'échouer. Si la clé de votre bot de CI est à la portée de l'organisation, ses résultats atterrissent comme mémoire mais pas comme proposition.

Par où commencer

Si vous faites déjà tourner des agents contre un projet Archyl documenté, la commande d'installation ci-dessus prend environ cinq minutes et la première session vous dira quelque chose. Regardez la Fleet console pendant un après-midi où deux agents tournent. Le moment intéressant, c'est le premier warn, parce qu'il nomme une collision qui restait invisible jusqu'à la revue.

Si vous n'avez pas encore d'architecture documentée, c'est ça le vrai prérequis, et c'est le même que toujours : le Harness arbitre en utilisant le modèle, donc un modèle vide n'arbitre rien.


Le Harness fait partie d'archyl : les sessions de travail, le preflight gate, la Fleet console et la mémoire. Le plugin, les skills et le hook Guard ainsi que les GitHub Actions sont open source. L'installation complète est dans le guide du Harness. À lire aussi : plusieurs agents, une seule architecture, pourquoi vos agents ont un fichier de règles et pas un modèle, et le serveur MCP qui est derrière.