1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIも人間も設計判断を忘れる ― Repository Decision Memoryを作った理由

1
Last updated at Posted at 2026-08-24

本記事の内容は私(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 に作らせる開発の進め方については、Zenn に別のシリーズを書いています:

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?