L'Archyl Harness: agenti di coding che dichiarano il proprio lavoro prima di iniziare

La settimana scorsa ho scritto di tre agenti, tre pull request e un sistema incoerente. Il post finiva con un esercizio: prendi l'ultima settimana in cui il tuo team ha mergiato più di una pull request scritta da un agente, leggile una accanto all'altra, e chiediti cosa, nel tuo setup attuale, ti avrebbe detto che erano in disaccordo.

L'ho fatto sul nostro repository e la risposta è stata niente. Non "alla fine se n'è accorto chi revisionava", non "la CI ne ha intercettata metà". Niente, perché nessuno degli agenti ha mai detto cosa stava per fare. Ognuno ha letto il repository, scritto codice e aperto una pull request. Il primo momento in cui una persona poteva vedere due di loro lavorare sullo stesso servizio era la review, che è l'ultimo momento, e per allora entrambi avevano finito di essere sicuri di sé.

Quindi abbiamo costruito il passo che mancava. L'Archyl Harness è uscito questa settimana. Non è l'ennesimo agente di coding. Sta sopra gli agenti che fai già girare, e obbliga ognuno di loro ad annunciare un'unità di lavoro, sull'architettura documentata, prima di toccare qualsiasi cosa.

Una sessione di lavoro, vista da dentro

Il loop ha quattro chiamate, esposte come tool MCP. Un agente pianifica, apre una sessione, lavora mandando heartbeat, e chiude la sessione con quello che è successo davvero.

Ecco la seconda di quelle chiamate, da una sessione vera sul progetto Archyl stesso, accorciata:

▶ 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.

In quella singola chiamata sono successe quattro cose, e nessuna di queste è alla portata di un file di regole.

Il task è stato risolto sul modello C4, quindi l'agente ha ricevuto la fetta di architettura che conta invece che tutta quanta. Sono stati presi lease consultivi sugli elementi che sta per cambiare, ed è così che il prossimo agente viene a sapere di questo. Il preflight gate ha restituito un verdetto. E il briefing portava con sé le decisioni che vincolano il lavoro, più quello che l'ultimo agente passato di qui ha imparato nel modo duro.

Quell'ultima riga è memoria, e merita un post suo invece di un paragrafo in questo. La versione breve: le sessioni lasciano note, convenzioni e trappole attaccate agli elementi di architettura, e la sessione successiva se le ritrova automaticamente.

Il gate ha tre verdetti, e deny è quello raro

Il preflight gate è deliberatamente piccolo. Risponde a una domanda, prima che il lavoro cominci, con un verdetto su cui l'agente può agire.

allow vuol dire che nessun'altra sessione tiene un lease sui tuoi elementi target e che nessun guardrail di livello error si applica al task. Vai avanti.

warn è quello comune, e arriva con delle motivazioni. Un'altra sessione sta già lavorando su un elemento che stai per cambiare, oppure una regola di conformità con severity error copre questo task. La stringa esatta nel primo caso è quella che hai visto sopra: N target element(s) are being worked on by other active sessions — coordinate before changing them. L'agente prosegue, ma deve rispondere a ogni motivazione elencata, e le motivazioni fanno nomi.

deny succede solo quando è una sessione a chiederlo. Passa exclusive: true e un conflitto di lease ferma la sessione invece di avvisarla. Quello è il flag per il lavoro che non deve andare in gara con nessuno: una migrazione di schema, un cambio di contratto, un rename che tocca ogni chiamante. La sessione non si apre mai, e all'agente viene detto di riferire all'utente invece di aggirare l'ostacolo.

Essere precisi su questo conta più che far sembrare il gate intelligente. deny non è un motore di policy. Non legge il tuo piano per rifiutarlo per principio. Si rifiuta di lasciare che due agenti rivendichino lo stesso elemento quando hai detto che quell'elemento è esclusivo, e tutto il resto è un avviso di cui l'agente deve rispondere.

Il Guard sorveglia le scritture

La sessione copre l'intenzione. Il Guard copre quello che viene scritto davvero.

È un hook PreToolUse per Claude Code, installato insieme al plugin. Prima che l'agente scriva o modifichi un file, l'hook ricostruisce il file come sarebbe dopo la modifica, lo manda alle regole di conformità del tuo progetto, e legge il verdetto. Una violazione critica blocca la scrittura e restituisce la motivazione all'agente:

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'agente legge, sistema il layering, e va avanti. Nessuna persona è stata interrotta, e la violazione non ha mai raggiunto un branch.

Due scelte di design vanno dette chiaramente. ARCHYL_GUARD_BLOCK controlla la soglia: critical di default, high per bloccare di più, off per limitarsi ad avvisare. E l'hook è fail-open dappertutto. Nessuna API key, niente rete, nessun jq installato, una risposta lenta: la modifica passa intatta. Uno strumento di governance capace di rompere la sessione di editing di qualcuno viene disinstallato nel giro di una settimana, quindi non può.

Chiudere il loop

finish_work_session prende un esito onesto: un riassunto, le decisioni che vale la pena registrare, i follow-up rimasti in sospeso. I lease vengono rilasciati, il riassunto viene appuntato sugli elementi che la sessione teneva, e se il lavoro ha cambiato l'architettura, createChangeRequest: true apre un Architecture Change Request in bozza.

Questa è la parte che impedisce al modello di andare alla deriva in silenzio. Un agente che ristruttura un servizio non modifica di nascosto il modello C4. Deposita una proposta, una persona legge come la documentazione dovrebbe rimettersi in pari, e il merge passa dal controllo di versione di cui abbiamo scritto la settimana scorsa. Gli agenti propongono. Le persone mergiano. Non abbiamo in programma di rimuovere quel confine.

Sopra tutto questo, la Fleet console nell'Agent Hub mostra dal vivo ogni sessione dell'organizzazione: chi sta lavorando, su cosa, tenendo quali elementi, dietro quale gate, quanto è fresco il suo ultimo heartbeat. Gli elementi sotto un lease attivo mostrano anche un indicatore di lavoro in corso direttamente sul diagramma C4, che è la vista in cui "qui dentro c'è già qualcuno" serve davvero.

L'abbiamo costruito sotto se stesso

L'Harness è stato costruito da agenti che lavoravano sotto l'Harness, su un progetto Archyl che documenta Archyl.

Non era una demo. Era l'unico modo per scoprire se il loop sopravvive al contatto con il lavoro vero, e ha cambiato il prodotto diverse volte. Le sessioni sono finite in warn per davvero, su conflitti reali, perché due agenti stavano genuinamente modificando lo stesso container nella stessa ora. La trappola nel transcript qui sopra è una memoria che una sessione ha scritto dopo averci perso un pomeriggio, e una sessione successiva se l'è ritrovata nel briefing prima di toccare lo stesso file. Da quelle sessioni sono usciti tre Architecture Change Request, ognuno una persona che rivede come il modello debba rimettersi in pari con quello che un agente aveva appena fatto.

Ha prodotto anche correzioni più piccole, di quelle che emergono solo facendo dogfooding. Il badge del gate nella console prima renderizzava un chip neutro per allow, finché non è stato fatto notare che un badge che dice "non c'è niente che non va" su ogni riga è rumore. Adesso non renderizza proprio niente quando il verdetto è allow senza motivazioni, e il ragionamento dietro quella scelta è stato salvato come convenzione sul progetto, così il prossimo agente che tocca quel component non lo rimette dentro con le migliori intenzioni.

Installarlo è un solo comando

Dalla radice del tuo repository:

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

Chiede il tuo progetto e una API key, poi scrive tre cose: un .mcp.json che punta al server MCP di Archyl con ?profile=coding, un .archyl.json committabile che lega il repository al progetto (la key resta nel tuo ambiente), e il loop dell'harness aggiunto in fondo a CLAUDE.md e 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.
...

Poi /plugin marketplace add archyl-com/agent-skills e /plugin install archyl-developer@archyl-marketplace in Claude Code, che portano lo skill archyl-harness e l'hook Guard. La versione 0.7.0 del plugin è online.

?profile=coding è il piccolo dettaglio che fa funzionare tutto il resto. Il server MCP di Archyl espone 189 tool, che è il numero giusto per gestire un'architettura e quello sbagliato da mettere davanti a un agente che sta cercando di aggiungere il rate limiting. Il profilo coding ne pubblicizza 16: orientamento, contesto legato al task, i quattro tool di sessione, la memoria, e i controlli di conformità e di diff. Niente che modifichi il modello direttamente, perché quella strada passa dalle Change Request. Nei nostri test, un agente a cui consegni il catalogo completo si mette a esplorarlo. Un agente a cui consegni sedici tool segue il loop.

Cosa non fa

I lease sono consultivi. Non c'è nessun lock. Un lease dice al secondo agente che il primo è là dentro, nel suo briefing, nella console e sul diagramma. Non lo ferma. Per ora è deliberato, perché un lock rigido su un modello della tua architettura è un modo molto efficace per impedire al tuo team di lavorare quando un agente muore a metà sessione, ma non dovresti descrivere i lease al tuo team come mutua esclusione.

Un agente che non apre mai una sessione è invisibile. Ogni garanzia qui parte dall'agente che chiama start_work_session. Niente nel protocollo obbliga a quella chiamata. Lo skill e lo snippet in CLAUDE.md la rendono il comportamento di default; un agente determinato, o uno collegato senza lo skill dell'harness, scrive semplicemente codice come ha sempre fatto. L'hook Guard è l'unica parte che scatta senza collaborazione, e solo dentro Claude Code.

deny vale quanto quello che hai scritto. Il gate legge le tue regole di conformità e i tuoi lease. Un set di regole vuoto e un solo agente producono allow per sempre, il che è tecnicamente corretto e del tutto privo di informazione.

I piani sono fondati, non corretti. plan_work è un piano AI costruito a partire dal tuo modello C4, dai tuoi ADR e dai guardrail, con un fallback deterministico che restituisce la verità di base ordinata quando non c'è nessun provider AI configurato o il modello restituisce qualcosa di inutilizzabile. Rispetta l'architettura documentata. Non sa se l'architettura documentata sia una buona idea.

Una Change Request ha bisogno di un autore noto. Le sessioni avviate con una credenziale che non è legata a un utente non possono aprirne una, e finish_work_session lo dice nella sua risposta invece di fallire. Se la key del tuo bot di CI è a livello di organizzazione, i suoi esiti atterrano come memoria ma non come proposta.

Da dove partire

Se fai già girare agenti su un progetto Archyl documentato, il comando di setup qui sopra richiede circa cinque minuti e la prima sessione ti dirà qualcosa. Guarda la Fleet console durante un pomeriggio in cui girano due agenti. Il momento interessante è il primo warn, perché dà un nome a una collisione che prima restava invisibile fino alla review.

Se non hai ancora un'architettura documentata, quello è il vero prerequisito, ed è lo stesso di sempre: l'Harness arbitra usando il modello, quindi un modello vuoto non arbitra niente.


L'Harness fa parte di archyl: le sessioni di lavoro, il preflight gate, la Fleet console e la memoria. Il plugin, gli skill e l'hook Guard e le GitHub Actions sono open source. Il setup completo è nella guida all'Harness. Letture correlate: molti agenti, una sola architettura, perché i tuoi agenti hanno un file di regole e non un modello, e il server MCP che c'è dietro.