Max Nardit
← 記事
11 mindev

スペック駆動開発 (Spec-driven development) と AI エージェント: Spec Kit 自身のリポジトリはスペックを無視している

セッションの後にスペックを残すこと自体は簡単な部分です。スペックが価値を発揮するのはデリバリーの時点で、コードが存在する前に人が読み、直した唯一の成果物だからです。マージの後は、誰かが動かさない限り何とも照合されない文書になります。誠実な選択肢は、履歴として印を付けるか、そのスペックに対するチェックを維持するか、残すべきものを constitution とテストへ移すかで、最後の選択こそ Spec Kit のドキュメントが自身のリポジトリについて説明しているものです。

目次

GitHub による Spec Kit の発表は、スペック駆動開発の約束を一行にまとめていました。「『コードこそが真実の源』から『意図こそが真実の源』へと移りつつある」。まずスペックを書き、コーディングエージェントにそこから計画させて作らせ、スペックは、誰もがコードと照らし合わせる基準として残します。この語り方では、スペックはエージェントのセッションのうち、セッションが終わっても生き残る部分です。

では、Spec Kit が自分のリポジトリに何を残しているかを見てみます。.gitignore には specs/ が載っています。機能ごとのスペック、計画、タスクリストがすべて置かれるディレクトリです。そしてプロジェクトのドキュメントは、これが意図的だと説明しています。

Spec Kit's own repository illustrates this choice: it tracks
`.specify/memory/constitution.md`, while `specs/` is gitignored and
feature artifacts are not tracked.

constitution、つまりプロジェクト全体のルールはバージョン管理に残ります。機能スペックはその外に置かれます。同じページは、除外したことは「それらが削除されたのかアーカイブされたのかを決めるものではない」と慎重に書き添えています。この言葉はそのとおりに受け取ります。プルリクエストの中や誰かのディスクに残っているのかもしれません。ただ、次のコントリビューターが clone するリポジトリには含まれていません。

ドキュメントはこの選択を「constitution-only」と呼び、デリバリー後のスペックの扱い方 4 つのうちの 1 つとして示しています。どれもデフォルトではありません。この選択は、発表よりもスペック駆動開発について多くを語っていると思います。スペックは確かに、それを書いたセッションより長く残ります。そして、それは簡単な部分だとわかります。スペックの価値が最も高いのはデリバリーの時点です。コードが存在する前に人が読み、直した唯一の成果物だからです。マージの後は、誰かが動かさない限り何とも照合されない文書になり、そういう文書はほかの文書と同じように古くなっていきます。

スペック駆動開発と Spec Kit、Kiro、Tessl: スペックはいつまで意味を持つか

Birgitta Böckeler (ビルギッタ・ベッケラー) が Martin Fowler のサイトに書いた Kiro、Spec Kit、Tessl の比較は、もう 1 年前のものですが、今でも最もわかりやすい地図です。スペック駆動開発を、スペックがどれだけの期間意味を持つかで分類しています。

Spec-first: A well thought-out spec is written first, and then used in
the AI-assisted development workflow for the task at hand.
Spec-anchored: The spec is kept even after the task is complete, to
continue using it for evolution and maintenance of the respective feature.
Spec-as-source: The spec is the main source file over time, and only the
spec is edited by the human, the human never touches the code.

彼女の結論は、ツールはスペックファーストはこなしていても、その先についてはほとんど語れることがない、というものでした。Kiro は「ほぼスペックファースト」。Tessl は「明示的にスペックアンカー型を目指している」唯一のツールでした。Spec Kit はスペックごとにブランチを作り、そのためスペックの寿命は「機能の寿命ではなく、変更リクエストの寿命」になっていました。

その一部は変わりました。現在の Spec Kit では、スペックのディレクトリと git ブランチは独立しています。スペックは specs/ の下の番号付きディレクトリに置かれ、ブランチ作成は任意のフックです。main ブランチの永続化ガイドは彼女の記事を引用し、4 つのモデルで応えています。各機能ディレクトリを変更不可の記録とし、変更には新しいディレクトリを作る flow-forward から、まず spec.md を編集して残りを再生成する living spec までです。Kiro のベストプラクティスのページは、2026 年 11 月 8 日時点で、スペックを「継続的な改良を前提に設計されている」と説明しています。requirements.md や design.md を編集し、tasks.md で Sync Files を選ぶと変更用のタスクが作られ、実装がタスクの受け入れ基準を満たしているかを Kiro に尋ねることもできます。

つまり、どちらのツールも今ではスペックを残し、変更することをサポートしています。どちらもデフォルトでやらないのは、古いスペックとコードがずれたことに自分から気づくことです。両ツールのチェックはどれも、誰かが起動したときに走ります。たいていは、そのスペックが書かれた機能を作っている最中です。スペックがセッションより長く生きるかどうかは今もチームの習慣次第で、Tessl のスペックアンカー型という野心は、Böckeler のレビューの時点では野心にとどまっていました。

Spec Kit の /speckit-converge はデリバリー時にコードをスペックと照合する

Spec Kit がチェックを用意しているのは、機能の最後です。6 月から /speckit-converge が同梱されていて、最小フローはこれで終わります。implement を実行し、converge を実行し、convergence が Converged を報告するまで繰り返します。コマンドのテンプレートは、これがどういう種類のチェックかをはっきり書いています。spec.md、plan.md、tasks.md を「意図の唯一の情報源」として読み、現在のコードをそれに照らして評価し、ギャップを 1 つずつ新しいタスクとして追加します。スペックを編集することはありません。そして、タスクリスト自身のチェックボックスを信用しません。

Include every existing task in the intent inventory, regardless of
checkbox state or Convergence phase: completion claims are not evidence.

ツールキット全体から 1 行だけ残すなら、この行です。完了とマークされたタスクは主張であり、証拠はコードです。テストを信じる前に、それが失敗するのを見る のと同じ本能です。作業をした当人が出す緑のステータスは、ほとんど何も証明しません。

弱点もここに見えます。converge はモデルがドキュメントに照らしてコードを読むもので、チェックの独立性はドキュメントの独立性を超えません。/speckit-specify は 1 文を受け取り (README の例では "Build a photo organizer with albums grouped by date and a tile preview of each album.")、そこからスペックを書きます。返ってきたものを流し読みして先へ進むと、後で converge が比べるのは、エージェントのコードと、あなたの文をエージェント自身が解釈した結果です。その解釈が間違っていても、両者は一致します。手に入るのは、エージェントがもともと信じていたことの、より長く、より自信に満ちた言い換えと、それに同意するチェックです。

スペックを照合の基準にするのは、計画ができる前に人がそれを読み、1 行ずつ受け入れたり直したりしたという事実です。Böckeler の反論が刺さるのはここです。

To be honest, I’d rather review code than all these markdown files.

多くの作業については、彼女が正しいです。私の読みでは、答えはそれぞれの成果物が何を示せるかで決まります。コードはシステムが何をするかを示します。システムが何を拒むはずだったか、どのエッジケースを扱わないと誰かが決めたかは示せません。スペックのうちレビュアーの時間に値するのはその部分で、スペックが「もっともらしく聞こえる」かだけを確かめるレビューは、同じ流し読みを遅くしただけです。スペックがそもそもレビューに値するかは、2 つのテストで決まります。コードが示せないことを述べていること、そして対象とするコードより短いことです。実装を散文で言い直したスペックは 1 つ目で落ち、diff のほうが短くなるほど小さな変更のスペックは 2 つ目で落ちます。どちらの場合も、その時間はコードのレビューに使うほうが有益です。両方を通るスペックは、要件の読み違いを捕まえるのに、ループ全体で最も安い場所です。その読み違いの上にまだ何も作られていないからです。

スペック駆動開発のスペックは、マージ後には何とも照合されない文書になる

デリバリーの後、Spec Kit の中でスペックをもう一度読むものは、誰かがコマンドを実行しない限りありません。converge は 1 つの機能ディレクトリに対して動き、テンプレートは自身について「diff ツールではない」と書いています。git も、ブランチの比較も、履歴も見ません。機能 003 のスペックは、機能 003 を作っている間にチェックされます。6 週間後に機能 007 が同じコードを変えても、誰かが思いつかない限り、その変更を 003 のスペックと比べるものはありません。

私自身のリポジトリにも、スペックツールとは関係のないところで、同じことの小さな例があります。このサイトの多言語化の設計ドキュメントは 2026 年 5 月 2 日にコミットされ、冒頭にステータス行がありました。

**Status:** Design validated, not yet implemented

実装は同じ日のコミットで入りました。ドキュメントにはその後コミットが一度もなく、6 か月間、自信のあるヘッダーで、数時間のうちに事実でなくなったことを述べ続けています。どのツールもチェックしないただの設計ドキュメントで、スペックではありません。そこが要点です。残っていて読めることは、最新であることと同じではありません。誰もチェックしないメモリファイル の問題も同じです。

このドキュメントを現在の計画として扱うよう指示されたエージェントは、もう存在しない状態に対して計画を立てるでしょう。Spec Kit には同じリスクがもっと鋭い形であります。誰かが古い機能ディレクトリを現在の意図であるかのように converge に向けたら、そこから外れた後の作業が contradicts や unrequested の指摘として返ってくることがあり、それを直すタスクも追加されます。テンプレートは unrequested の作業には穏やかな道を用意していて、見直すか正当化するタスクにとどめ、コードを自分で削除することもないので、何かが自動的に元に戻ることはありません。それでも、誰かが先にそのタスクを読まない限り、次の implement はそれをもとに作業します。Spec Kit のガイドはこのことを認識しています。flow-back の説明は「主なリスクは静かな乖離」と書き、進化についてのガイドは、変更をコードに残したまま「信頼できる状態を保つことになっている (meant to remain trustworthy) スペックと矛盾させる」ことのないよう求めています。重みは "meant to" (〜ことになっている) にかかっています。信頼できるというのはスペックごとに維持し続ける性質で、ツールはその仕事をあなたに任せています。

スペック駆動開発のデリバリー後: スペックに印を付けるか、チェックするか、昇格させるか

マージの後に重要なのは、それぞれのスペックが何になるかです。誠実な答えは 3 つあると考えています。

1 つ目は、スペックを履歴として残し、そうと明示することです。この日付の時点で意図されていたことで、その後に来たものに置き換えられている、と。これは Spec Kit の flow-forward モデルであり、より古いアーキテクチャ決定記録 (ADR) の規律でもあります。ADR は日付とステータスを持ち、superseded と印を付けられ、現在に合わせて書き換えられることはありません。これを安全にするルールは、どのエージェントも記録を現在の意図として読まないことです。

2 つ目は、スペックを最新に保ち、それを書いた変更だけでなく、その機能に触れるすべての変更でスペックをチェックする仕組みを持つことです。Böckeler のスペックアンカー型の定義は、スペックを残して保守に使うことしか求めておらず、手作業でスペックを保守するチームもそれを満たします。私のより強い要件は、誰かが走らせるのを覚えていなくても走るチェックです。善意だけで最新に保たれる living spec は、私の設計ドキュメントのステータス行と同じように朽ちていくからです。

3 つ目は、デリバリー後にスペックを引退させ、残すべきものを毎回読まれる場所へ移すことです。Spec Kit のドキュメントが自身のリポジトリについて説明しているのがこの選択です。constitution は残り、機能スペックはバージョン管理から外れ、長く残る知識は、ドキュメントの言葉では「設計ドキュメント、ドキュメント、テスト、コントリビューター向けガイダンスの中に」置かれます。移す先について 1 つ注意があります。constitution はエージェントが作業の開始時に読むルールファイルで、CLAUDE.md と同じ種類のものです。そして モデルが読むだけのルールは重み付けされるだけで、強制されません。規約や好みはそこに置けば十分です。必ず守られるべきものはテストに置きます。

何も決めないことは、3 つのどれよりも悪い結果になります。スペックは誰も保守しないステータスのままリポジトリに残り、今後のすべてのセッションから、まだ計画であるかのように読まれます。

では、これから specify init を走らせる、あるいは最初の Kiro スペックを開く開発者はどうするか。最初の機能の前に永続化モデルを決め、Spec Kit 自身のガイドが勧めるとおり、それを constitution に書いてください。レビューの時間は計画ができる前の spec.md に使い、コードがどうせ示す部分ではなく、システムが拒否すべきことと、扱わないと決めたエッジケースを見てください。そこが、あなたの修正がエージェントに届く最も早く、最も安いポイントだからです。私の設計ドキュメントが意味を持ったのは 5 月 2 日でした。それ以降に述べてきたことはすべて、誰も更新しなかったステータス行です。

01 / ディスカッション

コメント欄はありません。議論は X で行っています。

Max Nardit@mnardit

02 / ほかの記事

  1. Anthropic のエージェントメモリの構成要素を Claude Code で読む:MEMORY.md を監査し、トランスクリプトを残し、自前のドリーミングを回す

    Claude Code はメモと起動時のインデックスを無期限に残す一方、その裏付けになるトランスクリプトはデフォルトで 30 日後に削除し、両者を突き合わせる仕組みは持っていません。Anthropic の結果を見ると、その突き合わせには価値があります。ローカルで実行し、反映する前に結果を読む方法をまとめました。

  2. バイブコーディングとエージェンティック・エンジニアリングの違い: セッションが終わった後もコードを検査し続けるもの

    Simon Willison は、コードをレビューし、それを説明できる人がいるかどうかで線を引きます。ただ、そのレビューは一度きりで、チャットが閉じた後もコードは変わり続けます。エージェンティック・エンジニアリングが名前を言い換えただけのものでないなら、違いは、誰も見ていないときに動き、しかも意図した理由で失敗するのを確かめたチェックにあります。

  3. Google の SLEEP フレームワークとエージェントの記憶ファイル: 重みはコンテキスト税をなくすが、アドレスも消す

    記憶を重みに統合することは、呼び出しのたびにメモを読み直すコストへの妥当な解決策です。ただし同時に、記憶に対するオペレーターのあらゆる操作に必要なもの、つまり指し示せる対象も失われます。SLEEP では昨夜の分ですら取り出せるブロックになっていないため、ロールバックの単位はチェックポイントになり、忘れるコストは忘れる対象の古さとともに増えます。重みの記憶を安全に運用できるのは、保管した記録から作り直すキャッシュとして扱う場合だけです。