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.
- package/LICENSE +34 -0
- package/README.md +193 -0
- package/bin/spec-runner.js +715 -0
- package/install.sh +80 -0
- package/package.json +45 -0
- package/templates/base/.github/PULL_REQUEST_TEMPLATE.md +43 -0
- package/templates/base/.github/workflows/phase-gate-check.yml +216 -0
- package/templates/base/docs/adr/TEMPLATE.md +46 -0
- package/templates/base/docs/glossary.md +51 -0
- package/templates/base/docs/review/debt.md +8 -0
- package/templates/base/scripts/spec-runner.sh +1079 -0
- package/templates/base/templates/requirement/template.md +40 -0
- package/templates/claude/.claude/commands/sr-complete.md +9 -0
- package/templates/claude/.claude/commands/sr-configure.md +11 -0
- package/templates/claude/.claude/commands/sr-design-detail.md +9 -0
- package/templates/claude/.claude/commands/sr-design-high.md +9 -0
- package/templates/claude/.claude/commands/sr-fix.md +9 -0
- package/templates/claude/.claude/commands/sr-hotfix.md +9 -0
- package/templates/claude/.claude/commands/sr-implement.md +9 -0
- package/templates/claude/.claude/commands/sr-init.md +10 -0
- package/templates/claude/.claude/commands/sr-review.md +9 -0
- package/templates/claude/.claude/commands/sr-set-gate.md +9 -0
- package/templates/claude/.claude/commands/sr-status.md +9 -0
- package/templates/claude/.claude/commands/sr-test-design.md +9 -0
- package/templates/claude/.claude/hooks/pre-tool-use.sh +79 -0
- package/templates/claude/.claude/settings.json +29 -0
- package/templates/claude/CLAUDE.md +141 -0
- package/templates/copilot/.github/copilot-instructions.md +25 -0
- package/templates/copilot/.github/prompts/sr-complete.prompt.md +13 -0
- package/templates/copilot/.github/prompts/sr-configure.prompt.md +13 -0
- package/templates/copilot/.github/prompts/sr-design-detail.prompt.md +14 -0
- package/templates/copilot/.github/prompts/sr-design-high.prompt.md +13 -0
- package/templates/copilot/.github/prompts/sr-fix.prompt.md +14 -0
- package/templates/copilot/.github/prompts/sr-hotfix.prompt.md +14 -0
- package/templates/copilot/.github/prompts/sr-implement.prompt.md +13 -0
- package/templates/copilot/.github/prompts/sr-init.prompt.md +15 -0
- package/templates/copilot/.github/prompts/sr-review.prompt.md +14 -0
- package/templates/copilot/.github/prompts/sr-set-gate.prompt.md +14 -0
- package/templates/copilot/.github/prompts/sr-status.prompt.md +13 -0
- package/templates/copilot/.github/prompts/sr-test-design.prompt.md +13 -0
- package/templates/cursor/.cursor/commands/sr-complete.md +9 -0
- package/templates/cursor/.cursor/commands/sr-configure.md +11 -0
- package/templates/cursor/.cursor/commands/sr-design-detail.md +9 -0
- package/templates/cursor/.cursor/commands/sr-design-high.md +9 -0
- package/templates/cursor/.cursor/commands/sr-fix.md +9 -0
- package/templates/cursor/.cursor/commands/sr-hotfix.md +9 -0
- package/templates/cursor/.cursor/commands/sr-implement.md +9 -0
- package/templates/cursor/.cursor/commands/sr-init.md +10 -0
- package/templates/cursor/.cursor/commands/sr-review.md +9 -0
- package/templates/cursor/.cursor/commands/sr-set-gate.md +9 -0
- package/templates/cursor/.cursor/commands/sr-status.md +9 -0
- package/templates/cursor/.cursor/commands/sr-test-design.md +9 -0
- 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 "$@"
|