コーディングエージェントを使った開発では、テスト・ビルド・静的解析で囲った「開発ループ(ハーネス)」を組み、エージェントにそのループを回させるやり方が定着してきました。ただ、このループが保証するのは「仕様どおりに動くか」までです。仕様そのものが間違っていれば、間違ったものを高速に作り続けるだけになります。
そこで、開発ループの手前にもう 2 つループを置く構成を考えました。
- 要求確定ループ — システム利用者と開発者のあいだで、業務ルールを確定させる
- 仕様レビューループ — 確定した要求を、エージェント同士の批判と形式手法の検査で仕様書として固める
この 2 つと並行して、開発費と運用費を予算責任者と詰める「実現性・コストのループ」も回します(全体図には含めず、後の節で別の図を示します)。さらに、確定した仕様書からテストコードや OpenAPI 定義を自動生成し、開発ループの「ゴール」として渡すスキャフォールディングでつなぎます。この記事では、その全体像と各ループの組み方を整理します。
なぜ開発ループの前にループが要るのか
曖昧な仕様は、エージェントが増幅する
人間のエンジニアなら、仕様書に矛盾や「要検討(TBD)」があっても、ドメイン知識で補ったり「ここはどうしますか?」と聞き返したりできます。自律型のコーディングエージェントは違います。曖昧な仕様に対しても「それらしいコード」を大量に、速く書いてしまいます。
その結果、仕様の矛盾がそのまま不具合コードになり、テストで弾かれ続けてループが空回りします。悪くすると、テストを通すための辻褄合わせに迷走します。Garbage In, Garbage Out が、エージェントの速度で増幅されるわけです。
手戻りのコストがまるで違う
仕様の不備を開発ループに持ち込むと、コード修正・テスト実行・環境リセットという重い往復が発生します。一方、仕様書の段階でエージェントに相互批判させたり、エッジケースを洗い出させたりするなら、かかるのはトークン代だけです。
ここで役割を整理しておきます。
| ループ | 保証するもの | 問い |
|---|---|---|
| 要求確定・仕様レビュー | Validation(妥当性確認) | 作るべきものは正しく定義されているか |
| 開発ループ | Verification(検証) | 作ったものは仕様どおりに動くか |
| 実現性・コスト(上流と並行) | Feasibility(実現性) | その範囲を、この予算と期間で作り、運用できるか |
開発ループだけでは Validation を担保できません。上流でそれを担うループが別に必要です。品質を確保する工程を上流へ前倒しする、いわゆるシフトレフトの考え方です。
以下、図の上から順に要求確定ループと仕様レビューループを見ます。続いて、この 2 つと並行して回す実現性・コストのループを説明し、最後に仕様駆動のスキャフォールディングに進みます。
第 1 ループ:利用者と回す「要求確定ループ」
最初に考えた構成は「仕様レビューループ → 開発ループ」の 2 段でした。しかしこれは開発者側の技術的なループに偏っています。その前に、システム利用者(業務側・ステークホルダー)と要求を確定させるループが要ります。
利用者が気にするのは OpenAPI や型定義ではありません。「自分たちの業務ルールや条件が漏れなく、矛盾なくシステムに反映されるか」だけです。そのため、このループでは技術用語を使わず、業務シナリオ・ルール・入出力イメージの粒度で確認を回します。
- 構造化・可視化 — エージェント(Requirement Agent)がヒアリングメモや断片的な要望を、業務ルール・判定表・ユースケースに変換する
- 矛盾・エッジケースの検出 — 別のエージェント(Business Red Team Agent)が「もし〇〇の場合は?」「このルールとあのルールが衝突していませんか?」を洗い出し、Q&A 表に起票する
- 利用者の確認・合意 — Q&A 表の質問を持って利用者にヒアリングし、「この解釈で合っていますか?」を自然言語や画面で確認する。修正があれば 1 に戻る
利用者が合意(サインオフ)した時点で、要求仕様が確定します。
確認に使う 4 つの成果物
利用者が判断しやすいように、エンジニアでなくても読める形式をエージェントに作らせます。これが利用者側の「確認ハーネス」になります。
Q&A 表(未決定事項の台帳)
ループの軸になるのが Q&A 表です。エージェントが見つけた未決定事項を、システム利用者に聞く質問として 1 行ずつ起票し、回答をもらって仕様に反映するまでを追跡します。決定表の空欄も、Red Team の「もし〇〇の場合は?」も、最終的にはすべてここに集まります。
起票元には、このループのエージェントだけでなく、後で説明する仕様レビューループのエージェント(Red Team・Domain Checker・Formal Checker)と、実現性・コストのループも入ります。
| No | 起票元 | 質問(利用者向け) | 選択肢 | 回答 | 反映先 | 状態 |
|---|---|---|---|---|---|---|
| Q-001 | 要求確定(決定表) | シルバー会員で 10,000 円未満のとき、割引は A・B のどちらにしますか? | A: 割引なし(0%) / B: 3% 割引 | A(業務部が回答) | 決定表 4 行目 | 反映済み |
| Q-002 | 仕様レビュー(Red Team) | 決済の途中で通信が切れたとき、注文は A・B のどちらで扱いますか? | A: 取り消す / B: 「決済待ち」で残し、後で再開できる | 未回答 | — | 確認中 |
| Q-003 | 仕様レビュー(Domain Checker) | 割引後の金額に 1 円未満の端数が出たら、A・B のどちらで丸めますか? | A: 切り捨て / B: 四捨五入 | 未回答 | — | 確認中 |
| Q-004 | 仕様レビュー(形式検査・Z3 の反例) | クーポンを使うと「注文額の 60% 以上」の下限を割るとき、A・B のどちらにしますか? | A: 下限の金額で止める / B: この条件ではクーポンを使えなくする | 未回答 | — | 確認中 |
Q&A 表には次の役割があります。
- ヒアリングの台本になる。 打ち合わせのたびに一から聞き直すのではなく、「確認中」の行だけを持って利用者のところへ行きます。
- 回答を仕様に反映した証拠になる。 「反映先」の列で、どの回答が決定表・シナリオのどこを変えたかを追えます。後から「なぜこの仕様なのか」と聞かれたときの根拠にもなります。
- 利用者へのフィードバックになる。 回答がどう反映されたかを Q&A 表ごと返すことで、利用者は自分の答えが正しく伝わったかを確かめられます。
質問は技術用語を使わず、業務の言葉で書きます。仕様レビューループからの起票については、第 2 ループの節で詳しく説明します。費用付きの質問は、実現性・コストのループの節で説明します。
決定表(Decision Table)
文章では見落としやすい条件の組み合わせを、表にして突きつけます。割引ルールの例です。
| 会員ランク | 購入金額 | 割引率 | 利用者への確認事項 |
|---|---|---|---|
| ゴールド | 10,000 円以上 | 20% | 規定どおり |
| シルバー | 10,000 円以上 | 10% | 規定どおり |
| ゴールド | 10,000 円未満 | 5% | 規定どおり |
| シルバー | 10,000 円未満 | 未定義 | Q&A 表 Q-001 で確認 |
これはエージェントが最初に作った時点の決定表です。条件を全部掛け合わせて並べるだけで、ヒアリングでは誰も口にしなかった組み合わせが空欄として浮かび上がります。浮かび上がった空欄は、そのまま Q&A 表に起票します(上の Q-001)。回答が返ってきたら、空欄を埋めて決定表を更新します。
実行可能な仕様書(日本語の Gherkin)
業務シナリオを、プログラミング言語ではなく日本語で書きます。Gherkin は日本語のキーワードに対応しているので、利用者はこれを業務の流れとして読み、正しいかどうかを判断できます。
# language: ja
機能: 会員ランクによる割引
シナリオ: ゴールド会員の割引適用
前提 ユーザーの会員ランクが "ゴールド" である
かつ カートの合計金額が "12,000円" である
もし 決済処理を実行する
ならば 20% の割引が適用される
かつ 請求額が "9,600円" になる
このシナリオは、後段でそのままテストの元になります。
画面プロトタイプ
利用者はテキストより「画面の動き」を見たほうが矛盾に気づきます。ヒアリング結果からエージェントに画面のモックを作らせ、「この入力でこの結果になります」と実際に触ってもらいます。
モックを動くコードとして作っておくと、ドキュメント用の画面資料もそこから作れます。触って確かめるモックと、紙で確認する画面資料の元が同じになるので、両者が食い違いません(具体例は後の実践例で紹介します)。
要求確定ループのコツ
- エージェントに意地悪な質問をさせる。 利用者は普段の業務(正常系)は話してくれますが、例外(異常系)は忘れがちです。ヒアリング結果をエージェントに読ませ、「もし〜だったらどうしますか?」という質問を作らせ、Q&A 表に起票します。
- オープンクエスチョンにしない。 「どうしますか?」ではなく、「A(〇〇とする)と B(××とする)のどちらが業務の実態に近いですか?」と、仮説付きの選択肢で聞きます。Q&A 表で質問と選択肢を必ずセットで書くのはこのためです。
- サインオフの対象をシナリオと Q&A 表にする。 Q&A 表の未回答が 0 件になり、利用者が「この日本語シナリオどおりの動作で問題ない」と承認したものを「要求仕様確定」と定義します。
実践例:MkDocs を正本にして、レビュー会用の PPTX スライドを生成する
私は要求確定ループの成果物を MkDocs のドキュメントとして管理し、レビュー会のたびに、そこから PPTX のスライドを Claude Code に生成させています。利用者にはスライドでレビューしてもらい、修正点を洗い出し、未決定事項はその場で決めてもらいます。決まったことは Q&A 表に書き戻します。
成果物は、すべて「編集できる元ファイル」と「MkDocs に埋め込む表示用ファイル」に分けて git で管理します。上で挙げた成果物に加えて、実務では業務フロー(BPMN)も使っています。なお、私の運用では Gherkin のシナリオは使っていません。
| 成果物 | 編集する元ファイル | MkDocs への埋め込み |
|---|---|---|
| 本文 | Markdown | そのまま |
| 図(構成図など) | draw.io の drawio ファイル | PNG に書き出して埋め込む |
| 業務フロー | BPMN XML(Camunda Modeler で編集できる形式) | スイムレーン図を PNG にレンダリングして埋め込む |
| 表(決定表・Q&A 表など) | CSV(表ごとに 1 ファイル) | mkdocs-table-reader-plugin で表として埋め込む |
| 画面プロトタイプ | React のモック | Playwright で各画面をレンダリングし、画面遷移図と画面ごとのデータ説明書(その画面で表示・入力する項目の説明)にして埋め込む |
CSV の埋め込みは MkDocs の標準機能ではないので、プラグインを入れています。mkdocs-table-reader-plugin を使うと、Markdown の中に {{ read_csv('decision_table.csv') }} のように書くだけで、CSV が表として描画されます。
この形にしている理由は 3 つです。
- 正本が 1 つで、差分が追える。 元ファイルはすべてテキスト形式(drawio も中身は XML)なので、何が変わったかがコミット履歴に残ります。エージェントも読み書きできるので、決定表の空欄検出や Q&A 表への起票、スライドの生成までそのまま任せられます。
- 業務フローを利用者と同じ図で話せる。 BPMN のスイムレーン図は「誰が・どの順で・何をするか」を利用者が見慣れた形で示せます。XML のまま残しておけば、後から Camunda Modeler で開いて直せます。
- 利用者は慣れた形式で確認できる。 MkDocs のサイトを読んでもらうより、レビュー会でスライドを一緒に見るほうが確認が進みます。
この方式で一番大事なのは、PPTX を生成物として扱い、手で直さないことです。レビュー会で決まったことは、スライドではなく元ファイル(Q&A 表の CSV など)に書き戻してから再生成します。スライドやメモに書いただけで終わると、次に生成したスライドで決定済みの事項が「未決定」に戻ってしまいます。
ここから先は、まだ試していない改善案です。スライドの生成に次の 2 つを組み込めば、この書き戻しを仕組みで支えられるはずです。
- スライドに ID と版を出す。 各スライドに Q&A 表の ID(Q-001 など)と生成元のコミットを入れておけば、スライド上の指摘から元ファイルの該当箇所までたどれます。利用者が承認した版も、コミットで特定できます。
- 「前回からの変更点」を冒頭に置く。 前回のレビュー会時点との差分と、Q&A 表の状態の変化(確認中 → 反映済み)から冒頭のスライドを作ります。利用者は、自分の回答が反映されたかを最初に確かめられます。Q&A 表のフィードバックの役割を、このスライドが担います。
ツール選び:MkDocs を続けつつ、PPTX の生成を Quarto に任せる
前提として押さえておきたいのは、この方式の要が「元ファイルをテキストで git 管理し、表示用の形式はそこから生成する」ことで、MkDocs はその手段の 1 つにすぎない点です。元ファイル(Markdown・CSV・drawio・BPMN XML・React)はどれも MkDocs 専用ではないので、ツールを替えても作り直すのは表示の部分だけです。
とはいえ、ツールを見直すきっかけはあります。MkDocs 周辺の状況が変わりつつあるためです(2026 年 10 月時点)。
- MkDocs の定番テーマ Material for MkDocs は保守モードに入りました。 2025 年 11 月の 9.7.0 が新機能を含む最後のリリースで、開発チームは後継の Zensical に移っています。
- MkDocs 2.0 では 1.x のプロジェクトをビルドできないとされています。Material 9.7.5 は依存する MkDocs を 2.0 未満に制限しました。当面は
mkdocs==1.6.1とプラグインのバージョンを固定して使うのが安全です。
移行先・併用先の候補として、次の 2 つを検討しました。
| 候補 | 良い点 | 今の運用での注意点 |
|---|---|---|
| Zensical | Material の作者による後継。mkdocs.yml をそのまま読める。ビルド処理が Rust 製で、変更したページだけを再ビルドする | まだアルファ版。プラグインは公式が作り直したものしか動かない。ただし mkdocs-table-reader-plugin は対応済み(0.0.41 から) |
| Quarto | 1 つの元ファイルから HTML・PPTX・DOCX・PDF を出せる。PPTX は reference-doc に指定したテンプレートで見た目を固定できる | ドキュメントサイトとしての出来は Material に及ばない。CSV の表は、コードセルで読み込んで出力する必要がある |
私の結論(これから試す方針)は、ドキュメントの正本は MkDocs 系のまま、PPTX の生成にだけ Quarto を挟むです。
今の方式では、スライドの要約・構成と、PPTX ファイルの作成の両方を Claude Code に任せています。そのため、生成のたびにレイアウトや書式が同じになるとは限りません。ぶれやすいのはファイル作成の側です。そこで次のように役割を分けます。
- Claude Code には、MkDocs の元ファイルからスライド用の
.qmd(Quarto の Markdown)を書かせる。要約と構成までがエージェントの担当 - PPTX への変換は
quarto render review.qmd --to pptxに任せる。同じ.qmdからは同じ PPTX ができる
後で出てくる型定義の生成(Phase 2)と同じく、LLM には中身だけを任せ、ファイルへの変換は決定論的なツールに任せる形です。生成元のコミットや Q&A 表の状態はスクリプトで .qmd に差し込めるので、上の改善案(スライドへの ID と版の表示、「前回からの変更点」スライド)もテンプレートに組み込みやすくなります。
Zensical は mkdocs-table-reader-plugin にも対応しているので、CSV の埋め込みを含めて今の構成のまま移れる見込みです。アルファ版を抜けた時点で、実際にビルドして移行を判断するのがよいと考えています。
レビューを終えて確定した MkDocs のドキュメントは、次の仕様レビューループと、その後のスキャフォールディングの入力(後で出てくる docs/spec.md に当たるもの)として使えます。
第 2 ループ:エージェント同士で回す「仕様レビューループ」
確定した要求を、開発に渡せる仕様書へ固めるループです。開発ループにテストハーネスがあるように、仕様にも「仕様評価ハーネス」を用意します。役割の違うエージェントを複数立てて、多面的にレビューさせます。
| 役割 | 仕事 |
|---|---|
| Planner(仕様生成) | 確定した要求(決定表・シナリオ)を、入出力・エラー・状態遷移まで含む開発向けの仕様に書き下す |
| Red Team(批判役) | 「通信が切れたら?」「ユーザーが連打したら?」「入力が空だったら?」とエッジケースや矛盾を突く |
| Domain Checker(ドメイン検証) | 既存システム・社内規約・セキュリティポリシーとの不整合を見つける |
| Formal Checker(形式検査) | 決定表・計算ルール・状態遷移・権限を Z3・TLA+・Alloy などで形式化し、矛盾・抜け・競合を検査器に探させる |
4 つの役は、次の順で動きます。
- Planner が、確定した要求を開発向けの仕様に書き下す
- Red Team・Domain Checker・Formal Checker が、それぞれの観点で矛盾や抜けを指摘する
- 指摘を振り分ける。開発者が決めてよいものは仕様に書き込み、利用者の判断が要るものは Q&A 表で要求確定ループに差し戻す
- 下の「ループを抜ける条件」を満たすまで 1 に戻る
このループでも Q&A 表を使います。エージェントの指摘には、開発者が決めてよいもの(リトライ回数やタイムアウト値など)と、利用者に聞かないと決まらないもの(通信断のときに注文を残すか、など)があります。前者はその場で仕様に書き込みます。後者は業務の言葉に言い換えて Q&A 表に起票し、要求確定ループに差し戻して利用者にヒアリングします。回答が返ってきたら仕様に反映し、反映結果を利用者にフィードバックします。
開発者が勝手に決めてはいけない論点を、黙って仕様に埋め込まない — Q&A 表は、そのための関所です。
形式手法の検査役:Z3・TLA+ で仕様の矛盾と抜けを探させる
Red Team の指摘は、エージェントが思いついた範囲にとどまります。条件が重なる計算ルールや並行処理の穴は、思いつきでは見落とします。そこで、形式手法の検査器に仕様を調べ尽くさせる役を足します。具体的なやり方は 形式手法を Claude Code で回す — Z3 と TLA+ で仕様の穴を見つけ、反例をテストに落とす で、実行結果つきで紹介しました。
ただし、形式手法が確かめられるのは「仕様の中に矛盾や抜けがないか」という、仕様内部の整合性だけです。「利用者が本当に必要としているものか」は判定できません。そのため要求確定ループの代わりにはならず、仕様レビューループの中で Red Team や Domain Checker を補う役として置きます。
検査の対象は、見つけたい問題の種類ごとに次の 3 つに絞ります。
| 対象 | 道具 | 見つかるもの |
|---|---|---|
| 決定表・計算ルール | Z3(SMT ソルバー) | どのルールにも当てはまらない入力(抜け)、2 つのルールが違う結果を出す入力(矛盾) |
| 業務フローのうち、並行・リトライ・取り消しが絡む部分 | TLA+ | 通信断や同時操作で、データが食い違ったまま残る実行順 |
| 権限・ロール | Alloy | 見えてはいけないデータが見えてしまう組み合わせ |
画面・文言・使い勝手は対象にしません。そこは画面プロトタイプとレビュー会で確かめます。順番どおりに進むだけの業務フローに TLA+ を使うのも、手間に見合いません。
形式手法の検査役の流れは次のとおりです。
- 仕様のうち機械的に検査できる部分(決定表、計算ルール、状態遷移)を抜き出して形式化する。形式化は Claude Code に任せる
- 判定は検査器(Z3、TLA+ のモデル検査器 TLC、Alloy Analyzer)の出力で決める。エージェントの「問題ありません」は信用しない
- 見つかった矛盾や抜けは、ほかの役の指摘と同じ基準で振り分ける。利用者の判断が要るものは、選択肢付きの質問に直して Q&A 表に起票する
- 反例はテストの入力として残す。仕様が決まった論点はスキャフォールディング(Phase 3)の Red テストに、回答待ちの論点は「失敗するはず」のテストにする
Q&A 表との相性が良いのは、反例が具体的な入力の例になるからです。利用者には Z3 の式を見せません。たとえば「注文額の 60% を下回らない」という下限ルールとクーポンが重なるケースでは、次のように聞きます。
3,001 円の注文でクーポンを使うと 1,700 円になり、下限の 1,800.6 円を割ります。A(下限の金額で止める)と B(この条件ではクーポンを使えなくする)のどちらが業務の実態に近いですか?
Q&A 表の「起票元」には「仕様レビュー(形式検査・Z3 の反例)」と書きます。回答が出るまでは、その反例を「失敗するはず」のテスト(pytest なら xfail(strict=True)、Vitest なら it.fails)として残しておけば、挙動が変わったときにテストが知らせてくれます。
注意点は 2 つあります。
- 形式化は Claude Code の解釈です。 端数処理や割引の適用順など、自然言語の仕様から式を作るときに置いた前提も、Q&A 表で利用者に確認します(Q-003 の丸め方がその例です)。前提を読み違えたモデルでは、「反例なし」は何も保証しません。
- 「反例なし」は空振りを疑います。 モデルが正常な状態にすら到達できないと、検査器は実質的に何も調べずに「反例なし」を返します。正常な状態に到達できることと、わざと壊した変異版(たとえば下限チェックを外したモデル)で反例が出ることを、セットで確かめます。
ループを抜ける条件
ループを抜ける条件(Exit Criteria)は、次の 4 つにします。
- 未決定事項(TBD / TODO)が 0 件で、Q&A 表の未回答も 0 件である
- 仕様チェックリスト(入出力定義・エラーハンドリング・境界値・状態遷移)がすべて埋まっている
- 仕様書から自動テストのケースを漏れなく抽出できる
- 形式検査の対象で検査器が反例を返さず(空振りでないことも確認済み)、見つかった矛盾や抜けはすべて仕様の修正か Q&A 表の回答で解決済みである
3 つ目が肝心です。決定表の全行と Gherkin の全シナリオに、対応するテストケースが 1 つずつあるかで判定します。テストを書き起こせない仕様は、まだ確定していないと見なします。4 つ目は、形式検査の対象がない仕様(順番どおりに進むだけのフローなど)では省略してかまいません。
並行ループ:予算責任者と回す「実現性・コストのループ」
決めるべきことは機能だけではありません。コストも要求の一部です。コストは、リリースまでの開発コストと、リリース後の運用コストの 2 つに分かれます。開発コストは開発手法と体制で、運用コストは実行基盤の設計で大きく変わります。
これらは要求確定ループには入れず、別のループとして組みます。ただし、仕様が確定してから回すのではなく、要求確定・仕様レビューの 2 つと並行して回します。
別ループにして、並行で回す理由
別ループにするのは、決める人・必要な知識・終わる条件が違うからです。
| 観点 | 要求確定ループ | 実現性・コストのループ |
|---|---|---|
| 決める人 | 業務の利用者 | 予算を持つ人(事業責任者・発注者) |
| 必要な知識 | 業務ルール | 開発手法・体制・アーキテクチャ・クラウドの料金 |
| 問い | 作るべきものは正しいか | その範囲を、この予算と期間で作り、運用できるか |
| 終わる条件 | 利用者のサインオフ | 範囲・開発費・運用費・期間についての予算責任者のサインオフ |
利用者に開発手法やインフラの話をしても判断できません。予算責任者に業務ルールの細部を決めてもらうこともできません。1 つのループに混ぜると、どちらの論点も収束しにくくなります。
並行で回すのは、コストが作る範囲とトレードオフの関係にあるからです。 仕様を確定させてから見積もって予算を超えれば、要求確定ループをやり直すことになります。コストが効いてくる論点は、要求を固めている途中で、選択肢の一部として利用者に見せる必要があります。
開発費・運用費を見積もる手順
- 要求確定ループの途中成果物(決定表、画面一覧、非機能要求の回答)を受け取る
- 開発コストを見積もる。規模は途中成果物から機械的に数える(決定表の行数、画面数、エンドポイントの候補数、形式検査する状態遷移の数など)。そこに開発手法(エージェントの開発ループを使うか)と体制を掛け合わせる。スキャフォールディングの Phase 1 で
spec_ir.jsonができたら、そこから数え直して見積りを更新する - 運用コストを見積もる。実行基盤の案を 2〜3 個つくり、それぞれの月額と運用の人手を出す
- 費用が効いてくる論点を、費用付きの選択肢にして Q&A 表に起票する
- 範囲・開発費・運用費・期間について、予算責任者のサインオフをもらう
手順 3 の見積りにも、後述の「品質のガードレール」と同じ原則を当てはめます。エージェントには基盤の案を IaC(Terraform など)で書かせ、月額は Infracost(Terraform のコードからクラウドの月額を見積もるツール)やクラウドの料金計算ツールで出します。エージェントが文章で答えた概算は、そのまま使いません。
運用コストは非機能要求で決まる
運用費を左右するのは、インフラの設計そのものより、その前提になる非機能要求です。何時間止まってよいか、データを何年残すか、ピーク時にどれだけアクセスが来るか、といった条件です。これらの条件には、利用者しか答えられません。
そこで、質問の起票と見積りは実現性・コストのループが受け持ち、利用者へのヒアリングは要求確定ループの場で行う、と分担します。利用者が選んだ結果を合計した範囲と費用を、最後に予算責任者が承認します。Q&A 表には、たとえば次のような行を起票します(回答・反映先・状態の列は省略しています。費用の数値は説明用の仮の値です)。
| No | 起票元 | 質問(利用者向け) | 選択肢 | 費用への影響 |
|---|---|---|---|---|
| Q-005 | 実現性・コスト(運用費の見積り) | 夜間(0〜6 時)のシステム停止について、A・B のどちらにしますか? | A: 夜間は止まってよい / B: 24 時間稼働させる | B は冗長構成が要り、月額が A の約 2 倍、開発は 2 週間増える(仮の値) |
| Q-006 | 実現性・コスト(運用費の見積り) | 注文データを検索できる期間は、A・B のどちらにしますか? | A: 直近 1 年(それ以前はアーカイブ) / B: 全期間 | B は保管と検索の費用が年ごとに増える(仮の値) |
Q&A 表に「費用への影響」の列を足すと、利用者は機能と費用を同じ表の上で比べて選べます。費用を知らないまま「全部 B で」と答えてもらうより、予算に収まる合意にたどり着きやすくなります。
成果物の置き場所:正本のリポジトリは 1 つ、MkDocs のサイトは読者ごとに 2 つ
要求確定・仕様レビューの成果物と、実現性・コストのループの成果物は、1 つのリポジトリ(正本)の中に、読者ごとのドキュメントツリーを 2 つ置いて管理します。1 つの文書に混ぜるのも、リポジトリごと分けるのも勧めません。
正本を 1 つにするのは、2 つの側が同じものを共有するからです。Q&A 表には両方のループが起票し、決定表と非機能要求の回答は、実現性・コストのループが見積りの入力として読みます。リポジトリを分けるとどちらかに写しができ、いずれ食い違います。それに、後述する関門(スキャフォールディングへ進む前の確認)で確かめたいのは、2 つのサインオフが同じ範囲を対象にしているかです。同じリポジトリなら、これをコミットで機械的に確かめられます。
ツリーを 2 つに分けるのは、読者と承認者が違うからです(上の比較表のとおり)。加えて、人月単価や体制のように、利用者向けの資料に載せられない情報もあります。サインオフも別々に進むので、版はループごとに付けます(後述のタグ)。
実践例の MkDocs の構成に当てはめると、次のようになります(実現性・コストのループ側は、まだ試していない構成案です)。
docs/
├── shared/ # 両方のサイトが参照する
│ ├── qa.csv # Q&A 表(「費用への影響」列を含む)
│ ├── decision_table.csv
│ └── nfr.csv # 非機能要求の回答
├── requirements/ # 利用者向け:業務ルール・画面・業務フロー
├── feasibility/ # 予算責任者向け:規模の数え方・開発手法・体制・基盤案・見積り
│ └── schedule.csv # スケジュールの正本(後述)
└── proposal/ # 決裁者向け提案書の手書き部分(後述)
mkdocs.requirements.yml # 利用者向けサイト(shared + requirements)
mkdocs.feasibility.yml # 予算責任者向けサイト(shared + feasibility)
スキャフォールディングの入力になる docs/spec.md(Phase 4 のツリー)は、このうち shared/ と requirements/ を確定版としてまとめたものです。
- サイトは読者ごとにビルドする。
mkdocs build -f mkdocs.requirements.ymlのように設定ファイルを切り替え、レビュー会の PPTX も読者ごとに生成します。利用者向けの設定では、exclude_docs(MkDocs 1.5 以降)でfeasibility/とproposal/を除外します。navから外すだけでは、ページ自体はビルドされ、URL を知っていれば開けてしまうためです。 shared/には、両方の読者に見せてよいものだけを置く。shared/の CSV は、両方のサイトに静的ファイルとしても出力され、URL で直接ダウンロードできます。CSV は表として埋め込むので、パスは docs ディレクトリ基準({{ read_csv('shared/qa.csv') }})で書くと、どちらのサイトでも同じように解決されます。requirements/からfeasibility/へはリンクしない。 見積りの項目には、根拠になった Q&A 表の ID(Q-005 など)や決定表の行を書きます。逆に、利用者に見せる費用はshared/qa.csvの「費用への影響」の列の要約だけにします。こうすれば、利用者向けの資料に内訳は出ません。- サインオフはタグで記録する。 利用者の承認は
req-signoff-v3、予算責任者の承認はbudget-signoff-v2のように、ループごとにタグを付けます。2 つのタグの時点でshared/の中身が違えば、2 つの承認は別の範囲を見ていたことになるので、どちらかの承認を取り直します。CI ではgit diff --quiet req-signoff-v3 budget-signoff-v2 -- docs/shared/で確かめられます。GitHub Actions で走らせる場合は、タグを取得できるようactions/checkoutにfetch-depth: 0を指定します。
例外は、アクセス権の上でも利用者に費用の内訳を見せられない場合です。受託開発で単価や利益率を発注者に開示しない場合が典型です。このときは、単価や内訳だけを別の非公開リポジトリに置き、正本には集計結果(範囲ごとの開発費と月額)だけを書き戻します。Q&A 表などの共有ファイルは、正本から動かしません。
スキャフォールディングへ進む関門:利用者と予算責任者の 2 つのサインオフ
仕様レビューループを抜けただけでは、スキャフォールディングに進みません。仕様の確定(利用者のサインオフ)と、範囲・開発費・運用費・期間の合意(予算責任者のサインオフ)の両方がそろって、初めて先へ進みます。冒頭の全体図では省略していますが、③の前にはこの関門があります。2 つのサインオフが同じ範囲を対象にしていることは、前節のタグ(req-signoff-v3 と budget-signoff-v2)の時点で shared/ が一致するかで確かめます。
予算責任者とは別に決裁者がいる場合は、次の節の提案書で決裁者の承認を得るところまでを、予算側のサインオフに含めます。
提案書とスケジュール:決裁者向けの資料は正本から生成する
最終的な案件概要(グランドサマリ)は、決裁者向けの提案書にまとめます。提案書は 3 つ目の MkDocs サイトではなく、正本のツリー(shared/・requirements/・feasibility/)から生成する DOCX や PDF の文書です。手で書くのは、ほかのどこにもない部分(背景と目的、推奨案とその理由、比較した代替案、リスクと前提条件)だけで、その部分だけを proposal/ に置きます。範囲(要求の一覧)・費用(範囲ごとの開発費と月額)・期間・主な決定事項(Q&A 表で選んだ選択肢と、その費用への影響)の表は、集計から生成します。
グランドサマリを feasibility/ にまとめるのは勧めません。
- 読者が違う。 提案書を読むのは、最終的に承認する決裁者(経営層、受託開発なら発注者側の決裁権者)です。
feasibility/の読者は予算責任者と開発者・アーキテクトで、中身は規模の数え方や基盤案といった検討の過程です。決裁者が知りたいのは、結論と根拠の要約です。 - 内訳が混ざる。
feasibility/には単価や体制の内訳が入ります。提案書を発注者に出すなら、これは載せられません。 - 要求の写しができる。 要求の要約まで
feasibility/に書くと、要求の内容が 2 か所にでき、いずれ食い違います。
スケジュールも同じ考え方で分けます。詳細なスケジュールは feasibility/ で作り、提案書にはそこから生成したマイルストーンの要約だけを載せます。
| 観点 | feasibility/ | 提案書 |
|---|---|---|
| 粒度 | タスク単位(WBS)。依存関係・担当・工数つき | フェーズとマイルストーン(要求確定、開発開始、受け入れテスト、リリースなど) |
| 図 | 詳細なガントチャート | マイルストーンだけの簡略なガントチャート |
| 読者 | 予算責任者・開発者 | 決裁者 |
詳細なスケジュールを feasibility/ に置くのは、見積りと同じ材料から作るからです。タスクの期間は、開発費の見積りに使った規模と体制から決まります。範囲が変われば、費用とスケジュールが同時に変わります。2 つを別の場所で手作業で直すと、提案書の費用と期間がずれます。
スケジュールの正本も、ほかの成果物と同じくテキストにします。schedule.csv には、タスクを 1 行ずつ書きます。列は ID・タスク名・対応する要求や Q&A の ID・開始日・期間・先行タスク・担当・マイルストーンかどうかです。これをスクリプトで Mermaid のガントチャート記法に変換し、mermaid-cli(mmdc)で PNG に書き出します。マイルストーンの行だけを抜き出せば、提案書用の簡略な図になります。drawio や BPMN と同じく、編集するのは元ファイルで、図は生成物です。
決裁者向けのスケジュールには、開発側の作業だけでなく、利用者側の期限も必ず入れます。Q&A 表の回答期限、レビュー会の日程、受け入れテストの期間です。これらも利用者側のタスクとして schedule.csv に書きます。利用者の回答が遅れればスケジュールも遅れるので、決裁者が判断すべき前提条件になります。
提案書は、関門で 2 つのサインオフが同じ範囲を指していると確かめた時点で生成し、表紙に生成元のタグを書きます。こうしておけば、承認された内容と提案書の数字が食い違っていないかを、後からタグで確かめられます。出力は、要求確定ループの「ツール選び」で PPTX の生成に使う Quarto に任せれば、同じ元ファイルから DOCX や PDF にできます(PDF には TeX の環境が別に要ります)。範囲・費用の表は、そこで書いたとおりコードセルで CSV を読み込んで出力します。
予算責任者と決裁者が同じ人で、単価を隠す必要もない小さな案件なら、feasibility/ のトップページをサマリにしてもかまいません。読者が分かれた時点で、proposal/ に切り出します。
なお、運用コストは見積りで終わりません。リリース後に実際の費用と見積りを比べ、ずれが大きければ非機能要求や基盤の設計を見直す、運用のフィードバックループ(FinOps:クラウド費用を継続的に計測し、最適化していく取り組み)が別に要ります。これは開発ループより後ろの話なので、この記事では扱いません。
仕様駆動のスキャフォールディング
仕様レビューループの本当の価値は、次の開発ループで使うテストと評価基準をそのまま渡せることにあります。確定仕様書(Markdown / Gherkin)を入力に、4 つのフェーズで開発ループの起点を一括生成します。
やっていることは、実態としてはスキャフォールディングです。契約(OpenAPI と型)、まだ失敗するテスト、モックとシードデータ、それらを置くディレクトリ構成 — 実装が入る前の骨組みを一式組み立てます。rails generate のようなジェネレーターが雛形を出すのと同じ役割です。ただし、一般的なスキャフォールディングとは 2 点違います。
- 入力がテンプレートではなく、確定した仕様書である。 仕様が変わったら、骨組みを再生成できます。
- 生成物の一部は人もエージェントも手で直さない。 普通の雛形は、出たコードを書き換えて育てていきます。ここで生成するテストと型は読み取り専用のゴールで、実装エージェントが手を入れるのは残りの実装コードだけです。
Phase 1:仕様の構造化
人間向けの文書を、LLM が誤解なく扱える中間表現(spec_ir.json)に変換します。抽出するのは次の 3 つです。
- API エンドポイント(パス・メソッド・パラメータ・ステータスコード)
- 正常系・異常系・境界値の条件
- データモデル(エンティティの関係と制約)
Phase 2:契約(Contract)の生成
API のインターフェースと型を先に確定させます。
- OpenAPI 3.1 の
openapi.yamlを LLM で生成する。 生成後は Redocly CLI や Spectral などのリンターで構文と規約をチェックします。 - 型定義は LLM を通さずに出力する。
openapi-typescriptのような決定論的なツールでopenapi.yamlからtypes.tsを生成します。ツールで変換すれば、LLM 由来の誤字や揺れが入りません。
Phase 3:テストハーネスの生成
実装エージェントが「全部通すべき目標」になるテスト一式を作ります。
- 契約テスト — OpenAPI スキーマとレスポンスを照合する。Prism(OpenAPI からモックサーバーや検証用プロキシを立てるツール)で照合する、supertest(Node.js の HTTP サーバーをテストするライブラリ)で実装のレスポンスを確かめる、などの方法がある
- ユニット / ドメインロジックのテスト — 仕様書の境界値や状態遷移表から、TDD でいう Red(実装前なのでまだ失敗する状態)のテストを作る
- モック・シードデータ — フロントエンド開発や結合テスト用に、MSW(ネットワーク要求を横取りして応答を返す API モックのライブラリ)のモック定義や、Prisma(TypeScript の ORM)のシードデータを同時に作る
境界値テストの生成例です(Vitest + supertest)。
// tests/generated/user-registration.test.ts
import { describe, it, expect } from "vitest";
import request from "supertest";
import { app } from "../../src/app";
describe("ユーザー登録 API - 境界値", () => {
it("パスワードが 7 文字なら 400 と INVALID_PASSWORD を返す", async () => {
const res = await request(app).post("/api/users").send({
email: "test@example.com",
password: "passwor", // 境界の 1 文字手前
});
expect(res.status).toBe(400);
expect(res.body.code).toBe("INVALID_PASSWORD");
});
it("パスワードが 8 文字なら 201 を返す", async () => {
const res = await request(app).post("/api/users").send({
email: "test@example.com",
password: "password", // ちょうど境界
});
expect(res.status).toBe(201);
});
});
Phase 4:開発ループへの引き渡し
生成物を決まったディレクトリに置き、実装エージェントが触ってよい範囲を決めます。
.
├── docs/
│ ├── spec.md # 入力:確定仕様書
│ └── spec_ir.json # Phase 1 の出力(中間表現)
├── src/
│ ├── generated/
│ │ ├── openapi.yaml # Phase 2 の出力
│ │ └── types.ts # Phase 2 の出力(ツールで生成した型)
│ └── ... # 実装エージェントが書くコード
└── tests/
└── generated/ # Phase 3 の出力(生成されたテスト群)
├── contract.test.ts
└── unit.test.ts
実行の順序
CI やローカルのスクリプトとして回すなら、次の順番になります。
- 仕様書の検証と構造化 — 確定仕様書を
docs/spec.mdにコミットし、フォーマットの誤りがないかを機械的にチェックしてからspec_ir.jsonに変換する - OpenAPI と型の生成 — LLM が
spec_ir.jsonからopenapi.yamlを出力し、リンターを通過したらopenapi-typescriptでtypes.tsを確定する - テスト生成と Red の確認 — LLM が
openapi.yamlとspec.mdからテストを生成し、実行して全テストが失敗することを確認する。実装前に通ってしまうテストは、何も検査していない - コーディングエージェントの起動 — Claude Code などの実装エージェントに「
tests/generated/のテストをすべて Green にし、src/generated/types.tsに準拠した実装を書け」と指示して開発ループに入る
実装エージェントは「このテストを全部通す」という明確なゴールを持ってループに入れるので、迷走しにくくなります。
品質のガードレール
スキャフォールディングを信頼できるものにするために、2 つの約束を守ります。
LLM の出力は決定論的なツールで二重チェックする
LLM が生成した OpenAPI やテストコードは、必ず tsc --noEmit・ESLint・Spectral などの CLI で機械的に検証します。エラーが出たら、そのログをエージェントに返して自動修正させます。生成するのは LLM でも、合否を決めるのはツールです。
テストは読み取り専用にし、実装だけを変えさせる
実装エージェントにテストを書き換える権限を与えてはいけません。 テストを書き換えられるなら、エージェントにとって一番速い「Green にする方法」はテストを緩めることになってしまいます。生成したテストはスキャフォールディングで固定したルール(ハーネス)として読み取り専用で扱い、変更してよいのは実装コードだけにします。
エージェントの書き込み対象から外すだけでなく、CI で git diff --exit-code tests/generated/ を走らせ、生成済みのテストが書き換えられていたら失敗させておくと確実です。
まとめ
開発全体は、上流の 2 つのループと開発ループの 3 段で回します。上流の 2 つと並行して、実現性・コストのループも回します。
- 要求確定ループ(利用者 ⇔ 開発者) — Q&A 表・決定表・日本語シナリオ・画面プロトタイプで、求めるルールと条件が合っているかを確定する
- 仕様レビューループ(エージェント同士) — 批判役のエージェントと形式手法の検査器で穴を潰し、テストを書き起こせる仕様書にする。利用者の判断が要る論点は Q&A 表で差し戻す
- 実現性・コストのループ(予算責任者 ⇔ 開発者・アーキテクト) — 開発費と運用費を見積もり、費用付きの選択肢を Q&A 表で利用者に見せる。予算責任者の合意が、スキャフォールディングへ進む条件になる。合意した範囲・費用・スケジュールは、決裁者向けの提案書として生成する
- 開発ループ(開発者 ⇔ コーディングエージェント) — 仕様から生成したテスト・OpenAPI・型定義をゴールに、確定したルールどおりに動くかを自動検証する
開発ループのハーネスをいくら磨いても、入力の仕様が間違っていれば間違ったものが速くできるだけです。上流の 2 つのループで Validation を、並行する実現性・コストのループで実現性を、開発ループで Verification を担う — シフトレフトをエージェント開発に当てはめると、この形になります。上流で仕様を固めておけば、開発ループはエージェントに任せても収束に向かいやすくなります。


