Markdown を真実の源とした「要件 → 設計 → テスト仕様 → テストコード自動生成 →
三層テスト → 失敗分類 → 差分レビュー → 再生成」サイクルを、Windows 11 ローカル
環境で実現するための網羅的な手順書を以下に示すのである。
前回までの議論を踏まえ、環境構築のコマンドレベル、全設定ファイルの内容、ドキュメント
記述ルール、サイクル手順を一つに統合したリファレンスとして示すのが本稿の狙いである。
- 全体像
サイクルの基本原則は四つである。
第一に 仕様書(Markdown)は唯一の真実 であり、コードとテストはその射影にすぎない
と位置付ける。
第二に テストを先に生成して凍結 し、実装は「テストを満たすか」という単純判定で
進める。
第三に テストは単体・カバレッジ・シナリオの三層 で走らせる。
第四に NG 時は LLM に失敗原因を分類させてから差分を出させ、人間が承認するまで
実ファイルは変更しない のである。
1. 環境構築(Windows 11)
1.1. 必須ソフトウェアのインストール
# 開発ツール
winget install --id Microsoft.VisualStudioCode -e
winget install --id Git.Git -e
winget install --id OpenJS.NodeJS.LTS -e
winget install --id Python.Python.3.12 -e
# 図とドキュメント変換用(必要なら)
winget install --id EclipseAdoptium.Temurin.21.JDK -e
winget install --id Graphviz.Graphviz -e
winget install --id JohnMacFarlane.Pandoc -e
インストール後にPowerShellを開き直し、バージョンを確認する。
node --version # v20+
python --version # 3.12+
git --version
dot -V
1.2. LM Studio のセットアップ
-
https://lmstudio.ai/downloadから Windows 版をダウンロードして インストールする。 -
起動後、「Discover」タブからコーディング向けモデルを取得する。推奨は次のとおり。
-
32GB+ RAM/VRAM: Qwen3-Coder-30B または DeepSeek-Coder-V2-Lite
-
16GB前後: Qwen2.5-Coder-14B-Instruct (Q4_K_M)
-
8GB前後: Qwen2.5-Coder-7B-Instruct (Q4_K_M)
-
-
モデルをロードして「Developer」タブを開き、以下を設定する。
-
Context Length: 最低 32,000、可能なら 65,536 以上にする(Claude Code は コンテキストを大量消費するためである)。
-
Server: Start を押下し、ポート 1234 で Listen させる。
-
Anthropic-compatible endpoint が有効であることを確認する (LM Studio 0.4.1以降は標準で有効)。
-
-
lmsCLIをブートストラップしておく。
lms bootstrap
lms ls # ロード済みモデルIDを確認
ここで表示されるモデル ID の 完全一致文字列 を後の環境変数で使うので控えておく。
1.3. Claude Code のインストールとローカル LLM 接続
npm install -g @anthropic-ai/claude-code
claude --version
LM Studio へ Claude Code を向ける環境変数をユーザー恒久設定として登録する。
[Environment]::SetEnvironmentVariable("ANTHROPIC_BASE_URL", "http://localhost:1234", "User")
[Environment]::SetEnvironmentVariable("ANTHROPIC_AUTH_TOKEN", "lmstudio", "User")
[Environment]::SetEnvironmentVariable("ANTHROPIC_DEFAULT_SONNET_MODEL", "qwen/qwen3-coder-30b", "User")
[Environment]::SetEnvironmentVariable("ANTHROPIC_DEFAULT_HAIKU_MODEL", "qwen/qwen3-coder-30b", "User")
[Environment]::SetEnvironmentVariable("ANTHROPIC_DEFAULT_OPUS_MODEL", "qwen/qwen3-coder-30b", "User")
3つのティア(Sonnet/Haiku/Opus)すべてに同じローカルモデルをマップしておかないと、 Claude Code 内部が未存在のクラウドモデルを要求して 404 になる点に注意するのである。 PowerShell を開き直し、疎通確認を行う。
curl http://localhost:1234/v1/models
claude --model qwen/qwen3-coder-30b
1.4. VS Code 拡張
code --install-extension yzhang.markdown-all-in-one
code --install-extension davidanson.vscode-markdownlint
code --install-extension bierner.markdown-mermaid
code --install-extension ms-python.python
code --install-extension alexkrechik.cucumberautocomplete
1.5. プロジェクト初期化
mkdir C:\work\todo-api
cd C:\work\todo-api
git init
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
pip install fastapi "uvicorn[standard]" pytest pytest-cov pytest-bdd httpx
pip install pre-commit markdownlint-cli2
# 後述のディレクトリ構造を作成
"src\todo_api","tests\unit","tests\bdd\steps","docs\features","docs\build","pdca","scripts\prompts" | ForEach-Object { New-Item -ItemType Directory -Force -Path $_ | Out-Null }
New-Item -ItemType File src\todo_api\__init__.py | Out-Null
2. プロジェクト構造と命名規約
トレーサビリティ ID 命名規約は以下とする。 これが LLM の自己診断精度を決定づけるため、厳格に守るのである。
| 種別 | 接頭辞 | 例 | 配置 |
|---|---|---|---|
要件 |
|
|
|
設計 |
|
|
|
単体テスト |
|
|
|
シナリオ |
|
|
|
非機能 |
|
|
|
実装側にも対応マーカーを必ず残す。src/*.py では関数定義の直前行に
# impl: REQ-001, DES-001、tests/*.py ではテスト
関数の docstring 冒頭に # covers: TC-001 の形式である。
3. ドキュメント記述ルール
3.1. 共通ルール(仕様書全般)
第一に、ID は一度採番したら再採番禁止 である。削除する場合は (deprecated)
を付けるのみとし、履歴を保つ。
第二に、仕様表中の文字列リテラルや数値は、LLMが触らない真実値 である。
テストコードはこれらを一字一句そのまま埋め込む。
第三に、1セクション=1ID を厳守する。1見出しに複数のREQやDESを詰め込まない。
第四に、仕様の曖昧表現禁止 である。「適切に」「必要に応じて」のような表現は
失敗分類で A_spec の引き金になるので避ける。
3.2. 要件定義書(docs/01-requirements.md)
---
project: todo-api
version: 0.1.0
---
# 要件定義書
## REQ-001 Todo追加
- ユーザーはタイトル(1〜100文字)を指定してTodoを1件追加できる。
- 追加されたTodoは自動採番のid(int, 1始まり, 連番)とcreatedAt(ISO8601)を持つ。
- 応答は HTTP 201 で、本文に追加されたTodo全項目を含む。
## REQ-002 タイトル入力検証
- titleが空文字、または101文字以上の場合は HTTP 400 を返す。
- titleに制御文字(U+0000〜U+001F)を含む場合は HTTP 400 を返す。
- エラー応答本文は `{"error": "<message>"}` 形式である。
## REQ-003 一覧取得
- GET /todos で登録済み全Todoを id 昇順で返す。
- 0件時は空配列 `[]` を HTTP 200 で返す。
## NFR-001 単体テストカバレッジ
- src/todo_api/ の line coverage は 85% 以上を維持する。
## NFR-002 応答時間
- 100件未満のデータ量において、各エンドポイントの応答は 100ms 以内である。
3.3. 設計書(docs/02-design.md)
# 設計書
## DES-001 POST /todos (実装: REQ-001, REQ-002)
```mermaid
sequenceDiagram
participant C as Client
participant A as FastAPI App
participant V as validate_title
participant R as TodoRepository
C->>A: POST /todos {title}
A->>V: validate_title(title)
alt 不正
V-->>A: ValidationError
A-->>C: 400 {error}
else 正常
V-->>A: ok
A->>R: add(title)
R-->>A: Todo
A-->>C: 201 {todo}
end
```
### インターフェース
- 関数: `validate_title(title: str) -> None`
- 例外: `ValidationError`(messageを保持)
- 関数: `TodoRepository.add(title: str) -> Todo`
- データ: `Todo(id: int, title: str, done: bool=False, created_at: datetime)`
## DES-002 GET /todos (実装: REQ-003)
- 関数: `TodoRepository.list() -> list[Todo]`
- 並び順: id 昇順
3.4. テスト仕様書(docs/03-test-spec.md)
これがテスト自動生成の入力になるため、表形式での記述が必須 である。
# テスト仕様書
## 単体テスト
| TC-ID | 対象 | 入力 | 期待結果 | 紐付け |
|--------|------------------|-----------------------------------|----------------------------------------------------------------|-------------------|
| TC-001 | POST /todos | `{"title":"買い物"}` | 201, body.id==1, body.title=="買い物", body.done==false | REQ-001 / DES-001 |
| TC-002 | POST /todos | `{"title":""}` | 400, body.error含む | REQ-002 / DES-001 |
| TC-003 | POST /todos | titleが101文字 | 400 | REQ-002 / DES-001 |
| TC-004 | POST /todos | `{"title":"a\u0000b"}` | 400 | REQ-002 / DES-001 |
| TC-005 | GET /todos | (空状態) | 200, body==[] | REQ-003 / DES-002 |
| TC-006 | GET /todos | 2件追加後 | 200, len(body)==2, body[0].id<body[1].id | REQ-003 / DES-002 |
## カバレッジ
- 対象: src/todo_api/
- 閾値: 85% (NFR-001)
## シナリオ
- docs/features/*.feature を参照(@SC-NNN)
3.5. シナリオ(docs/features/add_todo.feature)
# language: ja
機能: Todo追加と一覧
ユーザーはTodoを追加して一覧で確認できる
@SC-001 @REQ-001 @REQ-003
シナリオ: 単一Todoの追加と取得
前提 サーバが起動している
もし ユーザーが "牛乳を買う" でTodoを追加する
ならば 応答ステータスは 201 である
かつ 一覧に "牛乳を買う" が含まれる
かつ そのTodoは未完了である
4. 設定ファイル一式
4.1. CLAUDE.md(最重要)
# プロジェクト常駐ルール
## 唯一の真実
- docs/ が仕様の真実である。src/ と tests/ は仕様の射影にすぎない。
- 仕様とコードが矛盾する場合、コードに合わせる修正を勝手に行わない。
→ 失敗分類で `A_spec` として申告する。
## ID規約
- 要件: REQ-NNN / 設計: DES-NNN / 単体: TC-NNN / シナリオ: SC-NNN / 非機能: NFR-NNN
- 一度採番したIDは再採番禁止。削除は (deprecated) を付けるのみ。
## コードのマーカー
- src/*.py: 関数定義の直前行に `# impl: REQ-xxx, DES-xxx`
- tests/*.py: テスト関数の docstring 1行目に `# covers: TC-xxx`
## 差分出力ルール
- 「差分モード」と指示された反復では、実ファイルを編集せず unified diff のみを
pdca/iteration-{N}/ に書き出す。
- diff には必ず `--- a/path` / `+++ b/path` を含め、-U5 以上の context を付ける。
## 文字列・数値の保全
- 仕様表中のリテラル(文字列・数値)は1文字も変えない。改行を勝手に挿入しない。
- 長いURLや文字列は1行で書く(ローカルLLMの既知の改行混入を避ける)。
## スコープ制限
- 実装書込み先は src/ と tests/ のみ。docs/ への書込みは差分提案ファイルのみ可。
- pdca/、scripts/、.venv/、docs/build/ を読みに行かない(コンテキスト節約)。
## 言語
- コメント・docstringは日本語可。識別子は英語。
4.2. pyproject.toml
[project]
name = "todo-api"
version = "0.1.0"
requires-python = ">=3.12"
[tool.pytest.ini_options]
minversion = "8.0"
testpaths = ["tests"]
addopts = "-ra --strict-markers"
markers = [
"scenario: BDD scenarios",
]
[tool.coverage.run]
branch = true
source = ["src/todo_api"]
[tool.coverage.report]
show_missing = true
fail_under = 85
exclude_lines = ["pragma: no cover", "raise NotImplementedError"]
4.3. .markdownlint.jsonc
{
"default": true,
"MD013": false, // 行長制限なし(表が長くなるため)
"MD033": false, // インラインHTML許可
"MD041": false // 先頭H1必須を緩める(frontmatter対応)
}
4.4. .pre-commit-config.yaml
repos:
- repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.13.0
hooks:
- id: markdownlint-cli2
files: ^docs/.*\.md$
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
4.5. tests/bdd/conftest.py
import pytest
from fastapi.testclient import TestClient
from todo_api.main import app
@pytest.fixture
def client():
return TestClient(app)
5. プロンプトテンプレート
5.1. scripts/prompts/10-gen-tests.md(テストコード生成)
あなたはテストエンジニアである。以下を厳守して作業せよ。
# 入力
- docs/03-test-spec.md(単体テストの表)
- docs/features/*.feature(Gherkinシナリオ)
- docs/01-requirements.md, docs/02-design.md(背景理解のみ。値の改変根拠にしない)
# 出力先(直接書き込み可)
- tests/unit/test_*.py
- tests/bdd/steps/*.py
# 厳守ルール
1. docs/03-test-spec.md の表中の各 TC-ID につき関数を1つ tests/unit/ に作成する。
2. 関数名は `test_TC001_<目的のsnake_case>` 形式とする。
3. docstring の1行目に `# covers: TC-NNN` を入れる。
4. 入力値・期待値は仕様表のリテラルをそのまま埋め込む。推測で値を変えない。
5. .feature の各 @SC-NNN について pytest-bdd の steps を tests/bdd/steps/ に書く。
各 step 関数の docstring 1行目に `# covers: SC-NNN` を入れる。
6. 実装が未完成でも、collection が通るテストを書く。
実装未着手のため落ちる前提のテストは `@pytest.mark.xfail(reason="impl pending")` を付ける。
7. 仕様にない振る舞いを期待値にしない。
完了したら変更ファイル一覧と各ファイルの目的を1行ずつ報告せよ。
5.2. scripts/prompts/20-gen-impl.md(実装コード生成)
あなたはシニアPython/FastAPIエンジニアである。
# 目的
tests/unit と tests/bdd のテストを通すために src/todo_api/ を実装する。
# 入力
- docs/02-design.md(DES-NNNのインターフェース)
- tests/unit/, tests/bdd/(受け入れ条件)
- 既存の src/todo_api/
# 厳守ルール
1. 仕様(docs/)とテストが整合している前提で実装する。
2. 関数定義の直前行に `# impl: REQ-NNN, DES-NNN` を入れる。
3. テストを満たすために必要最小限の変更にとどめる。
4. テストを変更してはならない(test/* は読み取りのみ)。
5. 仕様とテストの矛盾を発見した場合は実装せず、その旨を最後に列挙して終了する。
完了したら、変更ファイルとどの DES-NNN を実装したかを箇条書きで報告せよ。
5.3. scripts/prompts/30-classify-failures.md(失敗分類)
あなたは品質保証エンジニアである。テスト失敗の原因を分類する。
# 入力
- docs/01-requirements.md, 02-design.md, 03-test-spec.md
- pdca/iteration-{N}/unit.log
- pdca/iteration-{N}/coverage.log
- pdca/iteration-{N}/bdd.log
- src/todo_api/ と tests/ の現状
# 出力(直接書き込み可)
pdca/iteration-{N}/classification.json に JSON 配列を書け。
[
{
"test_id": "TC-002",
"failure_type": "A_spec" | "B_impl" | "C_test" | "D_coverage",
"evidence": "なぜそう分類したか(1〜3行、具体的に)",
"linked_ids": ["REQ-002","DES-001"]
}
]
# 分類定義
- A_spec : 仕様書同士の矛盾、または曖昧な仕様のためテストが正しく書けていない/実装が一意に決まらない。
- B_impl : 仕様もテストも正しいが、src/ の実装がそれを満たしていない。
- C_test : 仕様も実装も正しいが、テストコード自体に誤りがある(値ズレ等)。
- D_coverage : カバレッジ閾値未達。原因のソース行を evidence に明記する。
# 注意
- 仕様自体を変更すべきと感じた失敗は全て A_spec である。安易に B_impl に倒さない。
- 1つの失敗を複数の type に分類してはならない。最も根本的な原因1つを選ぶ。
5.4. scripts/prompts/40-propose-diffs.md(差分提案)
あなたはアーキテクトである。classification.json に従って修正差分を提案する。
# 入力
pdca/iteration-{N}/classification.json と関連ファイル。
# 出力(直接書き込み可、ただし出力先はpdca配下のみ)
- A_spec を含む場合: pdca/iteration-{N}/proposed-spec.diff
- B_impl を含む場合: pdca/iteration-{N}/proposed-impl.patch
- C_test を含む場合: pdca/iteration-{N}/proposed-test.patch
- D_coverage を含む場合:
・原因が仕様の網羅不足なら proposed-spec.diff に統合
・原因がテスト不足なら proposed-test.patch に統合
・原因がデッドコードなら proposed-impl.patch に統合
# 形式
unified diff 形式とし、各ファイルに以下を必ず含める。
--- a/<path>
+++ b/<path>
@@ ... @@
context は -U5 以上を確保する。
# 厳守ルール
- 実ファイルは絶対に編集しない。差分テキストのみ pdca/ に書く。
- IDの再採番禁止。仕様削除は (deprecated) を付加するのみ。
- proposed-test.patch でテストを「緩める」(期待値を実測値に書き換える)変更は
仕様表との整合を必ず evidence で説明すること。
- 1ファイルにつき diff は最小hunk構成にする。
最後に、生成した差分ファイル名一覧と、各 hunk の意図を1行サマリで列挙せよ。
6. 実行スクリプト
6.1. scripts/Invoke-Check.ps1(三層テスト実行)
[CmdletBinding()] param([int]$Iteration)
$ErrorActionPreference = "Continue"
$root = (Resolve-Path "$PSScriptRoot\..").Path
Set-Location $root
$iter = "pdca/iteration-{0:D3}" -f $Iteration
New-Item -ItemType Directory -Force -Path $iter | Out-Null
# Check-1 単体テスト
pytest tests/unit -q --junitxml="$iter/unit-report.xml" 2>&1 |
Tee-Object "$iter/unit.log"
$unit = $LASTEXITCODE
# Check-2 カバレッジ
pytest tests/unit `
--cov=src/todo_api `
--cov-report=xml:"$iter/coverage.xml" `
--cov-report=term-missing `
--cov-fail-under=85 2>&1 |
Tee-Object "$iter/coverage.log"
$cov = $LASTEXITCODE
# Check-3 シナリオ
pytest tests/bdd -q --junitxml="$iter/bdd-report.xml" 2>&1 |
Tee-Object "$iter/bdd.log"
$bdd = $LASTEXITCODE
@{ unit=$unit; coverage=$cov; bdd=$bdd } |
ConvertTo-Json | Set-Content "$iter/check-result.json"
if ($unit -eq 0 -and $cov -eq 0 -and $bdd -eq 0) { exit 0 } else { exit 1 }
6.2. scripts/Run-Cycle.ps1(PDCA統合ランナー)
[CmdletBinding()] param(
[int]$Iteration = 1,
[string]$Model = "qwen/qwen3-coder-30b",
[switch]$RegenTests # 仕様のテスト表が変わったときだけ指定
)
$ErrorActionPreference = "Stop"
$root = (Resolve-Path "$PSScriptRoot\..").Path
Set-Location $root
$iterDir = "pdca/iteration-{0:D3}" -f $Iteration
New-Item -ItemType Directory -Force -Path $iterDir | Out-Null
function Invoke-Claude {
param([string]$PromptPath,[string]$LogPath,[switch]$AllowWrite)
$tools = if ($AllowWrite) { "Read,Write,Edit,Glob,Grep" } else { "Read,Glob,Grep" }
$prompt = (Get-Content -Raw $PromptPath).Replace("{N}", ("{0:D3}" -f $Iteration))
$prompt | claude -p --model $Model --allowedTools $tools --output-format text |
Tee-Object -FilePath $LogPath | Out-Null
}
# === Plan : 仕様 lint ===
Write-Host "==> Plan" -ForegroundColor Cyan
markdownlint-cli2 "docs/**/*.md" 2>&1 | Tee-Object "$iterDir/lint.log"
# === Do-1 : テスト生成(必要時のみ) ===
if ($RegenTests) {
Write-Host "==> Do-1 (regen tests)" -ForegroundColor Cyan
Invoke-Claude "scripts/prompts/10-gen-tests.md" "$iterDir/gen-tests.log" -AllowWrite
Write-Host "tests/ をレビューしてください。" -ForegroundColor Yellow
code "tests"
if ((Read-Host "テストを commit して進めますか? (y/N)") -ne "y") { exit 1 }
git add tests
git commit -m "regen tests for iter $Iteration"
}
# === Do-2 : 実装生成 ===
Write-Host "==> Do-2 (impl)" -ForegroundColor Cyan
Invoke-Claude "scripts/prompts/20-gen-impl.md" "$iterDir/gen-impl.log" -AllowWrite
# === Check : 三層テスト ===
Write-Host "==> Check" -ForegroundColor Cyan
& "$PSScriptRoot/Invoke-Check.ps1" -Iteration $Iteration
$checkExit = $LASTEXITCODE
if ($checkExit -eq 0) {
Write-Host "[GREEN] iter $Iteration 完全合格" -ForegroundColor Green
git add -A; git commit -m "iter $Iteration green"
git tag "iter-$Iteration-green"
exit 0
}
# === Act準備 : 分類 → 差分提案 ===
Write-Host "==> Classify failures" -ForegroundColor Cyan
Invoke-Claude "scripts/prompts/30-classify-failures.md" "$iterDir/classify.log" -AllowWrite
Write-Host "==> Propose diffs" -ForegroundColor Cyan
Invoke-Claude "scripts/prompts/40-propose-diffs.md" "$iterDir/propose.log" -AllowWrite
# === Human Review ===
Write-Host "==> Human Review" -ForegroundColor Magenta
$cls = Get-Content "$iterDir/classification.json" -Raw | ConvertFrom-Json
$types = $cls | ForEach-Object { $_.failure_type } | Sort-Object -Unique
Write-Host "検出された failure_type: $($types -join ', ')"
if ($types -contains "A_spec") {
code "$iterDir/proposed-spec.diff"
if ((Read-Host "仕様修正(proposed-spec.diff)を適用しますか? (y/N)") -eq "y") {
git apply --whitespace=fix "$iterDir/proposed-spec.diff"
git add docs; git commit -m "iter $Iteration: spec update"
Write-Host "仕様変更を適用。次は -RegenTests 付きで起動してください。" -ForegroundColor Yellow
exit 2
} else {
Write-Host "仕様レビュー却下。手動修正してください。"; exit 1
}
}
if ($types -contains "B_impl") {
code "$iterDir/proposed-impl.patch"
if ((Read-Host "実装修正を適用しますか? (y/N)") -eq "y") {
git apply --whitespace=fix "$iterDir/proposed-impl.patch"
git add src; git commit -m "iter $Iteration: impl fix"
}
}
if ($types -contains "C_test") {
Write-Host "[警告] テスト修正案あり。仕様表との整合を厳格に確認せよ。" -ForegroundColor Yellow
code "$iterDir/proposed-test.patch"
if ((Read-Host "テスト修正を適用しますか? (y/N)") -eq "y") {
git apply --whitespace=fix "$iterDir/proposed-test.patch"
git add tests; git commit -m "iter $Iteration: test fix"
}
}
Write-Host "次反復: .\scripts\Run-Cycle.ps1 -Iteration $($Iteration+1)" -ForegroundColor Cyan
7. PDCAサイクルの手順
7.2. 手順詳細
初回サイクル(Iteration 1)の手順 は次のとおりである。
-
docs/01-requirements.md、docs/02-design.md、docs/03-test-spec.md、docs/features/*.featureを手動で作成し commit する。 -
PowerShell で
.\scripts\Run-Cycle.ps1 -Iteration 1 -RegenTestsを実行する。 -
テストが自動生成されたら VS Code でレビューし、
yを入力して進める。 -
実装生成後に三層テストが走る。GREEN なら
iter-1-greenタグが付いて完了である。 -
NG の場合は
classification.jsonが出力され、failure_type に応じて 差分ファイルが提示される。 -
A_specがあればまず仕様修正を承認し、PowerShell は exit code 2 で終了する。 次反復を-RegenTests付きで起動する。 -
B_implのみなら自動 apply され、Check が再実行される(再実行は-Iteration 2で起動)。 -
C_testがある場合は VS Code で慎重に diff を確認する。期待値の改変は バグの温床のためである。
機能追加時の手順 は以下のとおりとなる。新規機能を追加するときは仕様書に REQ/DES/TC
を追記する。既存のIDは触らない。-RegenTests 付きでサイクルを起動すると、
追加分の TC・SC に対応するテスト関数が自動的に追加される。実装もそれに合わせて
拡張されるのである。
7.3. 失敗パターン別の対処
| failure_type | 典型例 | 人間ゲート | 対処 |
|---|---|---|---|
|
「validate_title の空文字応答仕様が未定義」 |
必須・厳密 |
spec.diff を承認 → |
|
「validate_title の長さチェックが 101文字を許可している」 |
軽量 |
impl.patch を承認 → 次反復 |
|
「TC-001の期待値 createdAt 形式が仕様と齟齬」 |
必須・厳密 |
仕様表と diff を照合 → 承認 or 却下 |
|
「validators.py の例外パスが未到達」 |
軽量 |
test.patch または impl.patch を承認 |
8. ベストプラクティスと運用上の注意
第一に、仕様書のテスト表は表形式で書く のが決定的に重要である。表のセルがそのまま
テスト関数の入力・期待値になるため、平文で書くと LLM がリテラルを推測で書き換えてしまう。
第二に、テストは初回 commit 後に凍結し、仕様のテスト表が変わった反復でのみ
-RegenTests を付ける。これにより「赤いから期待値を緩める」という退行を
防げるのである。
第三に、ローカル LLM には file 直編集ではなく unified diff 出力を要求 する運用が
圧倒的に安定する。書込み権限は実装生成のみに限り、修正案はすべて pdca/iteration-N/
に書き出させる構成にしているのはこの理由である。
第四に、コンテキストは32K以上 を必ず確保し、CLAUDE.md で読み込み除外パス
(docs/build/、pdca/、.venv/ など)を明示する。
第五に、B_impl だけは軽量レビュー にして自動化を効かせ、A_spec
と C_test は必ず厳密な人間ゲート を設けるのが品質と速度のバランスを
取る要点である。
第六に、反復ごとに pdca/iteration-NNN/ フォルダを保存することで、
後から「なぜこの仕様にしたか」「どのテストが何回で通ったか」を完全に再現できる
トレーサビリティが手に入る。
第七に、ローカルLLMの長文字列改行混入バグ を避けるため、URLや長い文字列は必ず
1行で書く規約を CLAUDE.md に明記する。
最後に、サイクルを2〜3周回したら必ず traceability.csv を生成して縦串確認
することを推奨する。grep でコード内の # impl: REQ-001 を集め、仕様書の
REQ-001 と突き合わせるだけのスクリプトで足りる。
これにより「仕様にあるがコードにない要件」「コードにあるが仕様にない実装」を機械的に
検出でき、PDCAサイクルが意図せず歪んでいないかを定期点検できるのである。
以上の構成で、Windows 11 + LM Studio + Claude Code というローカル完結環境において、
Markdown 仕様書を真実の源とした自動テスト生成 → 三層テスト → 失敗分類
→ 差分レビュー → 再生成のPDCAサイクル が、品質ゲートを保ったまま実用可能な速度で
回るのである。
初期構築は半日、最初の1機能を通すまでに数反復、慣れれば1機能あたり1〜2反復で GREEN
に到達するのが現実的な到達点となる。