spec-runner 1.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +193 -0
  3. package/bin/spec-runner.js +715 -0
  4. package/install.sh +80 -0
  5. package/package.json +45 -0
  6. package/templates/base/.github/PULL_REQUEST_TEMPLATE.md +43 -0
  7. package/templates/base/.github/workflows/phase-gate-check.yml +216 -0
  8. package/templates/base/docs/adr/TEMPLATE.md +46 -0
  9. package/templates/base/docs/glossary.md +51 -0
  10. package/templates/base/docs/review/debt.md +8 -0
  11. package/templates/base/scripts/spec-runner.sh +1079 -0
  12. package/templates/base/templates/requirement/template.md +40 -0
  13. package/templates/claude/.claude/commands/sr-complete.md +9 -0
  14. package/templates/claude/.claude/commands/sr-configure.md +11 -0
  15. package/templates/claude/.claude/commands/sr-design-detail.md +9 -0
  16. package/templates/claude/.claude/commands/sr-design-high.md +9 -0
  17. package/templates/claude/.claude/commands/sr-fix.md +9 -0
  18. package/templates/claude/.claude/commands/sr-hotfix.md +9 -0
  19. package/templates/claude/.claude/commands/sr-implement.md +9 -0
  20. package/templates/claude/.claude/commands/sr-init.md +10 -0
  21. package/templates/claude/.claude/commands/sr-review.md +9 -0
  22. package/templates/claude/.claude/commands/sr-set-gate.md +9 -0
  23. package/templates/claude/.claude/commands/sr-status.md +9 -0
  24. package/templates/claude/.claude/commands/sr-test-design.md +9 -0
  25. package/templates/claude/.claude/hooks/pre-tool-use.sh +79 -0
  26. package/templates/claude/.claude/settings.json +29 -0
  27. package/templates/claude/CLAUDE.md +141 -0
  28. package/templates/copilot/.github/copilot-instructions.md +25 -0
  29. package/templates/copilot/.github/prompts/sr-complete.prompt.md +13 -0
  30. package/templates/copilot/.github/prompts/sr-configure.prompt.md +13 -0
  31. package/templates/copilot/.github/prompts/sr-design-detail.prompt.md +14 -0
  32. package/templates/copilot/.github/prompts/sr-design-high.prompt.md +13 -0
  33. package/templates/copilot/.github/prompts/sr-fix.prompt.md +14 -0
  34. package/templates/copilot/.github/prompts/sr-hotfix.prompt.md +14 -0
  35. package/templates/copilot/.github/prompts/sr-implement.prompt.md +13 -0
  36. package/templates/copilot/.github/prompts/sr-init.prompt.md +15 -0
  37. package/templates/copilot/.github/prompts/sr-review.prompt.md +14 -0
  38. package/templates/copilot/.github/prompts/sr-set-gate.prompt.md +14 -0
  39. package/templates/copilot/.github/prompts/sr-status.prompt.md +13 -0
  40. package/templates/copilot/.github/prompts/sr-test-design.prompt.md +13 -0
  41. package/templates/cursor/.cursor/commands/sr-complete.md +9 -0
  42. package/templates/cursor/.cursor/commands/sr-configure.md +11 -0
  43. package/templates/cursor/.cursor/commands/sr-design-detail.md +9 -0
  44. package/templates/cursor/.cursor/commands/sr-design-high.md +9 -0
  45. package/templates/cursor/.cursor/commands/sr-fix.md +9 -0
  46. package/templates/cursor/.cursor/commands/sr-hotfix.md +9 -0
  47. package/templates/cursor/.cursor/commands/sr-implement.md +9 -0
  48. package/templates/cursor/.cursor/commands/sr-init.md +10 -0
  49. package/templates/cursor/.cursor/commands/sr-review.md +9 -0
  50. package/templates/cursor/.cursor/commands/sr-set-gate.md +9 -0
  51. package/templates/cursor/.cursor/commands/sr-status.md +9 -0
  52. package/templates/cursor/.cursor/commands/sr-test-design.md +9 -0
  53. package/templates/cursor/.cursorrules +25 -0
@@ -0,0 +1,1079 @@
1
+ #!/usr/bin/env bash
2
+ # =============================================================================
3
+ # spec-runner.sh — ドメイン駆動AI開発 フェーズゲートシステム
4
+ # =============================================================================
5
+ # このスクリプトが「本当の強制」を担う。
6
+ # Claude CodeはCLAUDE.mdを読んでルールを「知る」が、スキップできる。
7
+ # このスクリプトはシェルレベルで実行を拒否するため、AIも人間もスキップできない。
8
+ #
9
+ # 使い方:
10
+ # ./scripts/spec-runner.sh init … 詳細設定の対話のみ
11
+ # ./scripts/spec-runner.sh init <ユースケース名> [集約名] … 設定後ユースケース作成
12
+ # ./scripts/spec-runner.sh require
13
+ # ./scripts/spec-runner.sh design-high
14
+ # ./scripts/spec-runner.sh design-detail <サブフェーズ: domain|usecase|table|infra>
15
+ # ./scripts/spec-runner.sh test-design
16
+ # ./scripts/spec-runner.sh implement
17
+ # ./scripts/spec-runner.sh status
18
+ # ./scripts/spec-runner.sh fix <修正内容>
19
+ # ./scripts/spec-runner.sh hotfix <内容>
20
+ # ./scripts/spec-runner.sh review-pass <ドキュメントパス>
21
+ # ./scripts/spec-runner.sh complete
22
+ # =============================================================================
23
+
24
+ set -euo pipefail
25
+
26
+ # ── 定数 ──────────────────────────────────────────────────────────────────────
27
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
28
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
29
+ STATE_FILE="$PROJECT_ROOT/.spec-runner/state.json"
30
+ DEBT_FILE="$PROJECT_ROOT/docs/review/debt.md"
31
+ GLOSSARY="$PROJECT_ROOT/docs/glossary.md"
32
+
33
+ # ── フレームワーク設定を読み込む ───────────────────────────────────────────────
34
+ # npx spec-runner によって生成された .spec-runner/config.sh を読み込む
35
+ # これによりフレームワーク依存の設定(拡張子・パス等)が外部化される
36
+ CONFIG_FILE="$PROJECT_ROOT/.spec-runner/config.sh"
37
+ if [[ -f "$CONFIG_FILE" ]]; then
38
+ # shellcheck source=.spec-runner/config.sh
39
+ source "$CONFIG_FILE"
40
+ else
41
+ # .spec-runner/config.sh がない場合のデフォルト値
42
+ SOURCE_EXTENSIONS="${SOURCE_EXTENSIONS:-ts tsx js jsx php py rb}"
43
+ DOMAIN_PATH="${DOMAIN_PATH:-src/domain}"
44
+ USECASE_PATH="${USECASE_PATH:-src/useCase}"
45
+ INFRA_PATH="${INFRA_PATH:-src/infrastructure}"
46
+ fi
47
+
48
+ # ── カラー出力 ──────────────────────────────────────────────────────────────
49
+ RED='\033[0;31m'
50
+ GREEN='\033[0;32m'
51
+ YELLOW='\033[1;33m'
52
+ BLUE='\033[0;34m'
53
+ CYAN='\033[0;36m'
54
+ BOLD='\033[1m'
55
+ NC='\033[0m'
56
+
57
+ ok() { echo -e "${GREEN}✓${NC} $*"; }
58
+ fail() { echo -e "${RED}✗${NC} $*"; }
59
+ warn() { echo -e "${YELLOW}⚠${NC} $*"; }
60
+ info() { echo -e "${CYAN}ℹ${NC} $*"; }
61
+ step() { echo -e "${BOLD}${BLUE}▶${NC} $*"; }
62
+ die() { echo -e "${RED}${BOLD}ERROR:${NC} $*" >&2; exit 1; }
63
+
64
+ # ── 依存チェック ───────────────────────────────────────────────────────────
65
+ require_cmd() {
66
+ command -v "$1" &>/dev/null || die "$1 がインストールされていません"
67
+ }
68
+ require_cmd jq
69
+ require_cmd git
70
+
71
+ # ── ステート操作 ───────────────────────────────────────────────────────────
72
+ state_get() { jq -r ".$1 // empty" "$STATE_FILE" 2>/dev/null || echo ""; }
73
+ state_set() {
74
+ local key="$1" val="$2"
75
+ local tmp
76
+ tmp="${STATE_FILE}.tmp.$$"
77
+ jq ".$key = $val" "$STATE_FILE" > "$tmp" && mv "$tmp" "$STATE_FILE"
78
+ }
79
+ state_set_str() { state_set "$1" "\"$2\""; }
80
+ state_set_bool() { state_set "$1" "$2"; } # true / false
81
+
82
+ state_init() {
83
+ local usecase="$1" aggregate="${2:-}"
84
+ local branch="feature/uc-$(echo "$usecase" | tr ' ' '-')"
85
+ local agg_branch=""
86
+ [[ -n "$aggregate" ]] && agg_branch="aggregate/$(echo "$aggregate" | tr ' ' '-')"
87
+
88
+ mkdir -p "$(dirname "$STATE_FILE")"
89
+ cat > "$STATE_FILE" <<EOF
90
+ {
91
+ "usecase": "$usecase",
92
+ "aggregate": "$aggregate",
93
+ "phase": "require",
94
+ "branch": "$branch",
95
+ "aggregate_branch": "$agg_branch",
96
+ "started_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
97
+ "gates": {
98
+ "require_approved": false,
99
+ "glossary_checked": false,
100
+ "high_level_reviewed": false,
101
+ "domain_model_reviewed": false,
102
+ "usecase_design_reviewed": false,
103
+ "table_design_reviewed": false,
104
+ "infra_design_reviewed": false,
105
+ "test_design_reviewed": false,
106
+ "test_code_committed": false
107
+ },
108
+ "history": []
109
+ }
110
+ EOF
111
+ }
112
+
113
+ state_push_history() {
114
+ local msg="$1"
115
+ local tmp
116
+ tmp="${STATE_FILE}.tmp.$$"
117
+ jq ".history += [\"$(date -u +%Y-%m-%dT%H:%M:%SZ): $msg\"]" "$STATE_FILE" > "$tmp" && mv "$tmp" "$STATE_FILE"
118
+ }
119
+
120
+ # ── ステートファイル必須チェック ───────────────────────────────────────────
121
+ require_state() {
122
+ [[ -f "$STATE_FILE" ]] || die "作業中のユースケースがありません。まず: ./scripts/spec-runner.sh init <ユースケース名>"
123
+ local phase
124
+ phase=$(state_get "phase")
125
+ [[ -n "$phase" ]] || die "ステートファイルが壊れています: $STATE_FILE"
126
+ }
127
+
128
+ # ── ゲートチェック関数群 ───────────────────────────────────────────────────
129
+
130
+ # ドキュメントのfrontmatterからstatusを取得
131
+ doc_status() {
132
+ local file="$1"
133
+ [[ -f "$file" ]] || { echo "missing"; return; }
134
+ grep -m1 '^status:' "$file" | sed 's/status: *//' | tr -d '[:space:]' || echo "draft"
135
+ }
136
+
137
+ # ファイル存在チェック(エラーメッセージ付き)
138
+ check_file() {
139
+ local file="$1" label="$2"
140
+ if [[ -f "$file" ]]; then
141
+ ok "$label: $file"
142
+ return 0
143
+ else
144
+ fail "$label が存在しません: $file"
145
+ return 1
146
+ fi
147
+ }
148
+
149
+ # ドキュメントステータスチェック
150
+ check_status() {
151
+ local file="$1" required_status="$2" label="$3"
152
+ local status
153
+ status=$(doc_status "$file")
154
+ if [[ "$status" == "$required_status" ]] || [[ "$status" == "approved" && "$required_status" == "reviewed" ]]; then
155
+ ok "$label (status: $status)"
156
+ return 0
157
+ else
158
+ fail "$label のステータスが '$status' です。'$required_status' が必要です"
159
+ info " レビュー後: ./scripts/spec-runner.sh review-pass $file"
160
+ return 1
161
+ fi
162
+ }
163
+
164
+ # gateフラグチェック
165
+ check_gate() {
166
+ local gate="$1" label="$2"
167
+ local val
168
+ val=$(state_get "gates.$gate")
169
+ if [[ "$val" == "true" ]]; then
170
+ ok "$label"
171
+ return 0
172
+ else
173
+ fail "$label(未完了)"
174
+ return 1
175
+ fi
176
+ }
177
+
178
+ # ── debt.md チェック ───────────────────────────────────────────────────────
179
+ check_debt() {
180
+ if [[ -f "$DEBT_FILE" ]]; then
181
+ local unchecked
182
+ unchecked=$(grep -c '^\- \[ \]' "$DEBT_FILE" 2>/dev/null; true)
183
+ unchecked="${unchecked:-0}"
184
+ if [[ "${unchecked}" -gt 0 ]]; then
185
+ echo ""
186
+ warn "══════════════════════════════════════════════════════════"
187
+ warn " ドキュメント負債が $unchecked 件あります: $DEBT_FILE"
188
+ warn " 新規開発の前に消化することを推奨します"
189
+ warn "══════════════════════════════════════════════════════════"
190
+ echo ""
191
+ read -r -p "このまま続けますか? [y/N] " answer
192
+ [[ "$answer" =~ ^[Yy]$ ]] || die "ドキュメント負債を先に解消してください"
193
+ fi
194
+ fi
195
+ }
196
+
197
+ # ── usecase/aggregate のパス計算 ───────────────────────────────────────────
198
+ uc_slug() { echo "$(state_get usecase)" | tr ' ' '-'; }
199
+ agg_slug() { echo "$(state_get aggregate)" | tr ' ' '-'; }
200
+ uc_req() { echo "$PROJECT_ROOT/docs/requirements/$(uc_slug).md"; }
201
+ uc_high() { echo "$PROJECT_ROOT/docs/high-level/$(uc_slug).md"; }
202
+ uc_detail() { echo "$PROJECT_ROOT/docs/detailed/$(uc_slug)"; }
203
+ uc_test() { echo "$PROJECT_ROOT/docs/test-design/$(uc_slug).md"; }
204
+
205
+ # ═══════════════════════════════════════════════════════════════════════════════
206
+ # コマンド実装
207
+ # ═══════════════════════════════════════════════════════════════════════════════
208
+
209
+ # ── init ──────────────────────────────────────────────────────────────────────
210
+ cmd_init() {
211
+ local usecase="${1:-}" aggregate="${2:-}"
212
+
213
+ [[ -f "$CONFIG_FILE" ]] || die ".spec-runner/config.sh がありません。まず npx spec-runner を実行してください。"
214
+
215
+ # 詳細設定がまだの場合は対話で設定(npx では開発環境だけ聞き、ここでパス・TDD 等を聞く)
216
+ if [[ "${CONFIGURED:-false}" != "true" ]]; then
217
+ echo ""
218
+ info "詳細設定がまだです。パス・TDD 等を対話で設定します..."
219
+ echo ""
220
+ npx spec-runner --configure
221
+ # 設定を再読み込み
222
+ source "$CONFIG_FILE"
223
+ echo ""
224
+ fi
225
+
226
+ # 引数なしの init = 設定対話のみ。ユースケース名を渡すと作成に進む
227
+ if [[ -z "$usecase" ]]; then
228
+ info "設定が完了しました。最初のユースケースを開始するには:"
229
+ echo ""
230
+ echo " ./scripts/spec-runner.sh init \"ユースケース名\" \"集約名\""
231
+ echo ""
232
+ return 0
233
+ fi
234
+
235
+ check_debt
236
+
237
+ echo ""
238
+ step "ユースケース初期化: $usecase"
239
+ echo ""
240
+
241
+ # 既存のstateがあれば警告
242
+ if [[ -f "$STATE_FILE" ]]; then
243
+ local current
244
+ current=$(state_get "usecase")
245
+ warn "作業中のユースケース '$current' があります"
246
+ read -r -p "上書きしますか? [y/N] " answer
247
+ [[ "$answer" =~ ^[Yy]$ ]] || die "中止しました"
248
+ fi
249
+
250
+ # ブランチ作成
251
+ local slug branch agg_branch
252
+ slug=$(echo "$usecase" | tr ' ' '-')
253
+ branch="feature/uc-$slug"
254
+
255
+ # デフォルトブランチを取得(main / master など)
256
+ local default_branch
257
+ default_branch=$(git symbolic-ref --short HEAD 2>/dev/null || echo "main")
258
+
259
+ if [[ -n "$aggregate" ]]; then
260
+ agg_branch="aggregate/$(echo "$aggregate" | tr ' ' '-')"
261
+ step "集約ブランチを確認/作成: $agg_branch"
262
+ if ! git show-ref --verify --quiet "refs/heads/$agg_branch"; then
263
+ git checkout -b "$agg_branch" 2>/dev/null || git checkout -b "$agg_branch" "$default_branch"
264
+ ok "集約ブランチ作成: $agg_branch"
265
+ else
266
+ git checkout "$agg_branch"
267
+ ok "集約ブランチに切替: $agg_branch"
268
+ fi
269
+ fi
270
+
271
+ step "ユースケースブランチを作成: $branch"
272
+ if git show-ref --verify --quiet "refs/heads/$branch"; then
273
+ die "ブランチ $branch は既に存在します"
274
+ fi
275
+ local base="${aggregate:+aggregate/$(echo "$aggregate" | tr ' ' '-')}"
276
+ git checkout -b "$branch" "${base:-$default_branch}" 2>/dev/null || git checkout -b "$branch"
277
+ ok "ブランチ作成: $branch"
278
+
279
+ # ステート初期化
280
+ state_init "$usecase" "$aggregate"
281
+
282
+ # 要件定義ファイルをテンプレートから生成(templates/requirement/template.md を必須とする)
283
+ local req_file tmpl
284
+ req_file=$(uc_req)
285
+ tmpl="$PROJECT_ROOT/templates/requirement/template.md"
286
+ [[ -f "$tmpl" ]] || die "要件テンプレートがありません: $tmpl (npx spec-runner でセットアップしてください)"
287
+
288
+ mkdir -p "$(dirname "$req_file")"
289
+ if [[ ! -f "$req_file" ]]; then
290
+ sed "s/{{USECASE}}/$usecase/g; s/{{DATE}}/$(date +%Y-%m-%d)/g" "$tmpl" > "$req_file"
291
+ ok "要件定義ファイルを作成: $req_file"
292
+ else
293
+ warn "要件定義ファイルは既に存在します: $req_file"
294
+ fi
295
+
296
+ echo ""
297
+ info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
298
+ info "次のステップ:"
299
+ info " 1. $req_file を編集する"
300
+ info " 2. チームに確認してもらう"
301
+ info " 3. ./scripts/spec-runner.sh review-pass $req_file"
302
+ info " 4. ./scripts/spec-runner.sh design-high"
303
+ info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
304
+ }
305
+
306
+ # ── require ───────────────────────────────────────────────────────────────────
307
+ cmd_require() {
308
+ require_state
309
+ local phase
310
+ phase=$(state_get "phase")
311
+ [[ "$phase" == "require" ]] || die "現在のフェーズは '$phase' です。要件定義フェーズではありません"
312
+
313
+ local req_file
314
+ req_file=$(uc_req)
315
+ info "要件定義ファイル: $req_file"
316
+ info "編集後、チームに確認してもらい:"
317
+ info " ./scripts/spec-runner.sh review-pass $req_file"
318
+ }
319
+
320
+ # ── design-high ───────────────────────────────────────────────────────────────
321
+ cmd_design_high() {
322
+ require_state
323
+
324
+ echo ""
325
+ step "【ゲートチェック】概要設計フェーズに進む条件を確認します"
326
+ echo ""
327
+
328
+ local errors=0
329
+ local req_file
330
+ req_file=$(uc_req)
331
+
332
+ check_file "$req_file" "要件定義ファイル" || ((errors++))
333
+ check_status "$req_file" "approved" "要件定義のステータス" || ((errors++))
334
+ check_gate "glossary_checked" "glossary.md の確認済み" || ((errors++))
335
+
336
+ if [[ $errors -gt 0 ]]; then
337
+ echo ""
338
+ die "ゲートを通過できません。${errors}件の条件が未達です ↑"
339
+ fi
340
+
341
+ echo ""
342
+ ok "ゲート通過!概要設計フェーズを開始します"
343
+ state_set_str "phase" "design-high"
344
+ state_push_history "design-high フェーズ開始"
345
+
346
+ # 概要設計ファイルをテンプレートから生成
347
+ local high_file usecase
348
+ high_file=$(uc_high)
349
+ usecase=$(state_get "usecase")
350
+ mkdir -p "$(dirname "$high_file")"
351
+
352
+ if [[ ! -f "$high_file" ]]; then
353
+ cat > "$high_file" <<TMPL
354
+ ---
355
+ title: $usecase 概要設計
356
+ status: draft
357
+ requirement: $(uc_req)
358
+ created: $(date +%Y-%m-%d)
359
+ updated: $(date +%Y-%m-%d)
360
+ ---
361
+
362
+ # $usecase 概要設計
363
+
364
+ ## ユースケース
365
+
366
+ ### UC-01: $usecase
367
+
368
+ **アクター**:
369
+ **前提条件**:
370
+ **主要フロー**:
371
+ 1.
372
+ **事後条件**:
373
+ **例外フロー**:
374
+
375
+ ## ドメインモデルの洗い出し
376
+
377
+ <!-- glossary.md の日本語名で書く -->
378
+
379
+ ### エンティティ候補
380
+
381
+ | 名前(日本語) | 説明 | 属する集約 |
382
+ |-------------|------|----------|
383
+ | | | |
384
+
385
+ ### 値オブジェクト候補
386
+
387
+ | 名前(日本語) | 説明 |
388
+ |-------------|------|
389
+ | | |
390
+
391
+ ### ドメインイベント候補
392
+
393
+ | 名前(日本語) | いつ発生するか |
394
+ |-------------|-------------|
395
+ | | |
396
+
397
+ ## 未決事項
398
+
399
+ - [ ]
400
+
401
+ ## レビュー記録
402
+
403
+ | 日付 | レビュアー | 結果 |
404
+ |------|----------|------|
405
+ TMPL
406
+ ok "概要設計ファイルを作成: $high_file"
407
+ fi
408
+
409
+ echo ""
410
+ info "次のステップ:"
411
+ info " 1. $high_file を編集する"
412
+ info " 2. チームにレビューしてもらう"
413
+ info " 3. ./scripts/spec-runner.sh review-pass $high_file"
414
+ info " 4. ./scripts/spec-runner.sh design-detail domain"
415
+ }
416
+
417
+ # ── design-detail ─────────────────────────────────────────────────────────────
418
+ cmd_design_detail() {
419
+ require_state
420
+ local sub="${1:-}"
421
+ local valid_subs="domain usecase table infra"
422
+
423
+ if [[ -z "$sub" ]]; then
424
+ # サブフェーズなしで呼んだ場合、現在の進捗を表示
425
+ echo ""
426
+ step "詳細設計フェーズの進捗"
427
+ echo ""
428
+ local usecase detail_dir
429
+ usecase=$(state_get "usecase")
430
+ detail_dir=$(uc_detail)
431
+
432
+ local all_ok=true
433
+ for s in domain usecase table infra; do
434
+ local f="$detail_dir/$s.md"
435
+ if [[ -f "$f" ]]; then
436
+ local st
437
+ st=$(doc_status "$f")
438
+ if [[ "$st" == "reviewed" || "$st" == "approved" ]]; then
439
+ ok "$s.md (status: $st)"
440
+ else
441
+ warn "$s.md (status: $st) ← レビュー未完了"
442
+ all_ok=false
443
+ fi
444
+ else
445
+ fail "$s.md 未作成"
446
+ all_ok=false
447
+ fi
448
+ done
449
+
450
+ echo ""
451
+ if $all_ok; then
452
+ info "全サブフェーズ完了。次: ./scripts/spec-runner.sh test-design"
453
+ else
454
+ info "次のサブフェーズ: ./scripts/spec-runner.sh design-detail <domain|usecase|table|infra>"
455
+ fi
456
+ return
457
+ fi
458
+
459
+ echo "$valid_subs" | grep -qw "$sub" || die "無効なサブフェーズ: $sub(domain / usecase / table / infra)"
460
+
461
+ # ゲートチェック
462
+ echo ""
463
+ step "【ゲートチェック】詳細設計($sub)フェーズに進む条件を確認します"
464
+ echo ""
465
+
466
+ local errors=0
467
+ local high_file
468
+ high_file=$(uc_high)
469
+
470
+ check_file "$high_file" "概要設計ファイル" || ((errors++))
471
+ check_status "$high_file" "reviewed" "概要設計のステータス" || ((errors++))
472
+
473
+ # サブフェーズ固有の依存チェック
474
+ local detail_dir
475
+ detail_dir=$(uc_detail)
476
+ case "$sub" in
477
+ usecase)
478
+ check_file "$detail_dir/domain.md" "ドメインモデル設計" || ((errors++))
479
+ check_status "$detail_dir/domain.md" "reviewed" "ドメインモデルのレビュー" || ((errors++))
480
+ ;;
481
+ table)
482
+ check_file "$detail_dir/domain.md" "ドメインモデル設計" || ((errors++))
483
+ check_status "$detail_dir/domain.md" "reviewed" "ドメインモデルのレビュー" || ((errors++))
484
+ check_file "$detail_dir/usecase.md" "ユースケース設計" || ((errors++))
485
+ check_status "$detail_dir/usecase.md" "reviewed" "ユースケース設計のレビュー" || ((errors++))
486
+ ;;
487
+ infra)
488
+ check_file "$detail_dir/domain.md" "ドメインモデル設計" || ((errors++))
489
+ check_status "$detail_dir/domain.md" "reviewed" "ドメインモデルのレビュー" || ((errors++))
490
+ check_file "$detail_dir/usecase.md" "ユースケース設計" || ((errors++))
491
+ check_status "$detail_dir/usecase.md" "reviewed" "ユースケース設計のレビュー" || ((errors++))
492
+ check_file "$detail_dir/table.md" "テーブル設計" || ((errors++))
493
+ check_status "$detail_dir/table.md" "reviewed" "テーブル設計のレビュー" || ((errors++))
494
+ ;;
495
+ esac
496
+
497
+ if [[ $errors -gt 0 ]]; then
498
+ echo ""
499
+ die "ゲートを通過できません。${errors}件の条件が未達です ↑"
500
+ fi
501
+
502
+ echo ""
503
+ ok "ゲート通過!詳細設計($sub)フェーズを開始します"
504
+ state_set_str "phase" "design-detail-$sub"
505
+ state_push_history "design-detail-$sub フェーズ開始"
506
+
507
+ # ファイルをテンプレートから生成
508
+ local dest_file usecase
509
+ usecase=$(state_get "usecase")
510
+ dest_file="$detail_dir/$sub.md"
511
+ mkdir -p "$detail_dir"
512
+
513
+ local tmpl="$PROJECT_ROOT/templates/detailed/$sub.md"
514
+ if [[ ! -f "$dest_file" ]]; then
515
+ if [[ -f "$tmpl" ]]; then
516
+ sed "s/{{USECASE}}/$usecase/g; s/{{DATE}}/$(date +%Y-%m-%d)/g" "$tmpl" > "$dest_file"
517
+ else
518
+ echo "---
519
+ title: $usecase $(case $sub in domain) echo "ドメインモデル設計";; usecase) echo "ユースケース設計";; table) echo "テーブル設計";; infra) echo "インフラ設計";; esac)
520
+ status: draft
521
+ created: $(date +%Y-%m-%d)
522
+ updated: $(date +%Y-%m-%d)
523
+ ---
524
+
525
+ # $usecase $(case $sub in domain) echo "ドメインモデル設計";; usecase) echo "ユースケース設計";; table) echo "テーブル設計";; infra) echo "インフラ設計";; esac)
526
+ " > "$dest_file"
527
+ fi
528
+ ok "設計ファイルを作成: $dest_file"
529
+ fi
530
+
531
+ echo ""
532
+ info "次のステップ:"
533
+ info " 1. $dest_file を編集する"
534
+ info " 2. チームにレビューしてもらう"
535
+ info " 3. ./scripts/spec-runner.sh review-pass $dest_file"
536
+ case "$sub" in
537
+ domain) info " 4. ./scripts/spec-runner.sh design-detail usecase" ;;
538
+ usecase) info " 4. ./scripts/spec-runner.sh design-detail table" ;;
539
+ table) info " 4. ./scripts/spec-runner.sh design-detail infra" ;;
540
+ infra) info " 4. ./scripts/spec-runner.sh test-design" ;;
541
+ esac
542
+ }
543
+
544
+ # ── test-design ───────────────────────────────────────────────────────────────
545
+ cmd_test_design() {
546
+ require_state
547
+
548
+ echo ""
549
+ step "【ゲートチェック】テスト設計フェーズに進む条件を確認します"
550
+ echo ""
551
+
552
+ local errors=0
553
+ local detail_dir
554
+ detail_dir=$(uc_detail)
555
+
556
+ for sub in domain usecase table infra; do
557
+ check_file "$detail_dir/$sub.md" "詳細設計/$sub.md" || ((errors++))
558
+ check_status "$detail_dir/$sub.md" "reviewed" "詳細設計/$sub.md のレビュー" || ((errors++))
559
+ done
560
+
561
+ # ADRチェック(必要なADRが作成されているか確認を促す)
562
+ local adr_count
563
+ adr_count=$(find "$PROJECT_ROOT/docs/adr" -name "*.md" ! -name "TEMPLATE.md" 2>/dev/null | wc -l | tr -d ' ')
564
+ if [[ "$adr_count" -eq 0 ]]; then
565
+ warn "docs/adr/ にADRが1件もありません。設計判断があればADRを作成してください"
566
+ fi
567
+
568
+ if [[ $errors -gt 0 ]]; then
569
+ echo ""
570
+ die "ゲートを通過できません。${errors}件の条件が未達です ↑"
571
+ fi
572
+
573
+ echo ""
574
+ ok "ゲート通過!テスト設計フェーズを開始します"
575
+ state_set_str "phase" "test-design"
576
+ state_push_history "test-design フェーズ開始"
577
+
578
+ local test_file usecase
579
+ usecase=$(state_get "usecase")
580
+ test_file=$(uc_test)
581
+ mkdir -p "$(dirname "$test_file")"
582
+
583
+ if [[ ! -f "$test_file" ]]; then
584
+ cat > "$test_file" <<TMPL
585
+ ---
586
+ title: $usecase テスト設計
587
+ status: draft
588
+ created: $(date +%Y-%m-%d)
589
+ updated: $(date +%Y-%m-%d)
590
+ ---
591
+
592
+ # $usecase テスト設計
593
+
594
+ ## テスト方針
595
+
596
+ | 種別 | 対象 | ツール |
597
+ |------|------|-------|
598
+ | Unit | ドメインモデルの振る舞い | PHPUnit |
599
+ | Unit | 値オブジェクトのバリデーション | PHPUnit |
600
+ | Integration | ユースケース | PHPUnit + DB |
601
+ | Feature | APIエンドポイント | PHPUnit + HTTP |
602
+
603
+ ## ドメインモデル テストケース
604
+
605
+ | # | テストケース名(日本語) | 入力 | 期待結果 | 種別 |
606
+ |---|----------------------|------|---------|------|
607
+ | 1 | の場合、となる | | | 正常 |
608
+
609
+ ## ユースケース テストケース
610
+
611
+ | # | テストケース名(日本語) | 前提条件 | 入力 | 期待結果 |
612
+ |---|----------------------|---------|------|---------|
613
+ | 1 | | | | |
614
+
615
+ ## テストコードチェックリスト
616
+
617
+ - [ ] ドメインモデルのUnitテスト作成済み(Red状態確認)
618
+ - [ ] 値オブジェクトのUnitテスト作成済み(Red状態確認)
619
+ - [ ] ユースケースのIntegrationテスト作成済み(Red状態確認)
620
+ - [ ] APIのFeatureテスト作成済み(Red状態確認)
621
+ TMPL
622
+ ok "テスト設計ファイルを作成: $test_file"
623
+ fi
624
+
625
+ echo ""
626
+ info "次のステップ:"
627
+ info " 1. $test_file を編集する(テスト設計)"
628
+ info " 2. テストコードを書く(実装前。Red状態でOK)"
629
+ info " 3. git commit でテストコードをコミット"
630
+ info " 4. ./scripts/spec-runner.sh review-pass $test_file"
631
+ info " 5. ./scripts/spec-runner.sh implement"
632
+ }
633
+
634
+ # ── implement ─────────────────────────────────────────────────────────────────
635
+ cmd_implement() {
636
+ require_state
637
+
638
+ echo ""
639
+ step "【ゲートチェック】実装フェーズに進む条件を確認します"
640
+ echo ""
641
+
642
+ local errors=0
643
+ local test_file
644
+ test_file=$(uc_test)
645
+
646
+ # TDD 有効時のみ: テスト設計+テストコードを必須(.spec-runner/config.sh の TDD_ENABLED=false で無効化可)
647
+ if [[ "${TDD_ENABLED:-true}" != "false" ]]; then
648
+ check_file "$test_file" "テスト設計ファイル" || ((errors++))
649
+ check_status "$test_file" "reviewed" "テスト設計のレビュー" || ((errors++))
650
+
651
+ local test_committed
652
+ test_committed=$(state_get "gates.test_code_committed")
653
+ if [[ "$test_committed" != "true" ]]; then
654
+ # テストディレクトリ(.spec-runner/config.sh の TEST_DIR)配下の未コミットを検出
655
+ local test_dir_prefix="${TEST_DIR:-tests}"
656
+ local uncommitted_under_test
657
+ uncommitted_under_test=$(git status --porcelain 2>/dev/null | awk '{print $2}' | grep -E "^${test_dir_prefix}/" | wc -l | tr -d ' ')
658
+ if [[ "${uncommitted_under_test:-0}" -gt 0 ]]; then
659
+ fail "テストコードが未コミットです(TDD 必須。${test_dir_prefix}/ 配下をコミットするか set-gate test_code_committed)"
660
+ ((errors++))
661
+ else
662
+ warn "テストコードがコミット済みか未確認です"
663
+ warn "確認後: ./scripts/spec-runner.sh set-gate test_code_committed"
664
+ fi
665
+ else
666
+ check_gate "test_code_committed" "テストコードのコミット確認" || ((errors++))
667
+ fi
668
+ else
669
+ # TDD オプション時: テスト設計・テストコードは必須ではない(あれば推奨)
670
+ if [[ -f "$test_file" ]]; then
671
+ ok "テスト設計ファイル: $test_file(TDD オプションのため未レビューでも可)"
672
+ else
673
+ warn "テスト設計ファイルがありません(TDD オプションのため実装は進められます)"
674
+ fi
675
+ fi
676
+
677
+ if [[ $errors -gt 0 ]]; then
678
+ echo ""
679
+ die "ゲートを通過できません。${errors}件の条件が未達です ↑"
680
+ fi
681
+
682
+ echo ""
683
+ ok "ゲート通過!実装フェーズを開始します"
684
+ state_set_str "phase" "implement"
685
+ state_push_history "implement フェーズ開始"
686
+
687
+ echo ""
688
+ warn "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
689
+ warn " 実装フェーズのルール"
690
+ warn "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
691
+ info " 1. テストを Green にする実装を書く"
692
+ info " 2. 設計と乖離した場合は先にドキュメントを更新する"
693
+ info " 3. コードとドキュメントを同一コミットに含める"
694
+ info " 4. 完了後: ./scripts/spec-runner.sh complete"
695
+ warn "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
696
+ }
697
+
698
+ # ── complete ──────────────────────────────────────────────────────────────────
699
+ cmd_complete() {
700
+ require_state
701
+
702
+ echo ""
703
+ step "【完了チェック】実装の完了条件を確認します"
704
+ echo ""
705
+
706
+ local errors=0
707
+
708
+ # テストが通るか確認を促す
709
+ warn "テストが全て通過していることを確認してください"
710
+ read -r -p "全テスト通過済みですか? [y/N] " ans_test
711
+ [[ "$ans_test" =~ ^[Yy]$ ]] || { fail "テストを通過させてから完了してください"; ((errors++)); }
712
+
713
+ # ドキュメントと実装の乖離チェック
714
+ read -r -p "設計ドキュメントと実装に乖離はないですか? [y/N] " ans_doc
715
+ [[ "$ans_doc" =~ ^[Yy]$ ]] || { fail "ドキュメントを更新してから完了してください"; ((errors++)); }
716
+
717
+ if [[ $errors -gt 0 ]]; then
718
+ echo ""
719
+ die "完了条件を満たしていません"
720
+ fi
721
+
722
+ state_set_str "phase" "complete"
723
+ state_push_history "実装完了"
724
+
725
+ # ブランチ情報の表示
726
+ local branch agg_branch usecase
727
+ branch=$(state_get "branch")
728
+ agg_branch=$(state_get "aggregate_branch")
729
+ usecase=$(state_get "usecase")
730
+
731
+ echo ""
732
+ ok "『$usecase』の実装が完了しました!"
733
+ echo ""
734
+ info "次のステップ:"
735
+ info " 1. git push origin $branch"
736
+ info " 2. Pull Request を作成(.github/PULL_REQUEST_TEMPLATE.md を使用)"
737
+ if [[ -n "$agg_branch" ]]; then
738
+ info " 3. PRマージ先: $agg_branch"
739
+ info " 4. 関連ユースケースがすべて揃ったら $agg_branch → main へPR"
740
+ fi
741
+ }
742
+
743
+ # ── review-pass ───────────────────────────────────────────────────────────────
744
+ cmd_review_pass() {
745
+ local file="${1:-}"
746
+ [[ -n "$file" ]] || die "使い方: ./scripts/spec-runner.sh review-pass <ファイルパス>"
747
+ [[ -f "$file" ]] || die "ファイルが存在しません: $file"
748
+
749
+ # frontmatterのstatusを更新
750
+ local today
751
+ today=$(date +%Y-%m-%d)
752
+
753
+ if grep -q '^status:' "$file"; then
754
+ # 既存のstatusを更新(同一デバイス上に一時ファイルを作成してmv)
755
+ local tmp
756
+ tmp="${file}.tmp.$$"
757
+ sed "s/^status: .*/status: reviewed/" "$file" > "$tmp" && mv "$tmp" "$file"
758
+ # updated日付も更新
759
+ tmp="${file}.tmp.$$"
760
+ sed "s/^updated: .*/updated: $today/" "$file" > "$tmp" && mv "$tmp" "$file"
761
+ else
762
+ die "ファイルにfrontmatter(status:)がありません: $file"
763
+ fi
764
+
765
+ ok "レビュー通過: $file (status: reviewed)"
766
+
767
+ # どのゲートフラグを立てるか判定
768
+ require_state
769
+ local uc_slug_val
770
+ uc_slug_val=$(uc_slug)
771
+
772
+ case "$file" in
773
+ *requirements*)
774
+ state_set_bool "gates.require_approved" true
775
+ state_set_str "phase" "require-approved"
776
+ # statusもapprovedに(同一デバイス上で一時ファイル)
777
+ local tmp2
778
+ tmp2="${file}.tmp.$$"
779
+ sed "s/^status: .*/status: approved/" "$file" > "$tmp2" && mv "$tmp2" "$file"
780
+ ok "ゲート更新: require_approved = true"
781
+ info "次: glossary.md を確認後 ./scripts/spec-runner.sh set-gate glossary_checked"
782
+ info " ./scripts/spec-runner.sh design-high"
783
+ ;;
784
+ *high-level*)
785
+ state_set_bool "gates.high_level_reviewed" true
786
+ ok "ゲート更新: high_level_reviewed = true"
787
+ info "次: ./scripts/spec-runner.sh design-detail domain"
788
+ ;;
789
+ *domain*)
790
+ state_set_bool "gates.domain_model_reviewed" true
791
+ ok "ゲート更新: domain_model_reviewed = true"
792
+ info "次: ./scripts/spec-runner.sh design-detail usecase"
793
+ ;;
794
+ *usecase*)
795
+ state_set_bool "gates.usecase_design_reviewed" true
796
+ ok "ゲート更新: usecase_design_reviewed = true"
797
+ info "次: ./scripts/spec-runner.sh design-detail table"
798
+ ;;
799
+ *table*)
800
+ state_set_bool "gates.table_design_reviewed" true
801
+ ok "ゲート更新: table_design_reviewed = true"
802
+ info "次: ./scripts/spec-runner.sh design-detail infra"
803
+ ;;
804
+ *infra*)
805
+ state_set_bool "gates.infra_design_reviewed" true
806
+ ok "ゲート更新: infra_design_reviewed = true"
807
+ info "次: ./scripts/spec-runner.sh test-design"
808
+ ;;
809
+ *test-design*)
810
+ state_set_bool "gates.test_design_reviewed" true
811
+ ok "ゲート更新: test_design_reviewed = true"
812
+ info "テストコードをコミット後: ./scripts/spec-runner.sh set-gate test_code_committed"
813
+ info "その後: ./scripts/spec-runner.sh implement"
814
+ ;;
815
+ esac
816
+ }
817
+
818
+ # ── set-gate ──────────────────────────────────────────────────────────────────
819
+ cmd_set_gate() {
820
+ require_state
821
+ local gate="${1:-}"
822
+ [[ -n "$gate" ]] || die "使い方: ./scripts/spec-runner.sh set-gate <ゲート名>"
823
+ state_set_bool "gates.$gate" true
824
+ ok "ゲートフラグ設定: $gate = true"
825
+ }
826
+
827
+ # ── status ─────────────────────────────────────────────────────────────────────
828
+ cmd_status() {
829
+ if [[ ! -f "$STATE_FILE" ]]; then
830
+ info "作業中のユースケースはありません"
831
+ info "開始するには: ./scripts/spec-runner.sh init <ユースケース名>"
832
+ return
833
+ fi
834
+
835
+ local usecase phase branch agg_branch
836
+ usecase=$(state_get "usecase")
837
+ phase=$(state_get "phase")
838
+ branch=$(state_get "branch")
839
+ agg_branch=$(state_get "aggregate_branch")
840
+
841
+ echo ""
842
+ echo -e "${BOLD}════════════════════════════════════════${NC}"
843
+ echo -e "${BOLD} 現在の作業状況${NC}"
844
+ echo -e "${BOLD}════════════════════════════════════════${NC}"
845
+ echo -e " ユースケース : ${CYAN}$usecase${NC}"
846
+ echo -e " フェーズ : ${YELLOW}$phase${NC}"
847
+ echo -e " ブランチ : ${BLUE}$branch${NC}"
848
+ [[ -n "$agg_branch" ]] && echo -e " 集約ブランチ: ${BLUE}$agg_branch${NC}"
849
+ echo ""
850
+ echo -e "${BOLD} ゲート状況${NC}"
851
+
852
+ local gates
853
+ for gate in require_approved glossary_checked high_level_reviewed \
854
+ domain_model_reviewed usecase_design_reviewed \
855
+ table_design_reviewed infra_design_reviewed \
856
+ test_design_reviewed test_code_committed; do
857
+ local val
858
+ val=$(state_get "gates.$gate")
859
+ if [[ "$val" == "true" ]]; then
860
+ echo -e " ${GREEN}✓${NC} $gate"
861
+ else
862
+ echo -e " ${RED}✗${NC} $gate"
863
+ fi
864
+ done
865
+
866
+ echo ""
867
+ echo -e "${BOLD} 次にやるべきこと${NC}"
868
+ local req_file high_file
869
+ req_file=$(uc_req)
870
+ high_file=$(uc_high)
871
+ case "$phase" in
872
+ require)
873
+ echo -e " 1. ${CYAN}$req_file${NC} を編集"
874
+ echo -e " 2. ./scripts/spec-runner.sh review-pass $req_file"
875
+ echo -e " 3. ./scripts/spec-runner.sh set-gate glossary_checked"
876
+ echo -e " 4. ./scripts/spec-runner.sh design-high"
877
+ ;;
878
+ design-high)
879
+ echo -e " 1. ${CYAN}$high_file${NC} を編集"
880
+ echo -e " 2. ./scripts/spec-runner.sh review-pass $high_file"
881
+ echo -e " 3. ./scripts/spec-runner.sh design-detail domain"
882
+ ;;
883
+ design-detail)
884
+ if [[ "$(state_get "gates.domain_model_reviewed")" != "true" ]]; then
885
+ echo -e " 1. docs/detailed/$(uc_slug)/domain.md を編集"
886
+ echo -e " 2. review-pass のあと ./scripts/spec-runner.sh design-detail usecase"
887
+ elif [[ "$(state_get "gates.usecase_design_reviewed")" != "true" ]]; then
888
+ echo -e " 1. docs/detailed/$(uc_slug)/usecase.md を編集"
889
+ echo -e " 2. review-pass のあと ./scripts/spec-runner.sh design-detail table"
890
+ elif [[ "$(state_get "gates.table_design_reviewed")" != "true" ]]; then
891
+ echo -e " 1. docs/detailed/$(uc_slug)/table.md を編集"
892
+ echo -e " 2. review-pass のあと ./scripts/spec-runner.sh design-detail infra"
893
+ elif [[ "$(state_get "gates.infra_design_reviewed")" != "true" ]]; then
894
+ echo -e " 1. docs/detailed/$(uc_slug)/infra.md を編集"
895
+ echo -e " 2. review-pass のあと ./scripts/spec-runner.sh test-design"
896
+ else
897
+ echo -e " ./scripts/spec-runner.sh test-design"
898
+ fi
899
+ ;;
900
+ test-design)
901
+ echo -e " 1. docs/test-design/$(uc_slug).md を編集し、テストコードを書く(Red)"
902
+ echo -e " 2. テストコードをコミット → ./scripts/spec-runner.sh set-gate test_code_committed"
903
+ echo -e " 3. ./scripts/spec-runner.sh review-pass docs/test-design/$(uc_slug).md"
904
+ echo -e " 4. ./scripts/spec-runner.sh implement"
905
+ ;;
906
+ implement)
907
+ echo -e " 実装してテストを Green にしたら: ./scripts/spec-runner.sh complete"
908
+ ;;
909
+ complete)
910
+ echo -e " 完了。PR を作成してマージしてください。"
911
+ ;;
912
+ fix)
913
+ echo -e " 案内に従って該当ドキュメントを修正し、必要なら design-detail 等から再実行。"
914
+ ;;
915
+ *)
916
+ echo -e " ./scripts/spec-runner.sh help でコマンド一覧を確認"
917
+ ;;
918
+ esac
919
+ echo ""
920
+ echo -e "${BOLD} 履歴${NC}"
921
+ jq -r '.history[]' "$STATE_FILE" 2>/dev/null | tail -5 | while read -r line; do
922
+ echo " $line"
923
+ done
924
+ echo -e "${BOLD}════════════════════════════════════════${NC}"
925
+
926
+ # debt確認
927
+ if [[ -f "$DEBT_FILE" ]]; then
928
+ local unchecked
929
+ unchecked=$(grep -c '^\- \[ \]' "$DEBT_FILE" 2>/dev/null; true)
930
+ unchecked="${unchecked:-0}"
931
+ if [[ "${unchecked}" -gt 0 ]]; then
932
+ warn "ドキュメント負債: ${unchecked} 件"
933
+ fi
934
+ fi
935
+ }
936
+
937
+ # ── fix ───────────────────────────────────────────────────────────────────────
938
+ cmd_fix() {
939
+ local content="${1:-}"
940
+ [[ -n "$content" ]] || die "使い方: ./scripts/spec-runner.sh fix <修正内容>"
941
+
942
+ echo ""
943
+ step "修正フロー: $content"
944
+ echo ""
945
+ echo "修正レベルを選択してください:"
946
+ echo " 1) ドメインモデルの構造変更 → domain設計から再実行"
947
+ echo " 2) ユースケースのフロー変更 → usecase設計から再実行"
948
+ echo " 3) APIの仕様変更 → infra設計から再実行"
949
+ echo " 4) テーブル構造の変更 → table設計から再実行"
950
+ echo " 5) バグ修正(設計は正しい)→ テスト追加→実装修正"
951
+ echo ""
952
+ read -r -p "番号を選択 [1-5]: " level
953
+
954
+ local usecase slug branch
955
+ slug=$(echo "$content" | tr ' ' '-' | tr -cd '[:alnum:]-' | cut -c1-30)
956
+ usecase="${2:-$(state_get "usecase" 2>/dev/null || echo "unknown")}"
957
+ branch="fix/$(echo "$usecase" | tr ' ' '-')-$slug"
958
+
959
+ git checkout -b "$branch" 2>/dev/null || warn "ブランチ作成に失敗しました(手動で作成してください)"
960
+
961
+ case "$level" in
962
+ 1)
963
+ info "ドメインモデルから再設計します"
964
+ info "修正ファイル:"
965
+ info " docs/detailed/$(uc_slug)/domain.md → status: draft に戻す"
966
+ info " docs/detailed/$(uc_slug)/usecase.md → 再確認"
967
+ info " docs/test-design/$(uc_slug).md → 再確認"
968
+ ;;
969
+ 2)
970
+ info "ユースケース設計から修正します"
971
+ info " docs/detailed/$(uc_slug)/usecase.md → status: draft に戻す"
972
+ info " docs/test-design/$(uc_slug).md → 再確認"
973
+ ;;
974
+ 3)
975
+ info "インフラ設計から修正します"
976
+ info " docs/detailed/$(uc_slug)/infra.md → status: draft に戻す"
977
+ ;;
978
+ 4)
979
+ info "テーブル設計から修正します"
980
+ info " docs/detailed/$(uc_slug)/table.md → status: draft に戻す"
981
+ ;;
982
+ 5)
983
+ info "テストを追加して実装を修正します"
984
+ info " テストコード追加 → git commit → ./scripts/spec-runner.sh implement"
985
+ ;;
986
+ *)
987
+ die "無効な選択です"
988
+ ;;
989
+ esac
990
+
991
+ state_set_str "phase" "fix"
992
+ state_push_history "修正開始: $content (level: $level)"
993
+ }
994
+
995
+ # ── hotfix ────────────────────────────────────────────────────────────────────
996
+ cmd_hotfix() {
997
+ local content="${1:-}"
998
+ [[ -n "$content" ]] || die "使い方: ./scripts/spec-runner.sh hotfix <内容>"
999
+
1000
+ local slug branch
1001
+ slug=$(echo "$content" | tr ' ' '-' | tr -cd '[:alnum:]-' | cut -c1-30)
1002
+ branch="hotfix/$slug"
1003
+
1004
+ step "緊急修正ブランチを作成: $branch"
1005
+ git checkout -b "$branch" main
1006
+
1007
+ ok "ブランチ作成: $branch"
1008
+ warn "緊急修正後、ドキュメント負債として記録されます"
1009
+
1010
+ # debt.mdに追記
1011
+ mkdir -p "$(dirname "$DEBT_FILE")"
1012
+ [[ -f "$DEBT_FILE" ]] || echo "# ドキュメント負債" > "$DEBT_FILE"
1013
+
1014
+ cat >> "$DEBT_FILE" <<DEBT
1015
+
1016
+ ## $(date +%Y-%m-%d): hotfix/$slug
1017
+
1018
+ - [ ] 修正内容に対応するテスト設計ドキュメントの更新
1019
+ - hotfix内容: $content
1020
+ - 対象ユースケース: 要確認
1021
+ - 修正したコード: 要確認
1022
+ DEBT
1023
+
1024
+ ok "負債を記録: $DEBT_FILE"
1025
+ info "修正完了後: git push origin $branch → main へPR"
1026
+ info "次回の init 時に負債の消化を促されます"
1027
+ }
1028
+
1029
+ # ── メインルーター ────────────────────────────────────────────────────────────
1030
+ main() {
1031
+ local cmd="${1:-help}"
1032
+ shift || true
1033
+
1034
+ case "$cmd" in
1035
+ init) cmd_init "$@" ;;
1036
+ require) cmd_require "$@" ;;
1037
+ design-high) cmd_design_high "$@" ;;
1038
+ design-detail) cmd_design_detail "$@" ;;
1039
+ test-design) cmd_test_design "$@" ;;
1040
+ implement) cmd_implement "$@" ;;
1041
+ complete) cmd_complete "$@" ;;
1042
+ review-pass) cmd_review_pass "$@" ;;
1043
+ set-gate) cmd_set_gate "$@" ;;
1044
+ status) cmd_status "$@" ;;
1045
+ fix) cmd_fix "$@" ;;
1046
+ hotfix) cmd_hotfix "$@" ;;
1047
+ help|--help|-h)
1048
+ echo ""
1049
+ echo -e "${BOLD}使い方:${NC}"
1050
+ echo " ./scripts/spec-runner.sh <コマンド> [引数]"
1051
+ echo ""
1052
+ echo -e "${BOLD}新規開発フロー:${NC}"
1053
+ echo " init [ユースケース名] [集約名] 引数なしで設定対話。名前を渡すとユースケース作成"
1054
+ echo " require 要件定義フェーズ(ファイルパスを確認)"
1055
+ echo " design-high 概要設計フェーズに移行(ゲートチェック)"
1056
+ echo " design-detail <sub> 詳細設計フェーズ(sub: domain|usecase|table|infra)"
1057
+ echo " test-design テスト設計フェーズに移行(ゲートチェック)"
1058
+ echo " implement 実装フェーズに移行(ゲートチェック)"
1059
+ echo " complete 実装完了(完了チェック)"
1060
+ echo ""
1061
+ echo -e "${BOLD}レビュー:${NC}"
1062
+ echo " review-pass <ファイル> ドキュメントをレビュー通過にする"
1063
+ echo " set-gate <ゲート名> 手動でゲートフラグを立てる"
1064
+ echo ""
1065
+ echo -e "${BOLD}確認:${NC}"
1066
+ echo " status 現在の状態を表示"
1067
+ echo ""
1068
+ echo -e "${BOLD}修正フロー:${NC}"
1069
+ echo " fix <修正内容> 通常修正フロー(影響範囲分析)"
1070
+ echo " hotfix <内容> 緊急修正(負債として記録)"
1071
+ echo ""
1072
+ ;;
1073
+ *)
1074
+ die "不明なコマンド: $cmd\n使い方: ./scripts/spec-runner.sh help"
1075
+ ;;
1076
+ esac
1077
+ }
1078
+
1079
+ main "$@"