ADR をリポジトリに残す意義と、コーディングエージェントへのコンテキストの効果的な渡し方

  • #ai
  • #adr
  • #context-engineering

前回の記事で、「コンテキストを渡すとコーディングエージェントの品質が上がる」という通説を私自身が検証していなかったことを書きました。 先行研究も足場(PR に添える仕様)しか測っておらず、蓄積された決定の効果は誰も測っていない、ということもです。

今回はそれを測りました。 自分のリポジトリを複製して条件ごとに文書を削り、同じ依頼を繰り返し実行する対照実験を 10 ラウンド、のべ 100 回あまり回しています。

先に結論の処方箋を書きます。

  1. 決定と却下は、結論(verdict)とステータスだけでも必ず残す。 理由は一行でよい。
  2. 重い索引や知識グラフは作らなくてよい。 読まれない。
  3. 「設計の前に過去の決定を確認する」という一文をエージェントの常設ルールに入れる。 これが最大の梃子で、記録の価値はこの一文があって初めて回収される。

以下では、この 3 行に至った実験を説明します。

実験の舞台

素材にしたのは、私が開発しているアーキテクチャ図ツール karasu のリポジトリです。

karasu では、文書の運用をプロジェクトの取り組みとして続けてきました。 開発時には要件から design doc を起こして設計を検討します。 合意した決定は実装後に ADR(Architecture Decision Record)へ昇格させて docs/adr/ に残します。 採用した決定だけでなく、検討して見送った提案も not_adopted のステータス付きで記録しています。

この運用を続けた結果、ADR は 263 本、合計約 98 万字になりました。 ほかに ADR 間の関係を示す索引層(グラフや有効決定の一覧で約 13 万字)、テスト観点のライブラリが 70 本、コーディングエージェント向けの常設ルール群があります。

98 万字という分量は、LLM の 100 万トークンのコンテキストウィンドウにも収まりません。 つまりエージェントは常に一部だけを選択して読みます。 「ADR をコンテキストとして渡す」と言うとき、実際に起きるのは全文の投入ではなく、この選択的な読み込みです。 今回の実験が測っているのも、この条件下での効果になります1

実験 1:記録を削って比べる

最初の設計は素朴でした。 リポジトリを 3 条件(原状のまま、docs/adr を全削除、ADR 本体は残して索引とリンクだけ除去)に複製します。 そして設計判断を要する同一の Issue を各条件で実装させ、成果物の設計文書を比べます。 記録が効いているなら、削った側の文書は劣化するはずです。

劣化しませんでした。 文書の長さも設計判断の中身も、条件間で系統的な差が出ません。

原因を追うと、決定は ADR 以外の場所でも重複して保存されていました。 たとえば「診断の重大度は事実の通知にとどめ、エラーにしない」という決定は、ADR を削除した条件でも出力に現れます。 概念ドキュメントに同じ方針の節があり、さらにソースコードのコメントに per ADR-20260514-02 という形で決定の出典が直接埋め込まれていたからです。 コードは実験の全条件で残るので、ここから漏れる決定は消しようがありません。

決定の保存層は数えると 4 つありました。 ADR 本体、概念ドキュメントと仕様書、コードの出典コメント、そして git の履歴です2。 ファイルを消してもリポジトリは憶えています。 成熟したリポジトリで「蓄積された文書は効くのか」を文書の削除で測ろうとしても、この冗長さに緩衝されて差が出ません。 反証できない設計だったと認めて、測り方を変えることにしました。

実験 2:却下済みの提案をもう一度依頼する

観察単位を変えました。 文書の有無で成果物の質を比べるのではなく、過去に却下された提案と衝突する依頼を出して、エージェントが衝突に気づくかを見ます。

karasu には「ブレース構文をやめて YAML スタイルに移行する」という提案を検討し、見送った ADR があります。 エッジ宣言が YAML 構造に収まらないこと、中途半端に YAML に似せるとかえって混乱することが見送りの理由でした。 この決定は概念ドキュメントやコードに痕跡がなく、ADR だけが憶えています。

この却下と同じ内容を、ADR の存在を伏せた自然な依頼として出しました。 「深いネストで閉じブレースが連続して読みにくい。YAML のような書き方に移行したいので設計してほしい」。

差は明瞭でした。 ADR がある条件では、3 試行すべてでエージェントが却下の記録に到達します。 「同じ提案が過去に検討され、却下されている」と明示した上で、覆すかどうかの確認を求めてきました。 ADR を削除した条件では、3 試行中 2 回、エージェントは過去の検討に気づかないまま移行の設計文書を書き上げてコミットまで済ませました。 閉じたはずの議論が、誰にも気づかれずに再び開いた状態です。

別の却下決定(ビルドツールチェーンの移行見送り)でも同じ反転が再現しました。 一方で、再現に失敗した的もあります。 PNG 出力に関する決定を的にしたところ、記録を削った条件でもエージェントは決定に到達しました。 調べると、この決定は「アプリ内には入れないが、サーバ側では画像埋め込み用に生成する」という採用済み機能の線引きで、実装がコードに生きていたのです。 実装が存在する決定は、文書を消しても実装から再構成されます。 衝突実験の的にできるのは「やらないと決めて、実装や痕跡が残っていない」決定に限る、というのが方法上の教訓です。

実験 3:確認を手続きに埋める

ここまでで「記録があれば衝突に気づく」と言いたいところですが、そうは言えないデータが続けて出ました。

別の却下決定(エージェントセッションのリネーム機能の見送り)で同じ実験をしたところ、記録がすべて揃った条件でも、エージェントが却下 ADR を読んだのは 9 試行中 1 回でした。 ツール寄りの依頼だとエージェントは docs/adr を探しに行きません。 13 万字の索引層も、自発的にはほとんど開きません。 記録は在っても、読まれなければ無いのと同じです。

そこで、依頼の前に一文だけ足しました。 「設計や実装案を書く前に、docs/adr/ を検索し、関連する決定や却下がないか必ず確認すること」。

到達は 0/3 から 3/3 に反転しました。 リポジトリの中身は同一で、違いはこの一文だけです。

この一文をプロンプトではなく常設ルール(.claude/rules/ のファイル 1 枚)に移しても、効果は同じでした。 実験で使ったルールファイルの全文がこれです。

# 設計・実装前の決定チェック

新機能の設計、実装方針の検討、design doc の作成を始める前に、必ず `docs/adr/`
検索し、関連する ADR・却下(`not_adopted`)・supersede された決定がないか確認する。

- 見つかった場合は、その決定を踏まえた上で回答する。
- 過去の決定を覆す提案になる場合は、黙って進めず、覆すことを明示して確認を取る。

これだけです。 .claude/rules/decision-check.md として置いた 8 行のファイルで、却下 ADR への到達が 0/3 から 3/3 になりました。

しかも衝突のある依頼では、ルールを入れた方が安くて速いのです。 早期に決着を見つけて止まる方が、知らずに設計を書き切るより手数が少なくて済みます(平均 0.80 ドルと 4 ターンに対し、ルール無しは 1.33 ドルと 18 ターン)3。 衝突しない依頼に課される税は 1 回あたり 0.83 ドルほどの探索コストでした。 「衝突がないのに止まる」という誤停止は観測されていません。

何が安全を担い、何が効率を稼ぐか

処方箋の 1 行目に「verdict とステータスだけでも」と書いた理由を、成分ごとの実験で示します。

安全を担うのはステータスです。 ADR から理由の節をすべて削り、「実装しない」という結論一文とステータスだけにした条件でも、エージェントは 3 試行すべてで停止して確認を求めました。 黙った再開を止めるのに、詳細な理由文はいりません。

理由(why)が稼ぐのは効率です。 理由を削った条件のエージェントは、止まりはするものの、記録があれば省けたはずの作業に回りました。 却下理由を自力で再調査することを設計の前提に置いたり、コミット履歴を発掘したりです。 理由の一行は、この再調査を丸ごと省きます。 特に「どうなったら覆してよいか」という再評価条件が書いてあると、エージェントは確認の質問をその条件に沿って組み立ててきます。

理由の文章には、予想していなかった働きもありました。 依頼の語彙を意図的に外した実験(「YAML」と言わずに「コロンとインデントの書き方」と依頼する、など)でも、到達は 12 試行すべてで成立しました。 ファイル名に当たらなくても、理由の本文が問題領域の語彙(ブレース、インデント)を含むため、本文への検索が命中するからです。 理由の記述は、それ自体が検索の表面積でもあります。

索引はどうでしょうか。 重い知識グラフは、存在する条件でもほとんど読まれませんでした。 到達を支えていたのは説明的なファイル名と本文への検索、そして確認を強制する常設ルールです。 索引に投資するなら、有効決定の軽い一覧までで足ります。

ルールに書くか、スキルに書くか

確認の一文をどこに置くかには選択肢があります。

実験で検証したのは、パスの条件を付けない常設ルールでした。 この形は CLAUDE.md に書くのと同じ常時ロード層に属します(置き場所による遵守率の差は測っていません)。 常にコンテキストへ載るので、正規のワークフローを通らないその場の依頼にも効きます。 実験で黙った再開が起きたのは、まさにこの種の依頼でした。

一方で、常時ロードの層は積み上がるほど劣化します。 確認の手順を design doc 作成のスキル側に明示しておけば、必要なときにだけコンテキストへ読み込まれるので経済的です。 その代わり発火はスキルを起動した依頼に限られ、選択的に読まれる分だけ確実性は下がる可能性があります。 この形の遵守率は今回の実験では測っていません。

成果物の側から挟む手もあります。 design doc のテンプレートに「関連する既存決定」の必須節を設ければ、確認したかどうかが成果物に現れて、節が空のままならレビューで気づけます。

実測で裏が取れているのは常設ルールだけです。 常時ロードの予算に余裕があるなら小さなルールで床を張り、テンプレートの必須節で成果物側からも挟むのが、今回のデータから言える堅い構成です。 スキルへの明示は、設計作業が確実にスキルを通るリポジトリなら、コンテキスト経済の面で有力な代替になります。

限界

この実験は個人開発の 1 リポジトリ、1 モデルで、各条件 3 試行という小さな規模です。 効果量ではなく、条件間の反転の方向を確かめたものと読んでください。

判定の妥当性は、条件を伏せた別モデルによる再分類で確かめました(39 試行で一致 85%、κ = 0.72、主要な結論のセルはすべて一致)。 ただし、統制しきれていない要素も残っています。 エージェント自身が実験対象のツールの知識を持つことによる交絡や、削除操作が git 履歴から漏れる問題です。

結び

蓄積されたコンテキストの価値は、書いたかどうかでは決まりませんでした。 衝突の瞬間に取り出させたかどうかで決まりました。

前回の記事で、先行研究が null を出したのは足場を PR 単位で測ったからではないか、と書きました。 観察単位を「決定と、それに衝突する後続の依頼」に変えたら、効果は測れました。 そして測ってみると、記録そのものは安く(結論とステータスの数行)、高くつくと思われがちな理由文は効率と検索面で元を取ります。 重い索引は要らず、最大の梃子は確認を手続きに埋める一文でした。

実験台にした karasu は公開リポジトリなので、実験の的にした ADR も実物を読めます。 実験の環境、条件定義、依頼プロンプト、全ラウンドの結果表は資料編にまとめてあります。

Footnotes

  1. 実験はすべて、複製したリポジトリに headless のコーディングエージェントで同一の依頼を与える形で行いました。エージェントは Claude Code で、モデルは Claude Opus 4.8 に固定しています。試行ごとにリポジトリを初期状態へ戻します。手順とスクリプトは記事末尾のリポジトリにあります。

  2. 後のラウンドでは、ADR から理由の節だけを削った条件のエージェントが、削除操作そのもののコミット差分を git log で見つけて元の理由を復元してみせました。

  3. 金額とターン数は、各 run の transcript に Claude Code が記録する会計(API 従量課金の合計とターン数)の 3 試行平均です。単価は Opus 4.8 の実験時点の価格なので、別のモデルでは絶対値が変わります。主張として持ち堪えるのは同一モデル内の相対比較です。