本文へスキップ
hdknr blog
戻る

要求仕様書作成ループ — コーディングエージェントの開発ハーネスの手前に「要求確定」と「仕様レビュー」のループを置く

更新日:

コーディングエージェントを使った開発では、テスト・ビルド・静的解析で囲った「開発ループ(ハーネス)」を組み、エージェントにそのループを回させるやり方が定着してきました。ただ、このループが保証するのは「仕様どおりに動くか」までです。仕様そのものが間違っていれば、間違ったものを高速に作り続けるだけになります。

そこで、開発ループの手前にもう 2 つループを置く構成を考えました。

  1. 要求確定ループ — システム利用者と開発者のあいだで、業務ルールを確定させる
  2. 仕様レビューループ — 確定した要求を、エージェント同士の批判と形式手法の検査で仕様書として固める

この 2 つと並行して、開発費と運用費を予算責任者と詰める「実現性・コストのループ」も回します(全体図には含めず、後の節で別の図を示します)。さらに、確定した仕様書からテストコードや OpenAPI 定義を自動生成し、開発ループの「ゴール」として渡すスキャフォールディングでつなぎます。この記事では、その全体像と各ループの組み方を整理します。

ヒアリングメモから始まり、利用者と合意する要求確定ループ、エージェントが批判し、形式手法の検査器が矛盾を探す仕様レビューループ(利用者の判断が要る論点は Q&A 表で要求確定ループに差し戻す)、OpenAPI とテストの骨組みを生成する仕様駆動のスキャフォールディング、コーディングエージェントがテストを Green にする開発ループの順に進む全体像。上の 2 つが「作るべきものは正しいか」、最後のループが「作ったものは仕様どおりか」を担う

なぜ開発ループの前にループが要るのか

曖昧な仕様は、エージェントが増幅する

人間のエンジニアなら、仕様書に矛盾や「要検討(TBD)」があっても、ドメイン知識で補ったり「ここはどうしますか?」と聞き返したりできます。自律型のコーディングエージェントは違います。曖昧な仕様に対しても「それらしいコード」を大量に、速く書いてしまいます。

その結果、仕様の矛盾がそのまま不具合コードになり、テストで弾かれ続けてループが空回りします。悪くすると、テストを通すための辻褄合わせに迷走します。Garbage In, Garbage Out が、エージェントの速度で増幅されるわけです。

手戻りのコストがまるで違う

仕様の不備を開発ループに持ち込むと、コード修正・テスト実行・環境リセットという重い往復が発生します。一方、仕様書の段階でエージェントに相互批判させたり、エッジケースを洗い出させたりするなら、かかるのはトークン代だけです。

ここで役割を整理しておきます。

ループ保証するもの問い
要求確定・仕様レビューValidation(妥当性確認)作るべきものは正しく定義されているか
開発ループVerification(検証)作ったものは仕様どおりに動くか
実現性・コスト(上流と並行)Feasibility(実現性)その範囲を、この予算と期間で作り、運用できるか

開発ループだけでは Validation を担保できません。上流でそれを担うループが別に必要です。品質を確保する工程を上流へ前倒しする、いわゆるシフトレフトの考え方です。

以下、図の上から順に要求確定ループと仕様レビューループを見ます。続いて、この 2 つと並行して回す実現性・コストのループを説明し、最後に仕様駆動のスキャフォールディングに進みます。

第 1 ループ:利用者と回す「要求確定ループ」

最初に考えた構成は「仕様レビューループ → 開発ループ」の 2 段でした。しかしこれは開発者側の技術的なループに偏っています。その前に、システム利用者(業務側・ステークホルダー)と要求を確定させるループが要ります。

利用者が気にするのは OpenAPI や型定義ではありません。「自分たちの業務ルールや条件が漏れなく、矛盾なくシステムに反映されるか」だけです。そのため、このループでは技術用語を使わず、業務シナリオ・ルール・入出力イメージの粒度で確認を回します。

  1. 構造化・可視化 — エージェント(Requirement Agent)がヒアリングメモや断片的な要望を、業務ルール・判定表・ユースケースに変換する
  2. 矛盾・エッジケースの検出 — 別のエージェント(Business Red Team Agent)が「もし〇〇の場合は?」「このルールとあのルールが衝突していませんか?」を洗い出し、Q&A 表に起票する
  3. 利用者の確認・合意 — 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 表には次の役割があります。

質問は技術用語を使わず、業務の言葉で書きます。仕様レビューループからの起票については、第 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円" になる

このシナリオは、後段でそのままテストの元になります。

画面プロトタイプ

利用者はテキストより「画面の動き」を見たほうが矛盾に気づきます。ヒアリング結果からエージェントに画面のモックを作らせ、「この入力でこの結果になります」と実際に触ってもらいます。

モックを動くコードとして作っておくと、ドキュメント用の画面資料もそこから作れます。触って確かめるモックと、紙で確認する画面資料の元が同じになるので、両者が食い違いません(具体例は後の実践例で紹介します)。

要求確定ループのコツ

実践例: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 が表として描画されます。

Markdown・drawio・BPMN XML・CSV・React モックの元ファイルを git で管理し、MkDocs のドキュメントにまとめ、そこからレビュー会用の PPTX スライドを生成する流れ。レビュー会で決まったことはスライドではなく Q&A 表の CSV などの元ファイルに書き戻し、再生成する

この形にしている理由は 3 つです。

この方式で一番大事なのは、PPTX を生成物として扱い、手で直さないことです。レビュー会で決まったことは、スライドではなく元ファイル(Q&A 表の CSV など)に書き戻してから再生成します。スライドやメモに書いただけで終わると、次に生成したスライドで決定済みの事項が「未決定」に戻ってしまいます。

ここから先は、まだ試していない改善案です。スライドの生成に次の 2 つを組み込めば、この書き戻しを仕組みで支えられるはずです。

ツール選び:MkDocs を続けつつ、PPTX の生成を Quarto に任せる

前提として押さえておきたいのは、この方式の要が「元ファイルをテキストで git 管理し、表示用の形式はそこから生成する」ことで、MkDocs はその手段の 1 つにすぎない点です。元ファイル(Markdown・CSV・drawio・BPMN XML・React)はどれも MkDocs 専用ではないので、ツールを替えても作り直すのは表示の部分だけです。

とはいえ、ツールを見直すきっかけはあります。MkDocs 周辺の状況が変わりつつあるためです(2026 年 10 月時点)。

移行先・併用先の候補として、次の 2 つを検討しました。

候補良い点今の運用での注意点
ZensicalMaterial の作者による後継。mkdocs.yml をそのまま読める。ビルド処理が Rust 製で、変更したページだけを再ビルドするまだアルファ版。プラグインは公式が作り直したものしか動かない。ただし mkdocs-table-reader-plugin は対応済み(0.0.41 から)
Quarto1 つの元ファイルから HTML・PPTX・DOCX・PDF を出せる。PPTX は reference-doc に指定したテンプレートで見た目を固定できるドキュメントサイトとしての出来は Material に及ばない。CSV の表は、コードセルで読み込んで出力する必要がある

私の結論(これから試す方針)は、ドキュメントの正本は MkDocs 系のまま、PPTX の生成にだけ Quarto を挟むです。

今の方式では、スライドの要約・構成と、PPTX ファイルの作成の両方を Claude Code に任せています。そのため、生成のたびにレイアウトや書式が同じになるとは限りません。ぶれやすいのはファイル作成の側です。そこで次のように役割を分けます。

  1. Claude Code には、MkDocs の元ファイルからスライド用の .qmd(Quarto の Markdown)を書かせる。要約と構成までがエージェントの担当
  2. 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 つの役は、次の順で動きます。

  1. Planner が、確定した要求を開発向けの仕様に書き下す
  2. Red Team・Domain Checker・Formal Checker が、それぞれの観点で矛盾や抜けを指摘する
  3. 指摘を振り分ける。開発者が決めてよいものは仕様に書き込み、利用者の判断が要るものは Q&A 表で要求確定ループに差し戻す
  4. 下の「ループを抜ける条件」を満たすまで 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+ を使うのも、手間に見合いません。

形式手法の検査役の流れは次のとおりです。

  1. 仕様のうち機械的に検査できる部分(決定表、計算ルール、状態遷移)を抜き出して形式化する。形式化は Claude Code に任せる
  2. 判定は検査器(Z3、TLA+ のモデル検査器 TLC、Alloy Analyzer)の出力で決める。エージェントの「問題ありません」は信用しない
  3. 見つかった矛盾や抜けは、ほかの役の指摘と同じ基準で振り分ける。利用者の判断が要るものは、選択肢付きの質問に直して Q&A 表に起票する
  4. 反例はテストの入力として残す。仕様が決まった論点はスキャフォールディング(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 つあります。

ループを抜ける条件

ループを抜ける条件(Exit Criteria)は、次の 4 つにします。

3 つ目が肝心です。決定表の全行と Gherkin の全シナリオに、対応するテストケースが 1 つずつあるかで判定します。テストを書き起こせない仕様は、まだ確定していないと見なします。4 つ目は、形式検査の対象がない仕様(順番どおりに進むだけのフローなど)では省略してかまいません。

並行ループ:予算責任者と回す「実現性・コストのループ」

決めるべきことは機能だけではありません。コストも要求の一部です。コストは、リリースまでの開発コストと、リリース後の運用コストの 2 つに分かれます。開発コストは開発手法と体制で、運用コストは実行基盤の設計で大きく変わります。

これらは要求確定ループには入れず、別のループとして組みます。ただし、仕様が確定してから回すのではなく、要求確定・仕様レビューの 2 つと並行して回します。

要求確定ループと仕様レビューループの横で、予算責任者と開発者・アーキテクトが回す実現性・コストのループが並行して動く図。要求確定ループの途中成果物(決定表・画面一覧・非機能要求の回答)を受け取り、開発コストは途中成果物の規模・開発手法・体制から、運用コストは非機能要求から IaC で基盤案を作り、見積りツールで月額を出す。費用付きの選択肢は「費用への影響」の列を持つ Q&A 表に起票し、利用者に聞く。利用者のサインオフによる仕様の確定と、予算責任者との予算合意の両方がそろうと、スキャフォールディングへ進む関門を通る。リリース後に実際の運用費を見積りと比べる FinOps は、記事の範囲外として図の下端に注記している

別ループにして、並行で回す理由

別ループにするのは、決める人・必要な知識・終わる条件が違うからです。

観点要求確定ループ実現性・コストのループ
決める人業務の利用者予算を持つ人(事業責任者・発注者)
必要な知識業務ルール開発手法・体制・アーキテクチャ・クラウドの料金
問い作るべきものは正しいかその範囲を、この予算と期間で作り、運用できるか
終わる条件利用者のサインオフ範囲・開発費・運用費・期間についての予算責任者のサインオフ

利用者に開発手法やインフラの話をしても判断できません。予算責任者に業務ルールの細部を決めてもらうこともできません。1 つのループに混ぜると、どちらの論点も収束しにくくなります。

並行で回すのは、コストが作る範囲とトレードオフの関係にあるからです。 仕様を確定させてから見積もって予算を超えれば、要求確定ループをやり直すことになります。コストが効いてくる論点は、要求を固めている途中で、選択肢の一部として利用者に見せる必要があります。

開発費・運用費を見積もる手順

  1. 要求確定ループの途中成果物(決定表、画面一覧、非機能要求の回答)を受け取る
  2. 開発コストを見積もる。規模は途中成果物から機械的に数える(決定表の行数、画面数、エンドポイントの候補数、形式検査する状態遷移の数など)。そこに開発手法(エージェントの開発ループを使うか)と体制を掛け合わせる。スキャフォールディングの Phase 1 で spec_ir.json ができたら、そこから数え直して見積りを更新する
  3. 運用コストを見積もる。実行基盤の案を 2〜3 個つくり、それぞれの月額と運用の人手を出す
  4. 費用が効いてくる論点を、費用付きの選択肢にして Q&A 表に起票する
  5. 範囲・開発費・運用費・期間について、予算責任者のサインオフをもらう

手順 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/ を確定版としてまとめたものです。

例外は、アクセス権の上でも利用者に費用の内訳を見せられない場合です。受託開発で単価や利益率を発注者に開示しない場合が典型です。このときは、単価や内訳だけを別の非公開リポジトリに置き、正本には集計結果(範囲ごとの開発費と月額)だけを書き戻します。Q&A 表などの共有ファイルは、正本から動かしません。

スキャフォールディングへ進む関門:利用者と予算責任者の 2 つのサインオフ

仕様レビューループを抜けただけでは、スキャフォールディングに進みません。仕様の確定(利用者のサインオフ)と、範囲・開発費・運用費・期間の合意(予算責任者のサインオフ)の両方がそろって、初めて先へ進みます。冒頭の全体図では省略していますが、③の前にはこの関門があります。2 つのサインオフが同じ範囲を対象にしていることは、前節のタグ(req-signoff-v3 と budget-signoff-v2)の時点で shared/ が一致するかで確かめます。

予算責任者とは別に決裁者がいる場合は、次の節の提案書で決裁者の承認を得るところまでを、予算側のサインオフに含めます。

提案書とスケジュール:決裁者向けの資料は正本から生成する

最終的な案件概要(グランドサマリ)は、決裁者向けの提案書にまとめます。提案書は 3 つ目の MkDocs サイトではなく、正本のツリー(shared/・requirements/・feasibility/)から生成する DOCX や PDF の文書です。手で書くのは、ほかのどこにもない部分(背景と目的、推奨案とその理由、比較した代替案、リスクと前提条件)だけで、その部分だけを proposal/ に置きます。範囲(要求の一覧)・費用(範囲ごとの開発費と月額)・期間・主な決定事項(Q&A 表で選んだ選択肢と、その費用への影響)の表は、集計から生成します。

グランドサマリを feasibility/ にまとめるのは勧めません。

スケジュールも同じ考え方で分けます。詳細なスケジュールは 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 つです。

Phase 2:契約(Contract)の生成

API のインターフェースと型を先に確定させます。

  1. OpenAPI 3.1 の openapi.yaml を LLM で生成する。 生成後は Redocly CLI や Spectral などのリンターで構文と規約をチェックします。
  2. 型定義は LLM を通さずに出力する。 openapi-typescript のような決定論的なツールで openapi.yaml から types.ts を生成します。ツールで変換すれば、LLM 由来の誤字や揺れが入りません。

Phase 3:テストハーネスの生成

実装エージェントが「全部通すべき目標」になるテスト一式を作ります。

境界値テストの生成例です(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 やローカルのスクリプトとして回すなら、次の順番になります。

  1. 仕様書の検証と構造化 — 確定仕様書を docs/spec.md にコミットし、フォーマットの誤りがないかを機械的にチェックしてから spec_ir.json に変換する
  2. OpenAPI と型の生成 — LLM が spec_ir.json から openapi.yaml を出力し、リンターを通過したら openapi-typescript で types.ts を確定する
  3. テスト生成と Red の確認 — LLM が openapi.yaml と spec.md からテストを生成し、実行して全テストが失敗することを確認する。実装前に通ってしまうテストは、何も検査していない
  4. コーディングエージェントの起動 — 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 つと並行して、実現性・コストのループも回します。

開発ループのハーネスをいくら磨いても、入力の仕様が間違っていれば間違ったものが速くできるだけです。上流の 2 つのループで Validation を、並行する実現性・コストのループで実現性を、開発ループで Verification を担う — シフトレフトをエージェント開発に当てはめると、この形になります。上流で仕様を固めておけば、開発ループはエージェントに任せても収束に向かいやすくなります。



前の記事
/claude-api prompt-audit とは — 古いモデル向けに書いたプロンプトと CLAUDE.md を棚卸しするコマンド
次の記事
mizchi さんの「俺のAIプログラミング手法」を読む — ループ・評価指標・形式手法で AI に任せる範囲を広げる