本記事の内容は私(R2-san)の体験(実装コード)・判断・指示に基づきますが、文章作成は生成 AI が行い、私の監修・確認を経て公開しています。
前回、AIに全部判断させるのをやめた の最後に、こう書きました。
この記事に出てきた「変更提案の前に読めるものが無いと判定する」という修正には、いつ・なぜそう決めたのかという記録が残っています。
これだけでは分からないと思うので、少し説明します。
私が作っている R2 は、AI にリポジトリを探索させ、読んだファイルを材料にして変更案を作らせる AI coding agent runtime です。前回書いた不具合は、こういうものでした。
1. ファイルが削除された。しかし索引には、まだそのファイルが残っていた
2. AI は索引を信じて、そのファイルを読もうとした
3. R2 は「無い」とは言わず、黙ってスキップした
4. 材料が 1 つも無いまま、変更案の生成に進んだ
5. まともな案が出るはずもなく、失敗した
6. 記録には「AI の応答が不正」と残った
悪いのは AI ではありません。空の材料を渡した R2 の側です。
これを直すために入れた修正が、「変更案を作らせる前に、R2 自身が『読めたものが 1 つも無い』と判定した時は、AI を呼ばずに止め、そう記録する」でした。前回の末尾で「変更提案の前に読めるものが無いと判定する」と書いたのは、この修正のことです。
そして言いたかったのは、この修正について、いつ・なぜそう決めたか、他にどんな直し方を検討して捨てたかが、コードとは別に 1 件の記録として残っている、ということでした。
今回は、その記録の話です。
先に結論を書きます。
R2 には、設計判断を 1 件 1 ファイルで保存する仕組みがあります。Repository Decision Memory と呼んでいます。
保存しているのは「何を決めたか」だけではありません。なぜそう決めたか、何を捨てたか、どうなったら見直すかです。
作った理由は単純で、AI も私も、それを忘れるからです。
あの修正には、こういう記録が残っている
先ほどの修正には、次のような記録が 1 件付いています(構造だけ示します。実物は JSON です。記録の中では AI 側を Provider、R2 本体を Core と呼んでいます)。
問い
実ログで確認された連鎖(時刻つき)
……探索は複数ラウンド、読み取り要求はあった、読めた数は 0、
……空のまま提案へ進み、Provider 応答不良として記録された
「本件は Provider の問題ではない」
なぜ Core は、読めなかった事実を既に持っているのに
成功し得ない呼び出しへ進み、失敗を別の原因として記録するのか
決定
提案を呼ぶ前に、3 つの条件が揃ったら AI を呼ばずに止める(fail-closed)
(何も読めていない / 読み取り要求はあった / 実際に読めた数は 0)
専用の失敗コードで記録し、「リポジトリは変更されていません」と表示する
根拠
成功し得ない呼び出しに時間と費用を使う
誤った原因をユーザーにもレビューにも伝える
この 2 つの実害を、Core 側の事実だけで確定的に防げる
……
捨てた案
一度 Provider を呼んでから、その失敗を Core の事実で上書き分類する
→ 無駄な呼び出しが防げない。Provider が何を返すかに依存して不安定
探索の集計値(読んだ数・要求した数など)に読めなかったファイル名の一覧を足し、そこから判定する
→ 集計値は数だけを持ち、ファイル名のような自由文は持たない、という既存の決まりを破る
「読めた数 0」だけを条件にして「要求があった」を外す
→ 新しくファイルを作るだけで何も読まない、正当な Task まで止めてしまう
見直す条件
索引の古さを検出して作り直す仕組みが別途実装されたら、
この止める仕組み自体が不要になる可能性がある。その時点で見直す
……
証拠
作業指示書 / 当日の実ログ / 受入テスト
変更の差分(diff)を見れば「決定」は分かります。
分からないのは、捨てた案と見直す条件です。
「Provider を呼んでから上書き分類する」という案は、実装されなかったのでコードのどこにもありません。でも次に同じ場所を触る AI が、同じ案を「良さそう」と思いつく可能性は十分あります。そのとき、却下理由がここにあれば、同じ検討を繰り返さずに済みます。
そして「見直す条件」のほうは、実際に動きました。
この修正を決めたとき、根本原因は「索引が古いままだった」ことだと分かっていましたが、そちらには触れず、「失敗の原因を正しい側に記録する」ことだけを直すと決めていました(この切り分けの経緯は Zenn の設計 AI 編 に書きました)。
その根本原因側は、後から別の記録になっています。別の記録は、最初の記録を参照しています。「あちらは原因の記録の仕方の是正で、根本原因には触れないとスコープ外にしていた。こちらはその根本原因側を、最小の変更で閉じる」と。
2 つの記録が、互いを指しています。
これが、私が残したかったものです。
AIは忘れる
ここで言う「忘れる」は、比喩ではありません。
R2 の開発は、設計 AI が作業指示書を書き、実装 AI が実装し、レビュー AI が突合する、という分担で進めています(この体制は Zenn のシリーズ に書いたので、ここでは繰り返しません)。
それぞれの AI は、セッションが変われば前の対話の中身を持っていません。持ち越せるのは、ファイルに書かれていて、次のセッションが読むものだけです(規約、指示書、記録。AI 自身のメモリも、結局はファイルです)。
具体例を 1 つ。
設計判断の記録は、連番で採番しています。設計 AI が作業指示書に「現在の最新は DM-○○○○」と書くことがあるのですが、これが古いことがある。指示書を書いてから実装に着手するまでの間に、別の作業で番号が進んでいることがあるからです。
そこで今は、作業指示書のテンプレートにこう書いてあります。
採番は着手時に
decisions/を実列挙して決める(指示書に書かれた「現在の最新」は古い場合がある)。
そしてレビューの入力を作るスクリプトが、実際のファイルを数えて「最大番号・次の未使用番号・重複」を出します。
設計 AI の記憶を信用しない。ファイルを数える。
小さなことですが、「AI は忘れる」を前提にすると、手順がこういう形になります。
もう 1 つ、もっと大きな忘れ方があります。
ある設計判断が、前の設計判断の誤診断の上に積まれていたことがありました。
少し前提を書きます。R2 の GUI には、新しいチャットを始めるボタンがあります。送った文を「リポジトリへの Task」として扱うか「ただの会話」として扱うかは、AI が内容を見て判定します(ルーティング)。
このボタンを押した直後の送信が、Task になったりならなかったりして予測できない、という報告がありました。最初の記録はこれを「AI ルーティングの精度の問題」と診断し、ボタンを押した直後の送信は AI の判定を通さず常に Task にする、という決定をしました。
その後の対話で、私自身が前提を訂正しました。そのボタンが作るのは GUI 側の「チャットの箱」であって、Task か会話かという Core 側の分類結果ではない、と。
次の記録は、最初の記録を撤回しています。題名に「撤回」と入っています。「問い」の欄には、私がそのとき言った言葉が、そのまま引用されています。「根拠」の欄には、こう書いてあります(記録の番号は省いて引きます)。
(最初の記録)は問題を正しく診断できていなかった。……この誤診断の上に、さらに大きな誤りを積み重ねてしまっていた。
これは AI が忘れたというより、AI が間違えた話です。ただ、間違えたことを忘れないためには、こういう記録が要ります。撤回した記録を消してしまうと、「なぜ今の形なのか」が分からなくなり、同じ案がもう一度出てきます。
人間も忘れる
私も忘れます。
よく分からなくなるのは「これは R2 の機能として入れたものか、それとも R2 の開発・運用の記録や手順として入れたものか」です。
分からなくなったら、開発で使っている AI(設計 AI)に尋ねます。R2 のチャットではなく、リポジトリを直接読める側の AI です。
以前、記録が残っていなかった頃は、どんな質問にも AI はコードを見て推測で答えていました。正しいこともあれば、正しくないこともありました。今は、記録があるので、推測を含まない答えが返ってきます。
AI に聞く以外の読み方も、1 つ作りました。R2 の記録の中に、こういう「問い」から始まるものがあります(要約)。
開発中の製品にどの機能を入れたか、入れなかった機能はなぜ入れなかったかを忘れてしまう。その答えは既に契約文書と設計判断の記録(特に捨てた案と却下理由)に記録されているが、閲覧手段がない。
記録はあった。でも読めなかった。
この記録の決定は「登録したリポジトリの設計判断の記録を、読み取り専用で GUI に表示する」でした。R2 自身のリポジトリを R2 に登録して眺める、というのが最初の動機ですが、記録は一般に「登録したリポジトリの記録」として扱い、R2 専用にはしていません。記録が無いリポジトリでは「記録がありません」と出すだけで、推測で補わない。壊れている記録は黙って落とさず、壊れていると表示する(棚卸しが目的なので、気づかないのが一番困る)。
ここで 1 つ、第 1 回の記事との関係を書いておきます。
第 1 回で、R2 はリポジトリの理解(Repository Knowledge)を根拠と鮮度つきで保存し、古くなったものは捨てる、と書きました。
Decision Memory は、それとは別物です。
Repository Knowledge
対象: ユーザーのリポジトリの理解
性質: 製品の実行時データ。古くなれば捨てる
Repository Decision Memory
対象: R2 自身の設計判断
性質: R2 の開発資産。捨てない。覆したら覆したと書き足す
開発規約にも、設計判断の記録を製品の Knowledge Store や Task の実行時データへ保存してはならない、と明記しています。片方は「正しく捨てる」ための仕組みで、もう片方は「捨てない」ための仕組みです。同じ「記憶」という言葉で呼ぶと混ざるので、名前も置き場所も分けています。さっき書いた「R2 の機能か、開発の記録か」という私の混乱も、ちょうどこの境目で起きます。
1件の記録に何が入っているか
記録の形は、こうです。
Decision(1 件 = 1 ファイル、連番)
├─ 問い … 何が問題だったか。問いの形で書く
├─ 決定 … 箇条書き
├─ 根拠 … なぜそれを選んだか
├─ 捨てた案 … 案ごとに「選ばなかった理由」
├─ 見直す条件 … どうなったらこの判断を再評価するか
├─ 証拠 … 指示書 / 実ログ / テスト / 参照する他の記録
├─ 帰結 … 実際にどう変わったか
├─ 影響する Capability … 契約文書の id
└─ 状態 … 採用 / 要見直し / 置き換え済み
必須なのは「問い・決定・根拠・見直す条件・影響する Capability・状態」など一部で、「捨てた案・証拠・帰結」は構造上は任意です。ただしレビューの手順では、複数案から選んだのに「捨てた案」が無い記録は差し戻し対象になります(後述)。
見方としては、「問い」が一番大事だと思っています。
「決定」だけ書かれた記録は、後から読むと「なぜこれが必要だったのか」が分かりません。問いがあると、「今もその問いは有効か」を確かめられます。問いが消えていれば、決定も見直してよい。
具体例を 1 つ。先ほどの「撤回」の記録は、問いの欄に、私がそのとき言った言葉を逐語で引いています。決定の欄だけ読むと「フィールドを削除し、ボタンのラベルを変える」という作業にしか見えませんが、問いを読むと、GUI と Core の境界をどこに引くかという話だったと分かります。
もう 1 つ、契約文書との分担について。
R2 には Capability ごとに「今、何を所有し、何を所有しないか」を書いた契約文書(Capability Intent Contract)があります。Decision Memory はその隣にあって、「なぜその境界を選んだか」を受け持ちます。
Contract … 現在、何を所有するか(今の姿)
Decision … なぜその境界を選んだか(来歴)
契約文書には、実装ファイル名や変更履歴の識別子、「どの記録に置き換えられたか」のような来歴(いつ・なぜそうなったかの経緯)を書きません。来歴は全部 Decision 側です。この境界がぼやけていた時期があって、関数名をどこまで契約文書に書いてよいか、という判定基準を 1 件の Decision で確定させたこともあります(「この名前を消したら、この文の主張は検証できなくなるか」が基準です)。
「見直す条件」を、決めたときに書く
Decision Memory の欄のうち、ここで特に書いておきたいのが「見直す条件」です。
保存された記録は、将来の実装を拘束する制約ではなく、現在の判断材料です。状況も、製品の目的も、AI の能力も変わる。だから決めた時点で「どうなったら考え直すか」を書いておきます。
これは実際に発火します。
例えば、調査だけを行う Task の記録に、「同じ Task への追い質問(続けて質問すること)はレポートを上書きする(履歴はイベントログに残る)。レポート履歴の保持が求められたら永続表現を再検討する」という見直す条件がありました。
後日、追い質問で前回の調査結果が消える、という問題が出ました。対応する記録が新しく作られ、元の記録の「見直す条件」の欄には、こう書き足されています(要約)。
[発火] 別の記録で追記専用の履歴を追加。上書き前のレポートの保持は履歴側で満たされ、上書き自体の意味は変えていない。
別の例では、実装直後のレビューで「見直す条件」の 1 つがもう成立していたことが分かり、その欄に「レビュー実測で既に発火」と注記が入っています。決めた当日に条件が成立する、ということもあるわけです(これは正直、決めた時点の見立てが甘かったという記録でもあります)。
共通しているのは、元の文を消さないことです。発火したら書き足す。覆したら「覆した」と書き足す。ある記録には「旧文は消していない」とわざわざ書いてあります。
覆すときは、覆すと書く
設計判断は覆ります。覆ること自体は問題ではありません。
問題は、黙って覆すことです。
前の記録と矛盾する決定を、何も言わずに新しい記録として追加すると、2 つの記録が並び立って、どちらが今の判断か分からなくなります。
なので R2 の作業指示書のテンプレートは、実装 AI にこう要求しています。
過去Decisionを覆す場合は、覆すことをDecision本文に明示させる(無言の上書きは差し戻し)。
覆し方には、いくつかの形があります。
- 全面的な撤回。先ほどの「ボタンを押した直後は AI の判定を通さず常に Task にする」判断は、題名から撤回と分かる形で覆されました。
- 部分的な翻し。元の判断は撤回せず、状態も「採用」のまま、「これは部分的な翻しであり、全体の破棄ではない」と新しい記録に書く。探索の最初の一手を速くする判断をした後、「速度より網羅性を優先する」方向にその一部を翻した記録がこの形です。
- スコープ宣言の翻し。コードのコメントに「Windows は射程外」と書かれていたのを、後から「射程に入れる」と決めた記録は、題名に「スコープ宣言の翻し」と入っています。
正直に書くと、ここはまだ揃っていません。
記録の「状態」には「置き換え済み」という値が用意されています。でも今のところ、その値になった記録はありません。覆した側が「覆した」と書く運用で回っていて、覆された側に注記が入っている記録と、入っていない記録が混在しています。
「置き換え済み」に切り替えるべきなのか、覆した側に書いてあれば十分なのか。覆された側の状態は後から機械的にでも揃えられるので、覆した側に書いてあることのほうが先、という順序までは言えます。ただ、それで本当に困らないかは、まだ分かりません。
分からないものは、後回しにしています。
開発のルールや手順は、試行錯誤で積み重ねてきたものです。今がベストというわけでもありません。これもその一例で、悩むなら後回しにして、判断の明確なものから先にやる。「状態」の揃え方は今、その後回しの側にあります。
機械で見ているのは「形」だけ
記録があるだけでは、形が崩れていきます。必須の欄が抜ける、番号が重複する、存在しない Capability を指す。
なので、形は機械で見ています。
常時走るテストが、全記録について次を確認します。
必須の欄が揃っているか
id がファイル名と一致しているか / 重複していないか
状態が既知の語彙か
影響する Capability が、実在する契約文書を指しているか
「置き換えた」と書いた先が実在するか
加えて、レビューの入力を作るスクリプトが、その作業で追加された記録について、採番(重複・次の未使用番号)と必須の欄を確認し、「捨てた案・証拠・帰結」が空なら警告を出し、「置き換え」の記載が無ければ「過去の判断を覆すなら明示が必要」と注意書きを出します。
ここで大事なのは、機械は内容を見ていないことです。
テストのコメントにも、「構造と相互参照の整合のみを見る。内容が意味的に正しいかは判定しない」と書いてあります。
「捨てた案」が 3 つ書いてあっても、それが指示書で実際に検討された案と一致しているかは、レビュー AI が指示書と突き合わせて見ます。最終的に目的に達しているかは、私が見ます。
機械が見るのは、人や AI が見るべきところに集中できるようにするための、下ごしらえです。
誰が書くのか
記録を書くのは、設計 AI と実装 AI です。
作業指示書が「この変更は Decision 作成必須の条件に該当する」と宣言し、検討して却下した案を列挙し、実装 AI に「却下理由つきで全部残せ」と要求します。実装 AI は着手時にファイルを数えて採番し、記録を書き、レビュー AI が指示書と突き合わせます。
私は、決めて、確認します。書きません。
どの変更で記録が必須かは、規約に列挙してあります。Capability の追加・削除、責務の変更、AI と Runtime の責務の変更、fail-closed 条件の変更、安全制御の変更、複数案からの選択、過去の判断を覆す判断。逆に、責務・契約・安全性を変えない内部実装、バグ修正、テスト追加、表示調整は要りません。
すべての変更に記録を要求しているわけではありません。バグ修正やテスト追加のような、設計上の選択が無い変更は、普通の変更履歴に一言書くだけです。
選択が無かった変更にまで記録を要求すると、書くことの無い記録が増えて、読むべき記録が埋もれます。闇雲に記録しているわけではなく、「選んだ」ところだけを残しています。
これは ADR では、と思った方へ
(2026-08-26 追記)
ソフトウェア設計の世界には、**ADR(Architecture Decision Record)**と呼ばれる実践があります。設計上の決定を 1 件ずつ短い文書にして、連番で残す。書くのは、背景(なぜ決める必要があったか)・決定・帰結。覆すときは古い文書を消さず、新しい文書で「置き換えた」と記す。2011 年に Michael Nygard が提唱した形が原型で、検討した選択肢を却下理由つきで残す変種も広く使われています。
ここまで読んで「それは ADR では」と思った方は、正しいです。
1 件 1 ファイル、連番、捨てた案を残す、覆すときは覆すと書く、「状態」に「置き換え済み」という値がある。構造はほぼそのまま ADR です。系譜としては、ADR の変種と呼ぶのが正確だと思います。
白状すると、私はこの記事を公開したあとで、ADR という名前を教えてもらいました。知らずに作っていたわけです(設計を任せていた AI は、間違いなく知っていたはずですが)。
その上で、ADR の一般的な説明と置き方が違うところが 3 つあります。どれも、書き手と読み手に AI がいることから来ています。これは私が知らずに作り始めた経緯に由来するもので、他のAIエージェントにも知らせる、という意図が強く働いていたためです。
1. 「書くかどうか」を書き手の判断に残さない
ADR では「アーキテクチャ上重要な決定なら書く」とされることが多く、「重要かどうか」の見極めは書き手に委ねられます。R2 では、記録が必須になる変更の種類と、要らない変更の種類を、規約に列挙してあります(「誰が書くのか」の節に書いたとおりです)。書くのは AI で、感覚はセッションを越えて持ち越されません。列挙にしておけば、判定は毎回同じになります。
2. 帰結ではなく「見直す条件」を必須にする
ADR の基本形の 3 番目の欄は帰結(この決定で何が起きるか)です。R2 の記録にも帰結の欄はありますが、構造上は任意で、必須にしているのは「どうなったらこの判断を再評価するか」のほうです。決定を静的な文書として置くのではなく、見直しの発火条件ごと保存する。これが実際に発火することは、「見直す条件」の節に書いたとおりです。
3. 人間の散文ではなく、機械で形を検査できる形式にする
ADR は Markdown の散文で書かれるのが普通です。R2 の記録は JSON で、必須の欄・採番・相互参照を機械が常時確認しています(「機械で見ているのは『形』だけ」の節)。読み手が人間だけなら、散文で足ります。読み手に AI とスクリプトが入るので、形だけは機械で保証しています。
もう 1 つ挙げるなら、単独の文書群ではなく、契約文書(今の姿)と対になっている点も違います。ただこれは ADR に無い何かというより、R2 側の分担の話です。
逆に言えば、ADR の文献に蓄積された知見(記録の粒度、置き換えの作法)は、そのままこの仕組みの改善に使えるはずです。焼き直しかと聞かれたら、「知らずに焼き直して、置き方だけ AI 前提に変わったもの」が正直な答えです。
まとめ
Repository Decision Memory は、設計判断を 1 件 1 ファイルで残す仕組みです。
作ってみて分かったことを 3 つ。
1. diff に残らないものを残す
決定そのものはコードに残ります。残らないのは、捨てた案と、見直す条件です。この 2 つが無いと、AI は同じ案をもう一度思いつき、人間は「なぜこうなっているか」を忘れます。
2. 記録は拘束ではなく材料
決めた時点で「どうなったら見直すか」を書いておく。発火したら元の文を消さずに書き足す。覆すなら覆すと書く。
3. 機械が見るのは形、内容は AI と人が見る
必須の欄・採番・相互参照は機械で。捨てた案が本当に検討された案かは、レビュー AI が指示書と突き合わせる。
第 1 回で書いた Repository Knowledge は「正しく捨てる」ための仕組みでした。Decision Memory は逆で、「捨てない」ための仕組みです。AI も人間も忘れる、という前提に立つと、どちらも要りました。
現在、この考え方を R2 Fugu Agent Runtime という個人開発の AI coding agent runtime で検証しています。
あとがき
機能の実装を検討するとき、当初は、仕様を伝えると、設計 AI からは既存コードとただ整合するだけの設計が返ってきていました。今は、過去の設計判断から変えるのか、拡張するのか、という確認が先に来ます。その多くは、私も忘れていたことです。
AI に提案を求めるときも同じで、過去の設計判断から始めてくれるので、同じ説明を繰り返すことがなくなりました。セッションが変わるたびに、以前は何度も何度も同じことを説明していたように思います。それが、なくなっています。
関連記事
このシリーズ(Qiita・製品設計):
- AI coding agentは、なぜ毎回リポジトリを調べ直すのか ― Repository理解を使い捨てにしない設計
- AIに全部判断させるのをやめた ― AI ModelとCoding Agent Runtimeの責務をどう分けたか
この製品を AI に作らせる開発の進め方については、Zenn に別のシリーズを書いています:
- 設計も実装もレビューもAIがやる個人開発で、精度をどう出しているか — 全体像
- 作業指示書は、最初に書かせない ― 設計AIとのやり取りを全部見せる — 設計 AI 編
- 実装AIには、指示書のパスを渡すだけ ― コピペで始めた運用が仕組みになるまで — 実装 AI 編