mcpField
A
Agent Coherence

Agent Coherence

ローカル
agent-coherence.devファイル・ストレージ122 日前に更新

Agent Coherence について

Agent Coherence は単一ホスト上の共有エージェント状態を調整し、古いビューや同時書き込みによる新しい内容の暗黙的な上書きを防ぎます。マルチエージェントシステム向けに、アダプターやファイル調整機能、TLA+で検証された安全性不変条件を備えます。

主な機能

  1. 01MESI方式の所有権と無効化
  2. 02同時書き込み向け楽観的コミットCAS
  3. 03複数リソースの一貫した読み取りセッション
  4. 04クラッシュ復旧時のグラント回収
  5. 05共有ファイル向けCoherentVolume
  6. 06TLA+で検証された安全性不変条件

活用シーン

  1. 01共有プランのエージェント間上書き防止
  2. 02共有状態への同時書き込みの調整
  3. 03エージェントメモリとRAGレコードの整合性維持
  4. 04クラッシュしたエージェントのグラント回収

MCP 対応

`stale-write-guard-fs` MCP サーバーは、MCP クライアントにコヒーレンス調整機能を公開します。`mcp` extra でインストールできます。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"agent-coherence": {
"command": "uvx",
"args": [
"agent-coherence"
]
}
}
}
起動コマンドuvx agent-coherence

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
pypiagent-coherence0.14.1

ツール

ツール一覧はまだ取得されていません

ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。

詳細

agent-coherence

agent-coherenceは、共有のplan.md、ストアキー、またはmemory.jsonをめぐって、あるエージェントが別のエージェントの作業を気づかれないまま上書きしてしまうのを防ぎます。単一ホスト上のエージェント状態を管理する、ベンダーに依存しないMESI方式+楽観的同時実行制御のコーディネーターで、安全性不変条件はTLA+で機械的に検証されています。

2つのエージェントが、plan.md、ストアキー、memory.jsonなどの成果物を共有しています。一方がそれを読み取って作業している間に、もう一方が新しいバージョンをコミットします。それでも最初のエージェントがそのまま書き戻すと、後勝ちで上書きされ、もう一方の作業は気づかれないまま消えてしまいます。エラーは発生せず、その後の判断はすべて誤ったバージョンに基づいて進められます。

見過ごされる理由。 エージェントシステムは、起きたことを2つの記録として保持します。1つは、インフラストラクチャで検証できる記録です。各エージェントがどのバージョンを保持していたか、実際に何がコミットされたか、何が拒否されたかを記録します。もう1つは、モデルが語る「タスク完了」「プランを更新しました」という記録です。エージェントが、その間に変更されたバージョンを使って動作するまでは、この2つは一致しています。そして、あなたが目にするのは、モデルが語る記録のほうです。そのため、更新の消失は問題のない実行に見え、モデルの問題としてデバッグされます。agent-coherenceは、エージェントが共有する状態についての検証済みの記録です。(エージェントが外部で行ったこと、たとえばメールの送信やWebhookの発火を検証するものではありません。そこは、アウトボックスと冪等性レイヤーの役割です。)agent-coherenceは、この気づかれない上書きを、明確な型付きの拒否へと変えます。共有成果物に対するMESI方式の所有権管理と無効化、並行書き込みに対する楽観的なコミットCAS、クラッシュ後に回収されたエージェントに対する読み取り世代フェンスによって、古い状態からの書き込みは拒否されるか、再試行可能な競合として返され、気づかれないまま適用されることはありません。LangGraph、CrewAI、AutoGen、OpenAI Agents SDK、プロセス間で共有するプレーンファイル(CoherentVolume)、あらゆるMCPクライアント(mcpエクストラ経由のstale-write-guard-fsサーバー)、カスタムオーケストレーターで、同じライブラリ、同じプロトコル、同じ動作を利用できます。エージェントが利用するモデルプロバイダー(Anthropic、OpenAI、Google、Mistral、オープンソース)にかかわらず、動作は変わりません。

CI PyPI arXiv Discussions

mcp-name: io.github.cohexa-ai/stale-write-guard-fs

# Requires Python 3.11+
pip install "agent-coherence[langgraph]"        # LangGraph drop-in
pip install "agent-coherence[crewai]"           # CrewAI adapter
pip install "agent-coherence[openai-agents]"    # OpenAI Agents SDK adapter (experimental)
pip install "agent-coherence[diagnose]"         # ccs-diagnose CLI
pip install "agent-coherence[mcp]"              # stale-write-guard-fs MCP server
pip install "agent-coherence[conformance]"      # substrate conformance corpus (for foreign implementations)
pip install "agent-coherence[all]"              # everything
# Before
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()

# After — one import change, no node code changes
from ccs.adapters import CCSStore
store = CCSStore(strategy="lazy")

store.get()、store.put()、store.search()は、変更せずそのまま動作します。CCSStoreは読み取り側の一貫性を追加します。ピアによるコミットでキャッシュされたビューが無効化されるため、次回の読み取りでは最新のデータを取得するためにキャッシュミスが発生します。ただし、古い状態からの書き戻しを拒否するわけではありません。putはバージョンCASではありません。書き込み側の更新消失を防ぐには、書き込みをCoherentVolumeまたはwrite_cas(以下を参照)経由にしてください。

1つのインポートを置き換えるだけで済むのは、ストアの名前空間がnamespace[0]にエージェントの識別情報を含む場合に限られます。(user_id, "memories")のような形では、複数のユーザーが同じ成果物に統合されてしまいます。名前空間の規約を参照してください。

# Plain files shared across processes / sessions — no framework required
from ccs.adapters.coherent_volume import CoherentVolume

vol = CoherentVolume(workspace_root, managed=("plans/**",))
plan = vol.read("plans/plan.md")           # tracked read — your view is registered
vol.write("plans/plan.md", revised_plan)   # stale view? denied fail-closed → vol.reacquire() and re-derive

agent-coherence-replay — CoherenceAdapterCoreを介するあらゆるエージェントシステム向けの不変条件リプレイです。LangGraphの記録は、v1でCCSStore.record_to(path)を使って検証済みです。CrewAI/AutoGenも同じ接続点を通じて連携していますが、未検証です。問題があれば、イシューを登録してください。

保証すること

各行は、TLA+/TLCでモデル検査された安全性不変条件です。make tla-checkを実行すると、9つすべての仕様が検査されます。CIでは、TLA+の入力(仕様とその設定ファイル、Makefile、ワークフロー、実行を決定して判定するスクリプト)を変更するすべてのプルリクエスト、およびプルリクエスト以外でのすべての実行において、3つの並列ジョブとして検査を実行します。各仕様には、必ず失敗することが求められるミュータントが文書化されています。不変条件は見せかけではなく、システムの安全性を支える重要なものです。

潜在的な障害代わりに起きること仕組み不変条件
古い読み取りによる上書き — エージェントが古いスナップショットに基づいて動作し、新しいバージョンを上書きする(2つのセッション、1つの plan.md)書き込みはフェイルクローズで拒否されます。書き込み側は reacquire() を実行し、現在のバージョンを読み取る必要がありますMESI の単一書き込み所有権と無効化SingleWriter、MonotonicVersion
同時更新の消失 — 2つの書き込み側が同じキーに書き込み、どちらも「成功」する必ず一方だけが成功し、もう一方には型付きの競合と回数制限付きの再試行が返されます。黙って破棄されることはありません楽観的なコミット CAS(write_cas)NoLostUpdate
回収済みゾンビによる書き込み — 停止していた書き込み側がクラッシュリカバリーによって回収された後、再開して古いコミットを反映する。バージョンは変わっていないため、バージョンチェックを通過してしまうコミットは拒否されます。悲観的ルート(CoherentVolume.write() と Claude Code フックの /hooks/post-edit)では、回収を示す reclaimed_by=reclaim_heartbeat を付けて commit_not_allowed が返されます。コミット CAS ルート(write_cas、write_cas_at、/hooks/post-edit-cas)では、型付きの stale_read_generation 競合が返されます悲観的ルートでは、権限チェックにより、権限が回収された保持者は拒否されます。読み取り世代フェンスは、アーティファクトの所有権エポックを回収時に更新し、コミット時にアトミックにチェックすることで、コミット CAS ルートでの適用を拒否します。また、回収がコミット途中で発生した場合は、悲観的ルートでも拒否しますNoStaleApply
ゾンビによる失効 — 無効化シグナルが発行され、それが適用されるまでに、対象として指定されたエージェントが回収され、権限を再取得して再読み込みしている。この状態で適用すると、シグナルの発行後に付与された権限まで失効してしまう。対象のバージョンは変わっていないため、バージョンチェックでは検出できない無効化は古いため破棄されます。対象は通知された状態より遅れていないため、権限は維持されますピアが発行する失効は、対象について最後に観測されたバージョンに固定されます。また、書き込み要求を終了させるすべての失効クラスの操作(スイープによる回収または解放)で所有権エポックが更新されます(ピアによる書き込みの取得では、どちらの比較対象も変わりません。その影響は権限状態によってエフェクトゲートでフェンスされます)NoZombieRevoke、NoSilentRevoke
回収済みゾンビによるエフェクト — 同じ回収済みの書き込み側が、失効された権限のもとで決定されたエフェクト(Webhook、デプロイ、作成された PR)を実行する。バージョンは変わっていないため、バージョンだけの再チェックを通過してしまうエフェクトはエフェクト境界で保留されます(StaleView)。バージョンは同じでも、所有権世代が異なりますgate() は実行前に、(version, ownership generation) のペアをアトミックに取得したスナップショットを再検証しますNoStaleAdmit
複数アーティファクトの不整合な読み取り(読み取りスキュー) — エージェントが複数のアーティファクトを1つずつ読み取る間にピアがコミットする。それぞれの読み取りは個別には最新でも、その組み合わせが同時に存在したことはないセッションの読み取りには固定された一貫性のあるスナップショットが使われます。コミットは固定されたベースに対して検証されます。期限切れのセッションは型付きの拒否でフェイルクローズし、ライブ状態へ黙ってフォールスルーすることはありません複数アーティファクトのスナップショットセッションNoReadSkewWithinCut、PinAlwaysRetained
停止した所有者がフリートをブロック — クラッシュしたエージェントが EXCLUSIVE を永久に保持するハートビート/TTL スイープが権限を回収します(デフォルトで有効。ベストエフォートで、レート制限付きです)クラッシュリカバリーのスイープ。オペレーターは /status?detail=full から、スイープが各書き込み権限を回収する場合の読み取り方法(方法)と、回収した権限(方法)を確認できますスイープの不変条件 I3–I6
この表はテストスイートによる機械的なチェックも受けます。パッケージに含まれる主張ラダー(ccs.testing.claim_ladder)は、上記の主張フレーズそれぞれを、それがなければ失敗するテストに対応付けます。この対応付けはテストの収集時に解決され、tests/conformance/test_claim_ladder.py によって双方向のドリフトが防止されるため、主張がその証明より長く残ることはなく、クロスホストの段階が証明なしで追加されることもありません。

適用範囲について、正確に述べます。 この保証が成り立つのは、1つのコーディネーター(1つのホスト)のもとで、コーディネーターを経由する書き込みに限られます。どちらのレジストリも、すべてのチェック・アンド・アクト処理の間、1つのロックを保持します。永続化される SQLite レジストリとインメモリのレジストリは同じ並行性の契約に従うため、組み込み側はどちらも複数のスレッドから操作できます。同一ホスト上での同一キーへの同時書き込みはカバーされていますが、未解決の問題が1つあります(#248)。あるピアの write_cas_at がコミットした後、ピアのバイト列がディスクに到達する前に通常の write() が行われ、書き込み側のボリュームがファイルの有効なビューを持っていない場合、その書き込みは失われます。ファイルを読んでいない場合、またはそのウィンドウ内でのみ読み取った場合(デフォルトの on_stale_read="allow" で read() が古いバイト列を返す場合でも、コーディネーターはその読み取りを拒否します)、あるいは実行した write_cas / write_cas_at が ViewWedged で終了した場合がこれに該当します。どちらの呼び出しも成功を報告しますが、ディスクにはピアのバイト列が残り、コーディネーターは失われた書き込みを最新バージョンとして記録します。その結果、そのファイルに対するすべての write_cas / write_cas_at は、通常の write() でファイルが置き換えられるまで ViewWedged を発生させます。一方、ピアのコミット前にファイルを読み取ったボリュームからの書き込みは拒否されます。クロスホストのフェンシングは需要に応じてロードマップに追加する予定です。必要な場合は、issue を作成してください。コーディネーターを完全に迂回する編集(エディターでの人手による編集、フォーマッター、再生成スクリプト)は、コンテンツハッシュチェックによって read() / write() の境界で検出されます。以下の外部編集ガード(コーディネーターを迂回する書き込み)を参照してください。これは TLA+ ではなくテストによって適用されます。バッチ CAS パスの境界はより限定的で、複数ファイルのアトミックな公開で詳しく説明しています。仕様、不変条件と実装の対応表、ミュータントのレシピは formal/tla/ にあります。

正確性が導入の決め手であり、トークンの節約はそれに伴って得られます。 書き込み時にはアーティファクト全体を再ブロードキャストする代わりに、約12トークンの無効化シグナルを公開するため、読み取りが多いフリートでは、すでに保持している状態に再びコストを払わずに済みます。

ワークロードエージェント数読み取り:書き込みヒット率節約率
計画(読み取り多め)412:175%69%
コードレビュー(中程度)38:360%47%
高頻度変更(書き込み多め)48:450%29%

実際の LangGraph グラフで測定しています。再現手順とユーザーガイドを参照してください。

これらは空間的な節約(1つのアーティファクトをより多くのエージェントで共有すること)です。時間的な側面、つまり1つのエージェントのターン間でソースが変化する場合については、事前登録済みの独自ベンチマーク TC-1(#116)があります。変更率の上昇に伴い、整合性ゲートによって再取得を何回回避できるかを示す、再現可能な節約レジームマップです。指標は 回避した再取得回数 です。これはあくまで代理指標であり、レジームマップであって、トークンや費用の請求額ではありません。python tools/run_cost_sweep.py で再現できます。固定済みの判定結果と数値(n=50 で PASS、クロスオーバー r≈0.31)は benchmarks/cost_preregistration.md にあります。v0.9.3 でリリースされました。

RAG と共有エージェントメモリ

RAG コーパスとエージェントのメモリは共有される可変状態なので、古い読み取り→書き込みによる更新の消失も発生します。そして、整合性のあるストアでも防げません。古くなるのはストアではなく、エージェントがキャッシュしたレコードのビューです。2 つのエージェントがレコードを v1 で読み取り、一方が v2 を書き込みます。もう一方は v1 のまま、v1 から計算した編集内容を書き込み、v2 を上書きしてしまいます。agent-coherence は読み取り側を最新の状態に保ちます。CCSStore は langgraph.store のドロップイン置換で、既存の構成に合わせて Mem0、Letta、LlamaIndex、ベクトルストア、または単純なファイルと組み合わせられます(ベクトルの保存もランキングも行いません)。ピアのコミットによって古いキャッシュビューが無効化されます(読み取り側のコヒーレンス)。古い内容の書き戻しそのものを防ぐには書き込み側の対策が必要です。その場合は、書き込みを CoherentVolume または write_cas 経由にします。

  • 実行可能で決定的なデモ(オフライン、キー不要):python -m examples.coherent_volume.main を実行すると、説明されている更新の消失が再現され、その後防止されます。
  • **適用範囲について:**コーディネーターを経由する書き込みは検出されます。コーディネーターを経由せずに変更される管理対象外の外部ソース(手動編集されたファイルや、別途実行された再インデックスなど)を自動監視するのは、ソースウォッチャーの対象です。ロードマップに含まれていますが、需要に応じて対応するもので、現時点では提供されていません。
  • 位置付けと FAQ:agent-coherence.dev/rag。
  • 📖 ユーザーガイド — インストール、名前空間の規約、戦略、可観測性、テレメトリー、使用例、API リファレンス全体
  • 🔎 RAG と共有メモリ — 検索コーパスとエージェントのメモリストアの一貫性、および実行可能な更新消失のデモ
  • 🗂️ 一貫性のあるワークスペース — CoherentVolume。プロセス間で共有されるプレーンファイル向けのデータプレーンアプライアンス
  • 🧱 基盤を持ち込む — CoherentRow / CoherentObject。すでに運用している Postgres の行または S3 オブジェクトに対して、同じ一貫性を提供します
  • ⏪ ワークスペースのバージョン管理と復元 — WorkspaceVersioner。ファイルと S3 が混在するワークスペースのチェックポイントを作成し、復元できるユーザーを指定して、メンバーごとの正確性を保って復元します
  • 🛡️ 外部編集ガード — 読み取り/書き込みの境界で、外部からの編集(人、フォーマッター、スクリプト)を検出し、オフライン中に発生したものも記録します(外部書き込みの検出)
  • 🔌 MCP サーバー — stale-write-guard-fs。Python 連携は不要で、あらゆる MCP クライアントに同じ保証を提供します
  • 🚦 副作用順序制御ゲート — gate()。エージェントが判断に使った入力状態(値と許可)に限り、その副作用を実行します。MCP(swg_gate)と HTTP(POST /hooks/effect-fence)でも同じ判定を提供します
  • 📸 複数アーティファクトのスナップショットセッション — 複数のアーティファクトを一貫した時点で読み取ります。読み取りの不整合は発生しません
  • 📦 複数ファイルのアトミックな公開 — atomic_publish。ファイルの一式をすべて成功またはすべて失敗で反映します。ファイル間の不整合は発生しません
  • 🤝 対象を指定した許可の引き継ぎ — ファイルを名前で指定した単一のセッションに引き渡します。バージョンが変わる、後継者が辞退する、または引き渡した側が取り消すまで、引き渡した側による後続の書き込みは handed_off として拒否されます。Claude Code、CoherentVolume、MCP、HTTP から利用できます。Python コーディネーターのみが提供します
  • 🔒 pre-edit で取得、または失敗 — "if_unheld": true を送る pre-edit は、別のエージェントがファイルを保持している間、保持者を示す other_holder として拒否され、許可の引き渡しは発生しません。これはオプトインした呼び出し元同士でのみ有効です。通常の pre-edit は、オプトインした保持者を引き続き置き換えます。追跡対象のパスに限られるため、strict-mode のパスでオプトインしてください。現時点ではこのフラグを送信するものはありません。リクエストに呼び出し元のプリンシパルを含む Python コーディネーターが提供します。また、プリンシパルが不要で、別のエージェントが 30 分以上保持している許可をリクエストが引き継げる Claude Code プラグインの Node コーディネーターは、0.6.0 より後のリリースから提供されます
  • 🧮 形式検証 — TLA+ 仕様、インバリアントと実装の対応表、ミュータントのレシピ
  • 🩺 ccs-diagnose CLI — コードを一切変更せずに、既存の LangGraph グラフで不一致のある読み取りを見つけます
  • 📊 競合結果カウンター — 型付き拒否が実際に発生した回数を数え、閉じた state.db からオフラインで読み取ります
  • 🧩 Claude Code プラグイン — 複数の Claude Code セッションで共有される文章ルール(CLAUDE.md、plan.md)のセッション横断的な一貫性
  • 🔍 一貫性が重要な理由 — LangGraph、CrewAI、AutoGen、Claude Agent SDK にまたがるギャップ
  • 🧭 MESI に着想を得たアプローチ — プロトコルが、文書化された各ギャップをどのように実装済みの機能に対応させているかと、その境界
  • 🪪 呼び出し元プリンシパル — 書き込み側がプリンシパルを登録すると、プリンシパルなしでそのセッションを指定したリクエストは、その許可を解放したり、その名前でコミットしたりできません。プリンシパルを登録しないクライアントは引き続き動作します
  • 🔐 セキュリティとサプライチェーン — キルスイッチ、ハッシュ固定インストール、アテステーション検証、脅威モデル
  • 📜 変更履歴 — バージョン履歴
  • 📄 arXiv の論文(2603.15183) — 形式プロトコル、TLA+ 検証、シミュレーション結果

仕組み

各共有アーティファクトはエージェントごとにローカルへキャッシュされ、コピーが最新であれば読み取りにはローカルキャッシュが使われます。書き込みはコーディネーターにコミットされ、コーディネーターは軽量な無効化シグナル(約12トークン)をピアに送信します。これにより、次の読み取りではアーティファクト全体を再ブロードキャストするのではなく、新しいバージョンが取得されます。一貫性はアーティファクトごとに単一ライター・複数リーダーで、古さの上限が定められています。ピアは次の読み取り時に再取得します。

同じ保証を提供する書き込み方式が2つあります。悲観的方式: EXCLUSIVE を取得してコミットします。参照している状態が古くなったライターは拒否され、reacquire() を呼び出す必要があります。楽観的方式: write_cas — 読み取り、計算、コミット時にCASを実行します。競合に負けた側には型付きの競合が返され、上限付きで再試行します。クラッシュ復旧はどちらの方式とも組み合わせられます。回収されたライターが後から処理を完了しても、バージョンが変わっていない場合でさえコミット時に拒否されます。悲観的方式のコミットは、ライターが付与された権限を保持していないため拒否されます。コミット時のCASは読み取り世代フェンスによって拒否されます。これは、停止した権限を回収するとアーティファクトの所有権エポックが増加するためです。読み取り側では、スナップショットセッションによって複数のアーティファクトにまたがる一貫した時点が固定されるため、複数アーティファクトの読み取りでバージョンが入り混じった状態を確認することはありません。

5つの同期戦略を標準で提供しています。lazy(デフォルト)、eager、lease(TTL ベース)、access_count、broadcast です。ワークロードの読み取りと書き込みの比率や、キャッシュされた読み取りをどの程度積極的に更新するかに合わせて選択してください。

アーキテクチャ

  • プロトコル(ccs.core、ccs.strategies)— 一貫性制御の状態機械と同期戦略を提供します。フレームワークへの依存はありません。
  • コーディネーター(ccs.coordinator)— ディレクトリの状態を追跡し、無効化を公開し、コミット時のCASを調停し、古くなった権限を回収する権限サービスです(クラッシュ復旧+読み取り世代フェンス)。
  • アダプター(ccs.adapters)— LangGraph、CrewAI、AutoGen 向けのフレームワーク統合(各約100行)に加え、実験的な OpenAI Agents SDK アダプター(Session キャッシュの一貫性制御+RunHooks)を提供します。
  • 一貫性のあるワークスペース(ccs.adapters.coherent_volume)— データプレーンのアプライアンスです。プロセス外のコーディネータークライアントにより、フレームワークを必要とせず、同じ保証をディスク上の通常のファイルに適用します。一貫性のあるワークスペースを参照してください。
  • MCP サーバー(ccs.mcp)— stale-write-guard-fs 標準入出力サーバーです。10個の swg_* ツールを介して、あらゆる Model Context Protocol クライアントに一貫性のあるワークスペースの保証を提供します。MCP サーバーを参照してください。
  • シミュレーション(ccs.simulation)— 障害注入に対応した、シナリオのベンチマーク用の決定論的なティック駆動エンジンです。
  • イベントバス(ccs.bus)— 無効化シグナルの転送手段です。現在はメモリー内/プロセス内(InMemoryEventBus)で動作します。複数ホストへのデプロイ向けのネットワーク対応トランスポート(Redis、Kafka、NATS、gRPC)は、需要に応じて対応するロードマップ上の項目です。

プロトコルの安全性プロパティ — 単一ライター、単調なバージョン管理、クラッシュ復旧スイープの不変条件、OCC における更新の喪失防止、回収フェンスによる古い適用の防止、エフェクトゲートによる古い要求の受け入れ防止、バージョン保持による回収済みデータの読み取り防止、スナップショットセッションによる固定時点内の読み取りスキュー防止、ワークスペース復元による部分復元の登録防止 — は、TLA+/TLC でモデル検査されています。CI は、TLA+ の入力(仕様と設定、Makefile、ワークフロー、実行の判定と評価を行うスクリプト)に変更を含むすべてのプルリクエスト、およびプルリクエスト以外のすべての実行で、9つの仕様を3つの並列ジョブに分けて実行します。

Coherent workspace:共有ファイルのデータプレーン

フレームワークアダプターはストアをラップします。CoherentVolume はもう一方の要素、つまりデータプレーンアプライアンスです。フレームワークを介さず、ディスク上の通常のファイルで共有 ワークスペース の一貫性を保つための構成要素です。アーキテクチャ上は、プロセス内ラッパーではなく、プロセス外のコーディネーターのクライアントです。ポリシーを書き込み、SQLite-WAL 経由でローカルコーディネーターを起動(または接続)し、読み取りと書き込みをコーディネーター経由で行います。コンテンツは実際のファイルシステム上に保持され、コーディネーターが管理する各ファイルについて保持するのは、MESI 状態、コンテンツハッシュ、バージョンのみです。別のプロセスにある同じワークスペースを指す同胞ボリュームを設定すると、同じコーディネーターに接続されるため、単一ホストのフリートで一貫したビューを共有できます。このフリート内のすべてのボリュームで、同一の managed glob を宣言する必要があります。接続するボリュームが稼働中のコーディネーターのポリシーに何かを追加することはないため、接続時に各ボリュームは宣言した glob をコーディネーターの厳格な追跡対象セットおよび無視対象セットと照合し、適用されていないものがあれば、その名前を示して接続を拒否します。glob が混在するフリートはサポートされません。黙って続行するのではなく、安全側に倒して失敗します。

from ccs.adapters.coherent_volume import CoherentVolume

vol = CoherentVolume(workspace_root, managed=("plans/**", "memory/**"))
data = vol.read("plans/plan.md")              # bytes — registers a SHARED view
vol.write("plans/plan.md", revise(data))      # stale view? denied fail-closed
data = vol.reacquire("plans/plan.md")         # recover: clear the stale view + mandatory fresh read

明示的な read / write / reacquire / write_cas API がサポート対象のプリミティブです(write_cas(path, make_content) は同一キーでの競合に対する楽観的な方法です。敗者には型付き競合が返され、更新が黙って破棄されることはありません)。書き換えを避けたいコード向けに、オプトインのデモ品質の open() shim も用意されています。これにより、管理対象パスに対する open() をボリューム経由で処理でき、既存の open() / pathlib 呼び出しは変更せずに一貫性を得られます。

from ccs.adapters.coherent_volume import coherent_workspace

with coherent_workspace(workspace_root, managed=("plans/**",)):
    text = open("plans/plan.md").read()       # registers a SHARED view
    open("plans/plan.md", "w").write(edit)    # stale view? raises out of close()

適用範囲について、正確に説明します。 通常の write() は、単一ホストのフリートが同じワークスペースを共有している場合、逐次的な古い読み取りから書き込みへの更新消失を防ぎます(A が v1 を読み取り、B も v1 を読み取り、A が v2 をコミットすると、B の古い書き込みは拒否され、B は再読み込みします)。同時に同一キーで競合した場合は write_cas を通じて処理されます。勝者は 1 つで、敗者には型付き競合が返され、再導出されます。更新が黙って破棄されることはありません。ボリュームを完全に迂回する編集は、境界で外部編集ガードにより検出されます。ただし、エージェントが最新のバイト列を再読み込みした後、古いバイト列をもとに計算したバッファを書き込むケースは検出されません。open() shim は利便性のためのものであり、契約ではありません。open() / pathlib によるテキストおよびバイナリの読み書きを扱いますが、raw os.open、サブプロセスによるリダイレクト、mmap、追記/更新モードには対応しません。これらは元の open() にそのまま委譲されます。実際に試すには、python -m examples.coherent_volume.main を実行してください(オフライン、決定論的、キー不要)。または位置付けと FAQをお読みください。

外部編集ガード:コーディネーターを経由しない書き込み

協調処理の対象となるのは、オプトインした書き込みだけです。しかし、実際のワークスペースは「外部」からも編集されます。人がエディターでファイルを修正したり、フォーマッターが書き換えたり、CI スクリプトが再生成したりします。ガードがなければ、次のエージェントの書き込みがその編集を気付かれないまま上書きし、次のエージェントの読み取りはコーディネーターが一度も見ていないバイト列を基に気付かれないまま処理を進めます。CoherentVolume はコンテンツハッシュのチェックで、両方の境界をガードします。

  • 書き込み境界 — デフォルトで有効です。 書き込む前に、ボリュームは管理対象ファイルのディスク上のバイト列が、最後に読み取ったとき、または書き込んだとき以降、外部で変更されていないかを確認します。変更されていた場合、外部編集を上書きする代わりに書き込みは StaleView を送出します。reacquire()(再読み取り → 再導出 → 再書き込み)で復旧してください。CoherentVolume(on_stale_write="allow") を指定すると、ガードを無効にして最終書き込み優先の動作に戻せます。

  • 読み取り境界 — オプトインです。 CoherentVolume(on_stale_read="raise") を指定すると、外部でバイト列が変更された管理対象ファイルを再読み取りした際、他の状態の計算に使われていないバイト列を返す代わりに StaleView を送出します。厳格モードでは、コーディネーターがサーバー側でも同じチェックを適用します。ボリュームが直前に自ら書き込んだバイト列を拒否することはありません。コミットからディスクへの書き込みまでに生じる無害な遅延時間は認識され、無視されます。on_stale_read が適用されるのは read() だけです。設定にかかわらず、バージョン付きの読み取りである read_with_version()、read_with_version_generation()、MCP の swg_read は、ボリューム自身のコミット後の同じ遅延時間を除き、報告するバージョン時点でコーディネーターのコンテンツと一致しない管理対象ファイルを拒否します。その場合は reason が split_pair の StaleView を送出するため、他の古いビューと区別できます(swg_read はバージョンなしで stale_view 拒否を返します)。これにより、比較交換が照合するバージョンを返してしまうことを防ぎます。

vol = CoherentVolume(workspace_root, managed=("plans/**",), on_stale_read="raise")
# a formatter rewrites plans/plan.md out-of-band …
vol.write("plans/plan.md", revised)   # StaleView — the foreign edit survives
fresh = vol.reacquire("plans/plan.md")  # recover: fresh read, re-derive, re-write

適用範囲について。 これはボリュームの読み取り/書き込み境界におけるコンテンツハッシュチェックです。ベストエフォートの時点検出であり、ファイルシステムをインターセプトするものではありません。ボリュームを経由しない書き込みは、その場でブロックされるのではなく、そのファイルに対する次のボリュームの読み取り/書き込み時に検出されます。管理対象外の外部ソースの監視は、需要に応じて対応するロードマップ項目です。その次の読み取りまたは書き込みまでに何が起きたかは、現在は確認できます。Python コーディネーターのスイープ処理は、協調対象のファイルのうちディスク上で変更されたものを git に問い合わせ、説明できない変更をそれぞれ foreign として記録します。これはカウントされますが、拒否はされません。また、ccs.diagnose.foreign_writes.read_foreign_write_report は、閉じた state.db から集計結果をオフラインで読み取ります(外部書き込みの検出 — 誰が知らない間に書き換えたのか)。監視対象は git が追跡するファイルに限られ、コーディネーターのルートが git 作業ツリーの最上位にある場合に限られます(#241)。これらのガードはテストで検証され、TLA+ では検証されません。モデル検査された不変条件が対象とするのはプロトコルの状態機械であり、ディスク上のバイト列ではありません。ガードの対象は read()/write()(およびそれらをラップする MCP ツール)です。CAS の経路では代わりにバージョンをチェックします。write_cas/write_cas_at は外部編集があれば引き続きフェイルクローズします(コンテンツをチェックする比較対象の読み取りが停止し、上書きは行われません)。ただし、複数ファイルの atomic_publish はディスク上のコンテンツを一切チェックしません。適用範囲の注記を参照してください。

MCP サーバー:stale-write-guard-fs

Model Context Protocol を使用するエージェント(Claude Code、Cursor、またはカスタムランタイム)にも、Python との統合を一切必要とせず、同じ保証を提供します。stale-write-guard-fs は CoherentVolume をラップする stdio MCP サーバーで、協調対象のファイルアクセスを 10 個のツールとして公開します。

pip install "agent-coherence[mcp]"
{
  "mcpServers": {
    "stale-write-guard-fs": {
      "command": "stale-write-guard-fs",
      "env": { "SWG_ROOT": "/path/to/shared/workspace" }
    }
  }
}
ツール機能
swg_read追跡対象の読み取り — エージェントがファイルを確認したことを記録します。ディスク上のバイト列がコーディネーターの記録と異なる場合、読み取りはバージョン情報なしで stale_view の拒否を返します。まず数秒間、swg_reacquire + swg_read を再試行してください。ピアのコミットがまだディスクに反映されていない可能性があるためです。それでも拒否される場合は、ファイルがコーディネーターの外部で変更されています(範囲外の編集、またはディスクへの書き込みに失敗したコミット)。再取得した内容を swg_write して記録してください
swg_write保護された書き込み — 古いビューまたは外部からの編集がある場合、recover: reacquire を含む型付きの stale_view 拒否を返し、黙って上書きすることはありません
swg_reacquire復旧 — 拒否の後、古いビューをクリアし、新たに読み取ることを必須とします
swg_write_cas同じキーへの競合を伴う同時書き込み向けの、バージョン確認付き単発書き込み
swg_gate副作用フェンス — 取り消し不能な外部アクション(Webhook、デプロイ、PR の作成)の直前に、swg_read から得た (version, owner_generation) のペアを再確認します。値が変わった場合、読み取りに使った権限が回収された場合、またはピアの書き込み要求が優先されていた場合は拒否します(この場合、比較対象のどちらも変わりません。フェンスは権限が引き続き有効かどうかも再確認します)
swg_status3 状態の連携ヘルス情報:on / off / unknown。さらに、このセッションの session_agent_id と、パスを引き継ぐ相手として別のセッションが指定している ID を返します
swg_transferこのセッションが持つ 1 つ以上のパスの権限を、指定した session_agent_id を持つセッションに引き渡します。引き渡しが終わるまで、このセッションによる対象パスへの swg_write / swg_write_cas は、型付きの handed_off 拒否を返します(対象を指定した権限の引き渡し)
swg_accept後継者として、パスに書き込まずに引き渡しを受け入れます
swg_decline後継者として、引き渡しを拒否します。引き渡し元は再びそのパスに書き込めるようになります
swg_withdraw引き渡し元として、ユーザーまたはホストから明示的な指示があった場合に限り、引き渡しを撤回します
POST /hooks/effect-fenceツールではありません — swg_gate と対になる HTTP インターフェースです。 コーディネーターは、MCP も Python も介さずに HTTP リクエストを送信できるクライアントに対して同じフェンス判定を返します。また、コンテンツの要求がないケースに対する判定を返せる唯一のインターフェースです。HTTP 経由の副作用フェンスを参照してください

サーバーはセッションごとに 1 つのワークスペースに接続します(SWG_ROOT。デフォルトは作業ディレクトリです。SWG_MANAGED — カンマ区切りの glob リスト — で対象を絞らない限り、ワークスペース全体を保護します)。パス traversal と、コーディネーター自身の状態ディレクトリへのアクセスを拒否し、I/O エラーが発生した場合は安全側に倒して処理を中止します。拒否は、型付きの機械可読なペイロードで返されます。エージェントは recover: reacquire を解析して自ら復旧できるため、無闇に再試行する必要はありません。コーディネーターから応答がなくなると、すべてのツール呼び出しは recover: stop_and_report を含む coordinator_unavailable を返します。エージェントは処理を停止し、別の経路では何も書き込まず、サーバーの再起動が必要であることを報告します(詳細)。読み取りへの応答中、または以前の swg_write が保持した書き込み権限の解放中に障害が起きた場合、呼び出しは coordinator_failed を返します。エージェントはもう一度だけ試し、その後停止して報告します。コミットに対してこの応答が返った場合や、HTTP 5xx が返った場合は、書き込みが反映されている可能性があるため、commit_unconfirmed を返します(詳細)。赤から緑へのデモを実行します:python -m examples.mcp_stale_write_guard.main(オフライン、決定論的、キー不要)。

正直な適用範囲。 包み込むボリュームと同じ契約です。単一ホスト、管理対象パス、協調動作が前提です。ファイルアクセスをこのツール経由で行うエージェントを保護しますが、迂回して行われた編集は検知できません(そうした編集は、そのファイルに対する次のツール呼び出し時に外部編集ガードによって検知されます)。

Effect-ordering gate

エージェントはファイルを上書きするだけではなく、以前に読み取った入力から計算した副作用(デプロイ、PR、シェルコマンド)も実行します。その間に入力が変わっていた場合、副作用は古い状態に対して実行されます。gate() はこの時間差を狭めます。判断時点で入力の(version, ownership generation)のペアを取得し、副作用を実行する直前にペアを再読み込みして、その再読み込み時点でも両方が変わっていない場合にのみ実行します。そうでなければ、実行前に副作用を保留します。2つの比較値は、それぞれ異なる障害を検出します。versionは入力を変更したピアを検出し、generationはバイト列が変わっていない間に、コーディネーターのスイープによって入力を読み取った際の許可が回収されたことを検出します。リースが判断の途中で期限切れになった停滞中のエージェントが、取り消された権限で副作用を実行してしまうケースも、versionチェックだけでは見逃します。1回の失効では、どちらの比較値も変わりません。ピアが悲観的な書き込み取得を行うと、コミットがまだない状態で保持者の許可が奪われます(versionは変わりません)。また、エポックも増加しません(このトリガーは意図的に増加条件から除外されています)。ゲートは、再読み込み自体が有効な許可のもとで行われていることも要求し、このケースを防ぎます。先取りされた保持者による再読み込みは stale を返すため、副作用は保留されます。この再読み込みは検証読み込みであり、コーディネーターは許可を再付与しないため、保留はレベル検知型です。呼び出し元が許可を再取得するまで、単に再試行しても再び保留されます。この3つ目の条件はテストで検証されます(claim ladderがこれを固定します)が、TLA+では検証されません。モデル検査済みのNoStaleAdmitが対象とするのは、比較値のペアです。

from ccs.adapters import CoherentVolume, gate

vol = CoherentVolume(workspace_root, managed=("deploy/**",))

# fires run_deploy(plan) only if deploy/config.txt is unchanged since decide() read it;
# else raises StaleView before the deploy runs — reacquire() and re-decide.
gate(vol, "deploy/config.txt", decide=plan_deploy, effect=run_deploy)

通常のPythonなので、同じ呼び出しを変更せずにLangGraphのノード、CrewAIのタスク、または単独のスクリプトに組み込めます。

3つのインターフェース、1つのルール。 gate()はフェンスのPythonでの表現です。swg_gateはMCP版であり、POST /hooks/effect-fenceはそれら以外の方法で通信するクライアントにも、同一の判定を返します。同じ問いに対して、同じ分類と同じ保留理由が返されます。これらの理由は、各理由を確定する条件と、それぞれの回復方法とともに、effect-fence contractで公開されているワイヤー用語です。ただし、1つの条件はすべてのインターフェースで同じではありません。content_claim_absentを報告できるのはHTTPルートだけです。これは、コーディネーターが成果物に対するコンテンツ要求を一切記録していないケースです。PythonとMCPのインターフェースが読み取るのは、コーディネーター自身の記録ではなくハッシュの比較なので、「要求が一致する」と「要求が存在しない」は、どちらのインターフェースにも同じ値として届きます。

適用範囲について。 このゲートは副作用の実行順序を制御しますが、ロールバックはしません。副作用の前に実行され、実行後に取り消すことはないため、外部に作用する副作用には、狭めることはできても完全には閉じられない、再読み込みから実行までの間隔が残ります。これは単一ホスト向けであり、協調的な仕組みです。エージェントが明示的に利用します。どちらの比較値も、確認できない場合は実行を許可しません。versionを確認できない場合(読み込みの劣化時)や、generationを確認できない場合(strictモードでの拒否時、またはv0.14.0より古いバージョンのようにコーディネーターがgenerationを報告しない場合)は、実行せずにHOLDします。純粋な書き込みの副作用には、原子的で時間差のない経路であるvol.write_cas_at(path, expected_version, content)を直接使用してください。相互に整合する複数の入力を一度にゲートする操作は、この単一入力のラッパーではなく、コーディネーター上のsnapshot-session操作です。次のコマンドで実行できます。python -m examples.effect_gate.main(オフラインで決定的に動作し、キーは不要)。捕捉される古い状態での実行を確認するには、--baselineを追加します。python -m examples.gate_effect_ordering.mainでは、回収されたリースのケース(versionは変わらず、権限は取り消され、デプロイが保留される)も確認できます。

複数アーティファクトのスナップショットセッション

gate() は1つの入力を保護します。しかし、プラン、設定、メモリファイルなど、複数のアーティファクトを1つずつ読み取るエージェントは、一貫性のない組み合わせを目にする可能性があります。たとえば、ピアのコミット前の plan.md と、コミット後の config.json が混在する場合です。個々の読み取りはすべて最新でも、読み取った一連の内容が同時に存在したことはありません(読み取りスキュー)。スナップショットセッションはこの隙間を埋めます。指定したアーティファクトの一貫したスナップショットを単一時点で取得して固定し、ピアが書き込みを続けている間も、セッション内のすべての読み取りにそのスナップショットを使います。

(CoherentVolume が起動するものと同じ)稼働中のコーディネーターに対して、HTTP 経由で実行する場合:

POST /session/begin      {session_id, read_set: ["plans/plan.md", "config/app.json"]}
                         → {session_token, cut: {path: version}, …}
POST /session/read       {session_id, session_token, path}
                         → the artifact at its PINNED version — never a newer one
POST /session/commit     {session_id, session_token, path, content}
                         → wins only if no peer moved the artifact since the cut
POST /session/heartbeat  {session_id, session_token} — keep the session's lease alive

または、プロセス内で実行する場合:CoordinatorService.begin_session(read_set=…, owner=…) → session_read(…) / session_commit(…)。スナップショットは不透明なハンドルではなく、確認可能な {artifact: version} マップです。セッションで固定されているバージョンを正確に確認できます。

設計上、フェイルクローズです。固定された読み取りセットに含まれていないアーティファクトの読み取りは、型付きの拒否によって拒まれます。ライブ状態から黙って提供されることはありません。セッションの有効期間には上限があり、ハートビートリースによって維持されます。ハートビートが途絶えたセッション、またはコーディネーターの再起動によって失われたセッションは無効化されます。その後の読み取りは、現在の状態へ黙ってフォールバックするのではなく、エージェントにセッションの再確立を促す、型付きの「セッションが無効です」という拒否を返します。モデル検査済みです:NoReadSkewWithinCut および PinAlwaysRetained(formal/tla/Snapshot.tla)。

適用範囲について、正直に説明します。 これは読み取りスキュー、つまりアーティファクト間で一貫性のない読み取りが起きるのを防ぎます。書き込みスキューの防止機能は追加されません。コミットは、write_cas と同じ楽観的 CAS を使い、固定されたベースに対してアーティファクトごとに検証されます。そのため、同じスナップショットを読み取って異なるアーティファクトに書き込む2つのセッションは、引き続き交錯する可能性があります。コーディネーターは1つ、ホストも1つです。コーディネーターがバージョンの本文を保持している場合は、固定されたバイト列を直接提供します。そうでない場合は、型付きシグナルとして固定されたバージョンとコンテンツハッシュを返し、呼び出し元が自身のデータプレーンからバイト列を取得します。

アトミックな複数ファイルの公開

write_cas_at は、ファイルに変更がなければそのファイルを反映します。しかし、エージェントは一貫性を保つ必要のある複数のファイル(計画とそのマニフェスト、複数のファイルに分割された設定など)を編集することがよくあります。その場合、ファイル間で食い違いが生じ、一方のファイルが、すでに変更された別のファイルを参照する状態になることなく、すべてを同時に反映するか、まったく反映しないかにする必要があります。atomic_publish は、この全か無かのバッチ処理です。

from ccs.adapters import CoherentVolume

vol = CoherentVolume(workspace_root, managed=("proj/**",))

# lands BOTH files iff each is still at the version the agent read; otherwise the
# WHOLE publish is held (StaleView / CasVersionConflict) with NO file written.
versions = vol.atomic_publish([
    ("proj/plan.md",     plan_version,     new_plan_bytes),
    ("proj/manifest.md", manifest_version, new_manifest_bytes),
])   # -> {"proj/plan.md": 2, "proj/manifest.md": 3}

各ファイルのバージョンがすべて進むか、どれも進まないかのいずれかです。変更されたファイルが1つでもあればバッチ全体が保留されるため、食い違いのあるコミット状態には決して到達しません。これは formal/tla/AtomicPublish.tla の NoPartialPublish 不変条件として形式的に規定されています。AtomicPublish.tla は規定され、意味的に検証されていますが、CI の検証対象には含まれていません。CI の制限時間内に収束する有界エンコーディングができるまで、対象から外されています。そのため、NoPartialPublish はプッシュのたびにモデル検査される9つの不変条件には含まれていません。単一ファイルの公開には直接 CAS パスが使われます。複数ファイルの公開ではスナップショットセッションを開き、チェックするバージョンを同一時点で取得します(メンバーの読み取りが、他のメンバーのコミットをまたぐことはありません)。実行するには、python -m examples.atomic_publish.main を実行してください(オフラインで動作し、決定的な結果が得られ、キーは不要です)。防止されるファイルごとの食い違いを確認するには、--baseline を追加します。

適用範囲について、正確に説明します。 全か無かの保証は、コーディネーターのコミットに対するものです。NoPartialPublish が保証するのはこの点です。ディスクへの反映はコミット後に行われ、ベストエフォートです。各ファイルは一時ファイルにステージングされてから所定の場所にリネームされるため、ディスク障害があればリネーム開始前に失敗し、ディスク上の内容は一様に古いままです。リネームが途中で失敗した場合は、反映されたファイルを正確に列挙する型付きエラー PublishMaterializationError が発生します。何も公開されなかったと誤解させる、単なるエラーにはなりません。これにより複数ファイルのディスク反映に伴う不整合の時間幅は縮まりますが、リネームの途中でクラッシュする可能性を完全になくすことはできません(POSIX には複数ファイルをアトミックにリネームする機能がありません)。このエラーが発生した場合、コーディネーターの状態はディスクより先に進んでいます。反映されなかったファイルには、公開したバイト列を使って write() で再度書き込んでください。それまでは、そのファイルのバージョン付き読み取りは拒否されます。これは単一ホスト向けであり、協調的な利用を前提としています。複数ファイルのパスでは、取得からコミットまでの短い時間差(セッションを開く処理)も発生します。その間に別の処理が先に成功した場合、公開を食い違った状態で反映するのではなく、保留します。これはファイルセットの全か無かの公開であり、すでに外部に出た影響をロールバックするものでも、セッション間の書き込みスキューを防止するものでもありません。また、検出するのはバージョンの変化です。バージョンを進めるのはボリューム経由の書き込みだけなので、ボリュームを完全に迂回する編集(エディターでの人手による編集、フォーマッター、ファイルを直接書き込むスクリプト)は、複数ファイルの公開では検出されません。そのバッチはコミットされ、変更を上書きします。単一ファイルの公開と通常の write() は、同じ編集があった場合に外部編集ガードによってフェイルクローズします。atomic_publish は、競合するすべての書き込み元がボリューム経由となるファイルセットにのみ使用してください。

BYO 基盤:すでに運用しているストア上のコヒーレンス

CoherentVolume はディスク上のファイルにコヒーレンスを提供します。しかし、共有エージェント状態は、すでに運用しているストア(Postgres の行や S3 オブジェクトなど)に存在することもよくあります。BYO 基盤バインディングは、そのストア上でも同じコヒーレンスを実現します。コーディネーターが保持するのはメタデータ(バージョン、エージェントごとの MESI、固定幅の content_hash、不透明な基盤トークン)だけであり、バイト列は保持しません。

from ccs.adapters.coherent_row import CoherentRow      # pip install "agent-coherence[coherent-row]"
from ccs.adapters.coherent_object import CoherentObject  # pip install "agent-coherence[coherent-object]"

# agent A reads a row it will edit over several steps
row = CoherentRow(dsn=..., table="workspaces", artifact_id="ws-42")
data, token = row.read("ws-42")
# ... meanwhile agent B commits a new version through the binding ...
# A's next binding-mediated read/act is DENIED before A writes:
row.commit("ws-42", expected_token=token, new_bytes=revised)  # -> StaleView; reacquire() and re-decide

基盤独自の条件付き書き込み(UPDATE … WHERE version=?、S3 の If-Match)に対する価値は、単なる CAS では書き込み時に A の書き込みを拒否するのに対し、バインディングは A が動作する前にキャッシュされたビューが古くなったことを通知する点です。ファイル(CoherentVolume)やストアキー(CCSStore)と同じ型付き語彙を使い、行、オブジェクト、ファイルにわたる単一のコヒーレンスサーフェスを提供します。宣言型の Coherence Manifest により、各アーティファクトを基盤と、正直な保証ティアに結び付けます。認証情報は参照(secret-file: / aws-default)として指定し、リテラル値は使いません。また、接続先は SSRF の制約下に置かれます。

適用範囲について、正直に説明します。 v1 では、2 つの native-CAS バインディング(Postgres + S3)と、アクションバックエンド(Slack への投稿、Gmail の送信など)向けの forward-only ティアを提供します。後者はアクション入力の鮮度確認のみで、CAS は行いません。価値は無効化してから動作すること + 基盤をまたいだ一貫性です。基盤上の読み取り世代フェンスは文書化されたロードマップ項目であり、提供済みとは主張しません。v1 の OCC ライターは admit-on-absent + version-CAS に従います。単一ホストかつ協調的な環境が前提です。基盤自体が分散型(S3、マネージド Postgres など)の場合、更新の消失を防ぐ保証は基盤自体が提供するものであり、このレイヤーの有無によって変わりません。実行方法:python -m examples.coherent_row.main / examples.coherent_object.main(オフライン、決定論的、キー不要 — インメモリ基盤の代替実装を使用します。本番環境では、同じバインディングを実際の Postgres / S3 に向けます)。完全な API、バインディングごとの最小権限設定、保証ティアの正直な一覧については、BYO substrate bindings を参照してください。

ワークスペースのバージョニングと復元

ここまでの機能は、エージェントが共有状態を書き換えている間、その状態を安全に保ちます。ワークスペースのバージョニングは事後の問題に答えます。エージェントがバックエンドにまたがるワークスペース(ディスク上のファイルや S3 内のオブジェクトなど)を変更したものの、処理に失敗し、ワークスペースを元に戻したい場合に、何を復元でき、何を復元できないかをメンバーごとに正直に示します。

from ccs.adapters.workspace import WorkspaceVersioner

wv = WorkspaceVersioner(service=service, owner=owner, file_resolver=resolver)
wv.add_file_member(source, "ws/notes.md")
wv.add_object_member(binding, "reports/summary.txt")   # a CoherentObject (S3)
wv.add_forward_only_member("actions/deploy-step")      # named, not captured

checkpoint = wv.checkpoint("before-migration")         # pins on by default
report = wv.restore(checkpoint.record.checkpoint_id)   # per-member terminal truth

チェックポイントは、ネイティブなバージョンポインター(S3 の versionId、ファイルのコーディネーターバージョン)とコンテンツフィンガープリントをまとめた名前付きマニフェストです。バイト列の別コピーは作成しません。チェックポイントはスキューを明示したカットとして取得されます。取得期間が記録され、隠されることはありません。その期間内に状態が変化したメンバーにはフラグが立ち、黙って一貫性のない状態になることはありません。取得時に存在しないメンバーは不在として記録されるため、復元には削除処理も含まれます。復元では、終了条件に従い、メンバーごとに条件付き書き込みを 1 回実行します。そのため、必ず完了し、メンバーごとに凍結されたレポートが作成されます。状態は restored(取得したバイト列が現在の状態に書き込まれ、レポートにはメンバーごとに、その書き込みによって破棄された内容が記載されます)、converged(現在の状態がすでに一致しています)、conflict(稼働中の別のライターが優先されました。再試行は上限付きで行われ、ライブロックは発生せず、そのメンバーの現在の状態が維持されます)、target_lost(取得したバージョンが失われています。代替バージョンを使わず、その旨を報告します)、held_unconfirmed、または forward_only_skipped のいずれかです。

各メンバーには、正直な復元ティアが付与されます。restorable(バージョン管理された S3 で、お客様のバケット内の法的ホールドによって保護されます)、restorable-unpinned(現時点では履歴がありますが、期限切れになる可能性があります)、または forward_only(記述はされますが復元できません。復元時ではなく取得時に明示されます)です。永続的な真実は (restore_tier, pin_state) の組み合わせです。(restorable, unpinned) は単なる約束としてではなく、保証されていない状態として表示されます。シェルからスクリプトで実行でき、終了コード 3 は復元が完了したので、レポートを確認してくださいという意味です。

agent-coherence-workspace checkpoint before-migration --file notes.md --file plan.md
agent-coherence-workspace status <checkpoint-id>     # (restore_tier, pin_state) per member
agent-coherence-workspace restore <checkpoint-id>    # 0 no absorbed outcome · 3 concluded with absorbed outcomes

チェックポイントを復元できるユーザーと、ピンの解放。 チェックポイントには作成したユーザーが記録されますが、復元時にそのユーザーかどうかは確認されません。多くの場合、別のユーザーが復元できることが目的だからです。復元できるユーザーを制限するには、チェックポイントの作成時に受信者を指定します。wv.checkpoint(name, receiver=<their owner id>) を使うか、POST /workspace/checkpoint で receiver_session_id を指定してください。それ以外のコントローラーは not_the_receiver で拒否されます。受信者を指定しているかどうかにかかわらず、復元を登録した最初のコントローラーがチェックポイントを確保します。その後、ほかのコントローラーは already_registered で拒否されます。確保したコントローラー自身による再試行は引き続き冪等ですが、確保は解除されません。そのコントローラーが停止した場合は、そのコントローラーで再開するか、新しいチェックポイントを作成してください(現在のワークスペースの状態が記録されます)。登録時には、チェックポイント自体との照合も行われます。チェックポイントに記述されていないパス(not_a_checkpoint_member)や、記録されたものと異なるバイト列(fingerprint_mismatch)は、変更が加えられる前に拒否されます。プロセス内では、restore() は何も書き込む前に受信者と確保状態を確認し、CheckpointRegistrationRefused を送出します。CLI は終了コード 2 を返します。wv.release_checkpoint(checkpoint_id) は、チェックポイントが保持するピンを解放します。同じレジストリ内の別のチェックポイントが引き続き依存している S3 の保護は維持され、マニフェストは残ります。この操作は取り消せず、Python でのみ実行できます。各 S3 メンバーについては、先に、保護を設定したバインディングとキーを使って、そのバージョナー上で再宣言する必要があります。詳しくはガイドをご覧ください:チェックポイントを復元できるユーザーとピンの解放。

スコープについて、ありのままに。 単一ホストのコーディネーターです。チェックポイント、ピン、復元の進行状況はローカルのコーディネーター状態に保存され、ホスト間の動作は保証しません。復元の対象はアーティファクトであり、作用ではありません。ファイルやオブジェクトは元に戻りますが、送信済みのメッセージは元に戻りません。ファイルメンバーは検出のみ(no-arbiter)です。競合する外部ライターを調停できるのは、ネイティブの条件付き書き込み機能を持つバックエンドだけです。また、ファイルメンバーはコーディネーターの保持期間に依存し、バージョンごとの保護はありません。そのため、ファイルメンバーのティアは restorable-unpinned のままです。バージョンが保持期間を過ぎた場合、別の内容を復元するのではなく、target_lost として通知されます。復元は古いバイト列を伴う前方へのコミットです。バージョンは必ず増加し、履歴が書き換えられることはありません。また、復元はマージではありません。キャプチャ後に内容が変わったメンバーは上書きされ、後から加えられた内容は失われます。この処理では、その書き込みを拒否しません。代わりに、その記録が残ります。メンバーごとに、破棄されたバージョンとダイジェスト、およびその情報を読み取れた復元段階が記録されます。さらに restore --exit-nonzero-on-discarded-content を指定すると、事後にその実行の終了コードを 0 以外にできます。終了コード 0 が意味するのは、吸収された結果になったメンバーがなかったことだけです。何も上書きされなかったという意味ではありません。S3 メンバーのキャプチャと復元は Python API を通じて行います。バインディングには、CLI が保持しない認証情報が含まれるためです。実行するには、python -m examples.workspace_versioning.main を使います(オフライン、決定的、キー不要)。先に失われる内容を確認するには --baseline を追加してください。python -m examples.session_handoff.main は、チェックポイントと復元を介して作業を引き継ぐ 2 つのセッションを別プロセスとして実行します。最初のセッションがまだ書き込み中の場合に発生する、範囲が限定された conflict も確認できます。誠実性モデルの詳細(ティア、ピンの状態、保持期間に関する注意事項、終了コードの仕様)については、ガイド § Workspace versioning & restore をご覧ください。

ステータス

v0.15.0 をリリースしました — 対象を指定した引き継ぎ、呼び出し元プリンシパル、HTTP 経由のエフェクトフェンス、そして復元できる人を明示するチェックポイント。 セッションは、指定した1つのセッションにファイルを引き継げるようになりました。バージョンが変わる、後継セッションが引き継ぎを拒否する、または引き渡し元が取り消すまで、引き渡し元自身によるそのファイルへの書き込みは handed_off として拒否されます。引き継ぎは Claude Code、CoherentVolume、MCP サーバー(新たに4つのツールが加わり、計10個)、HTTP から利用できます。Claude Code のフッククライアント、CoherentVolume、MCP サーバー、および BYO-substrate バインディングの背後にあるコーディネーターセッションは、各セッションの呼び出し元プリンシパルを自動的に申告するようになりました。一度申告されると、コーディネーターは、別の書き手の作業を変更できるルートで、呼び出し元プリンシパルを指定せずにそのセッションを指定したリクエストを HTTP 400 で拒否します。これにより、プロトコルに準拠するクライアント同士を区別できますが、セキュリティ境界ではありません。POST /hooks/effect-fence は、あらゆる HTTP クライアントにエフェクトフェンスの判定を返します。コーディネーターがコンテンツを記録したことのないアーティファクトに対する8つ目の保留理由 content_claim_absent も追加されました。復元の登録は、チェックポイントのメンバーと取得済みフィンガープリントに照らして検証されるようになりました。チェックポイントでは、復元を許可するコントローラーを1つ指定でき、最初に復元を登録したコントローラーが、そのチェックポイントの復元権限を確定的に取得します。復元結果には上書きされた内容が報告され、WorkspaceVersioner.release_checkpoint でチェックポイントのピンを解放できるようになりました。オペレーターは /status?detail=full で、スイープによって各書き込み許可がいつ回収されるか、回収された許可、各アーティファクトに最後に書き込んだユーザーを確認できます。また、すべての /status ビューで registry_schema_version が報告されるようになりました。Claude Code プラグインの Node コーディネーターは、新しいエンドポイントを一切提供しません。引き継ぎ、呼び出し元プリンシパル、エフェクトフェンスの各ルートには 404、/status?detail=full には 501 を返し、/status に registry_schema_version は含まれません。

0.14.1 からのアップグレード。 レジストリスキーマは7から10に移行し、ダウングレード用の移行はありません。このリリースがワークスペースの state.db またはワークスペース CLI の workspace.db を開くと、0.14.1 ではそのファイルを使用できなくなります。一部の応答と戻り値の型も変更されています。たとえば、/status はオペレーターティア未満では agent_name に null を返し、write_cas() / write_cas_at() は CasCommitResult を返します。コーディネーター、クライアント、Claude Code プラグインをまとめてアップグレードし、このリリースにはプラグイン 0.6.0 以降を組み合わせてください。ガイドの0.14.1 からのアップグレードに、互換性を破る変更をまとめています。

全バージョンの履歴は CHANGELOG.md、タグ付きアーティファクトはリリース一覧を参照してください。アルファ版 — API は v1.0 より前に変更される可能性があります。

論文

Token Coherence: Adapt​​ing MESI Cache Protocols to Minimize Synchronization Overhead in Multi-Agent LLM Systems arXiv:2603.15183

BibTeX
@article{parakhin2026token,
  title   = {Token Coherence: Adapting MESI Cache Protocols to Minimize
             Synchronization Overhead in Multi-Agent LLM Systems},
  author  = {Parakhin, Vladyslav},
  journal = {arXiv preprint arXiv:2603.15183},
  year    = {2026}
}

コミュニティ

質問、実体験、アイデアはディスカッションで歓迎します。マルチエージェントのワークフローで古い読み取りによるバグに遭遇した場合は、イシューを作成してください。ぜひお話を聞かせてください。

ライセンス

Apache-2.0。LICENSEを参照してください。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。