Archyl Harness:始める前に自分の作業を宣言するコーディングエージェント

先週、3 つのエージェント、3 本の pull request、そして 1 つの一貫性のないシステムについて書きました。その記事は 1 つの練習で終わっていました:あなたのチームがエージェントの書いた pull request を 2 本以上マージした直近の 1 週間を取り、それらを並べて読み、いまの構成の中で、それらが食い違っていることを教えてくれたはずのものは何かを問うてください。

私は自分たちのリポジトリでそれをやりました。答えは、何もない、でした。「レビュアーがそのうち気づいた」でもなく、「CI が半分は捕まえた」でもありません。何もない。どのエージェントも、これから何をするつもりなのかを一度も言わなかったからです。それぞれがリポジトリを読み、コードを書き、pull request を開きました。2 つのエージェントが同じサービスで作業していると人間が見られる最初の瞬間はレビューで、それは最後の瞬間です。そのときにはもう、両方とも自信を持ち終えています。

そこで、抜けていたステップを作りました。Archyl Harness を今週リリースしました。これはもう 1 つのコーディングエージェントではありません。すでにあなたが走らせているエージェントの上に乗り、それぞれに、何かに触れる前に、ドキュメント化されたアーキテクチャに対して作業の単位を宣言させます。

ワークセッションを内側から見る

ループは 4 つの呼び出しからなり、MCP tool として公開されています。エージェントは計画し、セッションを開き、heartbeat を送りながら作業し、実際に起きたことを添えてセッションを閉じます。

これはそのうちの 2 番目の呼び出しで、Archyl プロジェクト自体での実際のセッションから、短くしたものです:

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

この 1 回の呼び出しの中で 4 つのことが起きていて、そのどれもルールファイルにできることではありません。

タスクは C4 モデルに対して解決されたので、エージェントは全体ではなく、意味のあるアーキテクチャの断面を受け取りました。これから変更する要素には、アドバイザリーリース(advisory lease)が取られていて、次のエージェントはそれによってこのエージェントの存在を知ります。プリフライトゲートが判定を返しました。そしてブリーフィングは、この作業を縛る決定と、ここに最後に立ったエージェントが痛い目を見て学んだことを運んできました。

最後のその行が memory で、これは本記事の 1 段落ではなく、それ自体の記事に値します。短く言えば:セッションはメモ、規約、落とし穴をアーキテクチャ要素に紐づけて残し、次のセッションはそれを自動的に受け取ります。

ゲートの判定は 3 つ、そして deny は稀なもの

プリフライトゲートは意図的に小さく作ってあります。作業が始まる前に 1 つの問いに答え、エージェントが行動に移せる判定を返します。

allow は、あなたの対象要素に対して他のどのセッションもリースを持っておらず、error レベルの guardrail がこのタスクに当てはまらない、という意味です。進めてください。

warn はよくあるほうで、理由が付いてきます。あなたがこれから変更する要素で別のセッションがすでに作業しているか、severity が error の適合性ルールがこのタスクを覆っています。前者の場合の正確な文字列は、上で見たものです:N target element(s) are being worked on by other active sessions — coordinate before changing them。エージェントは進みますが、列挙された理由のすべてに対処しなければならず、その理由は名前を挙げてきます。

deny は、セッションがそれを求めたときにだけ起きます。exclusive: true を渡すと、リースの衝突はセッションに警告するのではなく、セッションを止めます。これは、誰とも競合してはいけない作業のためのフラグです:スキーマのマイグレーション、contract の変更、すべての呼び出し元に触れる rename。セッションは決して開かれず、エージェントは迂回するのではなくユーザーに報告するよう告げられます。

ここを正確に言うことは、ゲートを賢そうに見せることより大事です。deny はポリシーエンジンではありません。あなたの計画を読んで、主義として拒否したりはしません。ある要素を排他だとあなたが言ったときに、2 つのエージェントが同じ要素を主張することを拒むだけで、それ以外はすべて、エージェントが説明責任を負うべき警告です。

Guard は書き込みを見張る

セッションは意図をカバーします。Guard は実際に書かれるものをカバーします。

これは Claude Code 用の PreToolUse フックで、プラグインと一緒にインストールされます。エージェントがファイルを書いたり編集したりする前に、フックは編集後のファイルを再構成し、それをあなたのプロジェクトの適合性ルールに送り、判定を読みます。critical な違反は書き込みをブロックし、その理由をエージェントに返します:

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.

エージェントはそれを読み、レイヤリングを直し、そのまま進みます。人は誰も中断されず、違反はブランチに一度も届きませんでした。

2 つの設計上の選択は、はっきり言っておく価値があります。ARCHYL_GUARD_BLOCK がしきい値を制御します:デフォルトは critical、もっとブロックしたければ high、警告だけにしたければ off。そしてフックはどこでも fail-open です。API キーがない、ネットワークがない、jq が入っていない、応答が遅い:編集はそのまま通ります。誰かの編集セッションを壊しうるガバナンスツールは 1 週間でアンインストールされるので、壊せないようにしてあります。

ループを閉じる

finish_work_session は正直な結果を受け取ります:要約、記録に値する決定、手つかずで残ったフォローアップ。リースは解放され、要約はそのセッションが保持していた要素に貼り付けられ、作業がアーキテクチャを変えたなら、createChangeRequest: trueArchitecture Change Request(アーキテクチャ変更リクエスト)のドラフトを開きます。

これが、モデルが黙って drift していくのを防ぐ部分です。サービスを組み替えたエージェントが、こっそり C4 モデルを編集することはありません。提案を出し、ドキュメントがどう追いつくべきかを人が読み、マージは先週書いたバージョンチェックを通ります。エージェントは提案する。人がマージする。この境界を外す予定はありません。

そのすべての上で、Agent Hub の Fleet console が、組織内のすべてのセッションをライブで表示します:誰が、何に取り組み、どの要素を保持し、どのゲートの後ろにいて、最後の heartbeat がどれだけ新しいか。アクティブなリースの下にある要素は、C4 ダイアグラムの上に直接、作業中インジケーターも表示します。「ここには他の誰かがいる」が実際に役立つのは、そのビューです。

自分自身の下で作った

Harness は、Harness の下で働くエージェントたちが、Archyl をドキュメント化している Archyl プロジェクトの上で作りました。

これはデモではありませんでした。ループが現実の作業との接触に耐えるかどうかを知る唯一の方法で、そしてそれはプロダクトを何度も変えました。セッションは本物の衝突で本当に warn になりました。2 つのエージェントが同じ container を同じ時間に本当に編集していたからです。上の transcript にある落とし穴は、あるセッションが午後をまるごと失ったあとに書いた memory で、後のセッションが同じファイルに触れる前にブリーフィングでそれを受け取りました。それらのセッションから 3 件の Architecture Change Request が生まれ、そのそれぞれが、エージェントがいましたことにモデルがどう追いつくべきかを人がレビューしたものです。

ドッグフーディングでしか表に出てこない、もっと小さな修正も生まれました。コンソールのゲートのバッジは、以前は allow に対して中立的なチップを描画していましたが、どの行にも「何も問題ありません」と表示するバッジはノイズだと指摘されました。いまは、判定が理由なしの allow のときには何も描画しません。そしてその理由づけはプロジェクトの規約として保存されたので、次にその component に触れるエージェントが、親切心からそれを戻すことはありません。

インストールはコマンド 1 つ

リポジトリのルートで:

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

プロジェクトと API キーを尋ねたうえで、3 つのものを書きます:?profile=coding 付きで Archyl の MCP サーバーを指す .mcp.json、リポジトリをプロジェクトに結びつける、commit してよい .archyl.json(キーはあなたの環境に残ります)、そして CLAUDE.mdAGENTS.md に追記される harness のループ:

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

そのあと Claude Code で /plugin marketplace add archyl-com/agent-skills/plugin install archyl-developer@archyl-marketplace。これで archyl-harness スキルと Guard フックが入ります。プラグインのバージョン 0.7.0 が公開されています。

?profile=coding は、残りを機能させる小さなディテールです。Archyl の MCP サーバーは 189 個の tool を公開していて、それはアーキテクチャを管理するには正しい数で、レートリミットを追加しようとしているエージェントの前に置くには間違った数です。coding プロファイルが宣伝するのは 16 個:オリエンテーション、タスク単位のコンテキスト、4 つのセッション tool、memory、そして適合性と diff のチェック。モデルを直接編集するものはありません。その経路は Change Request を通るからです。私たちのテストでは、フルカタログを渡されたエージェントはカタログを探索します。16 個の tool を渡されたエージェントはループに従います。

これがやらないこと

リースはアドバイザリーです。 何もロックしません。リースは 2 番目のエージェントに、1 番目がそこにいることを伝えます。ブリーフィングの中で、コンソールの中で、ダイアグラムの上で。止めはしません。いまのところこれは意図的です。あなたのアーキテクチャのモデルに対する強いロックは、エージェントがセッションの途中で死んだときにチームの作業を止める、非常に効果的な方法だからです。ただし、チームに対してリースを相互排他として説明するべきではありません。

セッションを一度も開かないエージェントは見えません。 ここでのすべての保証は、エージェントが start_work_session を呼ぶところから始まります。プロトコルの中に、その呼び出しを強制するものはありません。スキルと CLAUDE.md のスニペットがそれをデフォルトの振る舞いにします。決意の固いエージェント、あるいは harness スキルなしで接続されたエージェントは、これまでどおりにコードを書くだけです。協力なしで発火する唯一の部分は Guard フックで、それも Claude Code の中だけです。

deny は、あなたが書いたものの分だけしか良くなりません。 ゲートはあなたの適合性ルールとリースを読みます。空のルールセットと 1 つのエージェントは永遠に allow を返します。技術的には正しく、まったく何も教えてくれません。

計画は根拠を持ちますが、正しいとは限りません。 plan_work は、あなたの C4 モデル、ADR、guardrail から作られた AI の計画で、AI プロバイダーが設定されていない場合やモデルが使えないものを返した場合には、順序づけられた基礎情報を返す決定的なフォールバックが付いています。ドキュメント化されたアーキテクチャを尊重します。ドキュメント化されたアーキテクチャが良い考えかどうかは知りません。

Change Request には既知の著者が必要です。 ユーザーに紐づいていない認証情報で開始されたセッションは、Change Request を開けません。finish_work_session は失敗するのではなく、レスポンスの中でそう伝えます。CI ボットのキーが組織スコープなら、その成果は memory としては残りますが、提案としては残りません。

どこから始めるか

すでにドキュメント化された Archyl プロジェクトに対してエージェントを走らせているなら、上のセットアップコマンドはだいたい 5 分で終わり、最初のセッションが何かを教えてくれます。2 つのエージェントが走っている午後に Fleet console を眺めてください。面白い瞬間は最初の warn です。それは、これまでレビューまで見えなかった衝突に名前を与えるからです。

まだドキュメント化されたアーキテクチャがないなら、それこそが本当の前提条件で、それはいつもと同じものです:Harness はモデルを使って裁定するので、空のモデルは何も裁定しません。


Harness は archyl の一部です:ワークセッション、プリフライトゲート、Fleet console、そして memory。プラグイン、スキル、Guard フックGitHub Actions はオープンソースです。完全なセットアップは Harness ガイドにあります。関連記事:多くのエージェント、ひとつのアーキテクチャなぜあなたのエージェントにはルールファイルがあってモデルがないのかその背後にある MCP サーバー