マルチハーネスアダプター設計の内側
Apothem の HarnessAdapter プロトコルと、1 つのプロファイルを 17 のハーネスへ物質化する 3 つのアダプタークラスの技術的な深掘り。
Apothem は、1 つの共有プロファイルを 17 のハーネスネイティブな設定へと 物質化します。17 のターゲット、17 のベンダー形式、17 のインストール位置 —— そして、オペレーターと CLI の双方が頼りにする 1 つの統一されたインターフェイス。 本稿では、アダプター層がその契約をどのように束ね続けているのかを説明します。
HarnessAdapter プロトコル
各ハーネスアダプターは、構造的な HarnessAdapter プロトコルを実装します。
プロトコルは、CLI とベンダー固有の詳細とのあいだの継ぎ目です。CLI が呼び出す
操作と、CLI が読み取るメタデータを宣言します。
name— アダプターの登録された識別子。output_path— 物質化された設定がディスク上に着地する場所。install— ハーネスネイティブな設定をレンダリングして書き込む。uninstall— 共有プロファイルには手を触れずに、物質化された設定を取り除く。is_installed— そのハーネスの設定が存在するかを報告する。verify— プロファイルとディスク上の設定とのあいだのドリフトを検出する。
契約は typing.Protocol であるため、アダプターは継承ではなく形によってそれを
満たします。CLI は、pyproject.toml の
[project.entry-points."apothem.harnesses"] テーブルから importlib.metadata
を介してアダプターを動的に読み込みます。ハーネスを 1 つ追加するとは、
プロトコルを公開するサブパッケージを発行することを意味します —— コアの
ディスパッチャーに変更を加えることはありません。
アダプターごとに 1 つのサブパッケージ
各アダプターは、src/apothem/harnesses/<name>/ の下にそれ自身のサブパッケージ
として住んでいます。
__init__.py— プロトコルを実装し、隣接するアクションモジュールへ委譲する<Name>Adapterクラス。install.py/uninstall.py/update.py/verify.py— アダプター クラスがインポートして呼び出す、アクションごとの実装。materializer.py— 共有プロファイルをハーネスネイティブな設定テキストへ レンダリングする、任意のmaterialize_native_config(profile) -> str。
各アクションをそれ自身のモジュールへ分割することで、アダプタークラスを薄く
保ちます。それは、プロトコルメソッドをアクション関数へ配線するディスパッチャー
です。verify.py を開く読者が目にするのは、まさにその 1 つのハーネスの
ドリフト検出ロジックだけであり、かき分けるべきインストールやアンインストールの
ノイズはありません。
3 つのアダプタークラス
17 のハーネスが、みな同じやり方で設定を消費するわけではありません。Apothem は、 プロファイルがハーネスへ到達する前にどれだけの変換を必要とするかに応じて、 それらを 3 つのクラスに分類します。
クラス I —— 素の伝播
ハーネスは、Apothem がすでに書き出している同じ成果物 —— 平坦な .md ルール、
agents、hooks —— を、レンダリング工程なしで消費します。アダプターは、素の
テンプレートをハーネスの設定ツリーへコピーします。何もレンダリングされない
ため、materializer.py はありません。プロファイルの成果物がそのまま出力です。
クラス II-A —— 素にベンダーテンプレートを加えたもの
ハーネスは、Apothem の素の成果物 AND 一連のベンダー固有のテンプレートファイル
(ハーネスが必要とする設定・マニフェスト・足場ファイル)を消費します。
アダプターは、素の成果物とベンダーテンプレートを一緒にコピーします。ベンダー
テンプレートは、アダプターの隣の templates/ ディレクトリの下に住んでいます。
クラス II-B —— プロファイルからのレンダリング
ハーネスは、共有プロファイルの構造化フィールドからレンダリングされた、1 つの
ネイティブ設定ファイルを求めます。アダプターの materializer.py がプロファイル
を読み取り、ハーネスネイティブなテキストを発します —— Hermes の config.yaml、
Open-Claw の openclaw.json、OpenCode の opencode.json、あるいは Qwen Code の
settings.json。クラス II-B は、形式の調停作業が集中する場所です。
規約収束の深掘り
が、それを詳しく扱っています。
claude-code:フルサーフェスの参照アダプター
claude-code アダプターは、最も広い設定面 —— ルール、agents、hooks、
output-styles、設定、規約ディレクトリ —— のための参照実装です。これはクラス
II-A に属します。Apothem の素のテンプレートを伝播し、規約ディレクトリを平坦化
し、そして src/apothem/harnesses/claude_code/templates/ の下にベンダー設定
テンプレートを同梱します。materializer.py は携えていません —— プロファイル
からネイティブ設定ファイルをレンダリングするのではなく、成果物と規約ディレクトリ
を直接伝播します。
意図的な 1 つの境界:claude-code アダプターは CLAUDE.md を管理しません。
そのファイルはオペレーターが所有する領域です。Apothem は、その周囲の設定面 ——
ルール、hooks、設定 —— を物質化し、オペレーター自身の CLAUDE.md の声には手を
触れずに残します。それを上書きするようなアダプターは、apothem install のたびに
手書きのプロジェクト指示を消し去ってしまうため、アダプターはディレクトリの
境界で線を引き、そこで止まります。
なぜこの統一性が重要なのか
このプロトコルは、オペレーターに 17 のツールにまたがる 1 つの心的モデルを
与えます。ターゲットが単一ファイルをレンダリングするのであれ、ディレクトリ
ツリーを平坦化するのであれ、apothem install、apothem verify、
apothem uninstall は同じように振る舞います。ベンダーの差異は、プロトコルの
背後、アダプターサブパッケージの内部 —— それらがあるべき場所 —— に住んでいます。