spec-runner 1.0.24 → 1.1.0

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 (32) hide show
  1. package/README.md +15 -6
  2. package/bin/spec-runner.js +28 -18
  3. package/docs/flow.md +58 -201
  4. package/package.json +1 -1
  5. package/templates/.spec-runner/project.json.example +1 -1
  6. package/templates/.spec-runner/scripts/check.sh +2 -38
  7. package/templates/.spec-runner/scripts/spec-runner-core.sh +129 -348
  8. package/templates/.spec-runner/scripts/uc-next-start.sh +154 -0
  9. package/templates/.spec-runner/spec-runner.sh +2 -3
  10. package/templates/.spec-runner/steps/steps.json +22 -97
  11. package/templates/.spec-runner/steps//343/203/206/343/202/271/343/203/210/350/250/255/350/250/210.md +10 -9
  12. package/templates/.spec-runner/steps//343/203/211/343/203/241/343/202/244/343/203/263/350/250/255/350/250/210.md +2 -2
  13. package/templates/.spec-runner/steps//344/273/225/346/247/230/347/255/226/345/256/232.md +16 -14
  14. package/templates/.spec-runner/steps//345/210/206/346/236/220.md +2 -2
  15. package/templates/.spec-runner/steps//345/256/237/350/243/205.md +8 -7
  16. package/templates/.spec-runner/steps//345/256/237/350/243/205/350/250/210/347/224/273.md +10 -10
  17. package/templates/.spec-runner/steps//346/206/262/347/253/240.md +1 -1
  18. package/templates/.spec-runner/steps//346/233/226/346/230/247/343/201/225/350/247/243/346/266/210.md +1 -1
  19. package/templates/mkdocs-scaffold/docs/index.md +2 -2
  20. package/templates/skills/uc-k1-work-card-init/SKILL.md +76 -0
  21. package/templates/skills/uc-k2-pre-commit-check/SKILL.md +57 -0
  22. package/templates/skills/uc-k3-spec-impl-diff-review/SKILL.md +57 -0
  23. package/templates/spec-runner-command.md +4 -3
  24. package/templates/.spec-runner/hooks/pre-commit +0 -47
  25. package/templates/.spec-runner/hooks/pre-push +0 -9
  26. package/templates/.spec-runner/scripts/branch/uc-next-start.sh +0 -224
  27. package/templates/.spec-runner/scripts/docs-serve.sh +0 -21
  28. package/templates/.spec-runner/scripts/test/require-tests-green.sh +0 -27
  29. package/templates/.spec-runner/steps//343/201/235/343/201/256/344/273/226/344/275/234/346/245/255.md +0 -34
  30. package/templates/.spec-runner/steps//343/202/277/343/202/271/343/202/257/344/270/200/350/246/247.md +0 -95
  31. package/templates/.spec-runner/steps//343/203/201/343/202/247/343/203/203/343/202/257/343/203/252/343/202/271/343/203/210.md +0 -80
  32. package/templates/.spec-runner/templates/grade-history.json +0 -5
@@ -1,33 +1,20 @@
1
1
  #!/usr/bin/env bash
2
- # spec-runner 判定の単一入口。振る舞い仕様に沿って「フェーズ・ゲート・状態・グレード」を扱う。
3
- # 使用: spec-runner-core.sh [--phase] [--json] | --gate [GRADE] | --status | --grade
4
- # cmd-dispatch.sh(次のステップ・ゲート確認・ブランチ作成)から呼ばれる。
2
+ # spec-runner 判定の単一入口(薄いオーケストレータ)
3
+ # 使用: spec-runner-core.sh [--phase] [--json] | --status
5
4
 
6
5
  set -e
7
6
 
8
- # ============================================================
9
- # 0) 基本設定
10
- # ============================================================
11
7
  REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
12
8
  cd "$REPO_ROOT"
13
9
  STEPS_DIR="${STEPS_DIR:-$REPO_ROOT/.spec-runner/steps}"
14
10
  STEPS_JSON="${STEPS_JSON:-$STEPS_DIR/steps.json}"
15
11
  LOCK_FILE=".spec-runner/phase-locks.json"
16
- GRADE_FILE=".spec-runner/grade-history.json"
17
12
  PROJECT_JSON=".spec-runner/project.json"
18
13
 
19
- # ============================================================
20
- # 1) ユーティリティ
21
- # ============================================================
22
14
  die() { echo "$1" >&2; exit 1; }
23
15
 
24
- require_cmd() {
25
- command -v "$1" >/dev/null 2>&1 || die "spec-runner-core: $1 が必要です(例: brew install $1"
26
- }
27
-
28
- require_file() {
29
- [[ -f "$1" ]] || die "spec-runner-core: ファイルがありません: $1"
30
- }
16
+ require_cmd() { command -v "$1" >/dev/null 2>&1 || die "spec-runner-core: $1 が必要です"; }
17
+ require_file() { [[ -f "$1" ]] || die "spec-runner-core: ファイルがありません: $1"; }
31
18
 
32
19
  get_steps_common_doc() {
33
20
  local key="$1"
@@ -37,228 +24,112 @@ get_steps_common_doc() {
37
24
  echo "$v"
38
25
  }
39
26
 
40
- # ============================================================
41
- # 2) 引数解析
42
- # ============================================================
43
27
  MODE="phase"
44
28
  JSON_MODE=false
45
- GATE_GRADE=""
46
- parse_args() {
47
- while [[ $# -gt 0 ]]; do
48
- case "$1" in
49
- --phase) MODE="phase" ;;
50
- --json) JSON_MODE=true ;;
51
- --gate) MODE="gate"; GATE_GRADE="${2:-}"; shift ;;
52
- --status) MODE="status" ;;
53
- --grade) MODE="grade" ;;
54
- *) [[ "$MODE" == "gate" && -z "$GATE_GRADE" ]] && GATE_GRADE="$1" ;;
55
- esac
56
- shift
57
- done
58
- }
59
- parse_args "$@"
29
+ while [[ $# -gt 0 ]]; do
30
+ case "$1" in
31
+ --phase) MODE="phase" ;;
32
+ --json) JSON_MODE=true ;;
33
+ --status) MODE="status" ;;
34
+ esac
35
+ shift
36
+ done
60
37
 
61
- # ============================================================
62
- # 3) 前提チェック & 状態ロード(1回だけ)
63
- # ============================================================
64
38
  require_cmd jq
65
39
  require_file "$PROJECT_JSON"
66
40
  require_file "$STEPS_JSON"
67
41
  require_file "$LOCK_FILE"
68
- require_file "$GRADE_FILE"
69
42
 
70
43
  has_charter_lock=0
71
44
  has_domain_lock=0
72
45
  has_arch_lock=0
73
- has_infra_lock=0
74
46
  uc_discovery_completed=0
75
- grade=""
76
- branch=""
77
47
  test_dir=""
78
48
  test_pattern=""
79
- branch_prefix=""
80
- uc_id_re=""
81
- other_work_pattern=""
82
- on_uc_branch=0
83
- on_other_work_branch=0
84
- current_uc_id=""
85
-
86
- load_state() {
87
- jq -e '.charter.completed == true' "$LOCK_FILE" >/dev/null 2>&1 && has_charter_lock=1
88
- jq -e '.domain.completed == true' "$LOCK_FILE" >/dev/null 2>&1 && has_domain_lock=1
89
- jq -e '.architecture.completed == true' "$LOCK_FILE" >/dev/null 2>&1 && has_arch_lock=1
90
- jq -e '.infra.completed == true' "$LOCK_FILE" >/dev/null 2>&1 && has_infra_lock=1
91
- jq -e '.uc_discovery.completed == true' "$LOCK_FILE" >/dev/null 2>&1 && uc_discovery_completed=1
92
-
93
- grade=$(jq -r '.current_grade' "$GRADE_FILE")
94
- [[ -n "$grade" && "$grade" != "null" ]] || die "spec-runner-core: grade-history.json の current_grade が未設定です"
95
-
96
- branch=$(git branch --show-current 2>/dev/null || echo "")
49
+ require_uc_prefixed_tests=0
50
+
51
+ jq -e '.charter.completed == true' "$LOCK_FILE" >/dev/null 2>&1 && has_charter_lock=1
52
+ jq -e '.domain.completed == true' "$LOCK_FILE" >/dev/null 2>&1 && has_domain_lock=1
53
+ jq -e '.architecture.completed == true' "$LOCK_FILE" >/dev/null 2>&1 && has_arch_lock=1
54
+ jq -e '.uc_discovery.completed == true' "$LOCK_FILE" >/dev/null 2>&1 && uc_discovery_completed=1
55
+
56
+ test_dir=$(jq -r '.test_design.dir' "$PROJECT_JSON")
57
+ [[ -n "$test_dir" && "$test_dir" != "null" ]] || die "spec-runner-core: project.json test_design.dir が未設定です"
58
+ test_pattern=$(jq -r '.test_design.pattern' "$PROJECT_JSON")
59
+ [[ -n "$test_pattern" && "$test_pattern" != "null" ]] || die "spec-runner-core: project.json test_design.pattern が未設定です"
60
+ rq="$(jq -r '.test_design.require_uc_prefixed_tests // false' "$PROJECT_JSON")"
61
+ [[ "$rq" == "true" || "$rq" == "1" ]] && require_uc_prefixed_tests=1
62
+
63
+ quality_done() {
64
+ local kind="$1"
65
+ local scope="$2"
66
+ local key="$3"
67
+ local key_md="${key}.md"
68
+ jq -e --arg k "$key" --arg km "$key_md" --arg s "$scope" \
69
+ ".quality.${kind}[\$s][]? | select(. == \$k or . == \$km)" \
70
+ "$LOCK_FILE" >/dev/null 2>&1
71
+ }
97
72
 
98
- branch_prefix=$(jq -r '.naming.branch_prefix' "$PROJECT_JSON")
99
- [[ -n "$branch_prefix" && "$branch_prefix" != "null" ]] || die "spec-runner-core: project.json naming.branch_prefix が未設定です"
100
- uc_id_re=$(jq -r '.naming.uc_id_pattern' "$PROJECT_JSON")
101
- [[ -n "$uc_id_re" && "$uc_id_re" != "null" ]] || die "spec-runner-core: project.json naming.uc_id_pattern が未設定です"
102
- other_work_pattern=$(jq -r '.naming.other_work_prefixes | join("|")' "$PROJECT_JSON")
103
- [[ -n "$other_work_pattern" ]] || die "spec-runner-core: project.json naming.other_work_prefixes が空です"
73
+ resolve_step() {
74
+ local sid="$1"
75
+ step_id="$sid"
76
+ command=$(jq -r --arg id "$sid" '.steps[]? | select(.id==$id) | .name_ja' "$STEPS_JSON")
77
+ local md
78
+ md=$(jq -r --arg id "$sid" '.steps[]? | select(.id==$id) | .md_file' "$STEPS_JSON")
79
+ [[ -n "$command" && "$command" != "null" ]] || die "spec-runner-core: steps.json に id=$sid の name_ja がありません"
80
+ [[ -n "$md" && "$md" != "null" ]] || die "spec-runner-core: steps.json に id=$sid の md_file がありません"
81
+ command_file="$STEPS_DIR/$md"
82
+ step_commands=$(jq -c --arg id "$sid" '.steps[]? | select(.id==$id) | .commands' "$STEPS_JSON")
83
+ check_command=$(jq -r '.common.commands.check' "$STEPS_JSON")
84
+ }
104
85
 
105
- test_dir=$(jq -r '.test_design.dir' "$PROJECT_JSON")
106
- [[ -n "$test_dir" && "$test_dir" != "null" ]] || die "spec-runner-core: project.json test_design.dir が未設定です"
107
- test_pattern=$(jq -r '.test_design.pattern' "$PROJECT_JSON")
108
- [[ -n "$test_pattern" && "$test_pattern" != "null" ]] || die "spec-runner-core: project.json test_design.pattern が未設定です"
109
- require_uc_prefixed_tests=1
110
- rq="$(jq -r '.test_design.require_uc_prefixed_tests // true' "$PROJECT_JSON")"
111
- [[ "$rq" == "false" || "$rq" == "0" ]] && require_uc_prefixed_tests=0
86
+ latest_uc_spec() {
87
+ find docs/02_ユースケース仕様 -mindepth 2 -maxdepth 2 -type f -name "UC-*.md" 2>/dev/null | sort -V | tail -1
88
+ }
112
89
 
113
- if [[ "$branch" =~ ^${branch_prefix}/(${uc_id_re})- ]]; then
114
- on_uc_branch=1
115
- current_uc_id="${BASH_REMATCH[1]}"
116
- elif [[ "$branch" =~ ^${branch_prefix}/(${other_work_pattern})/ ]]; then
117
- on_other_work_branch=1
118
- fi
90
+ latest_unreviewed_uc_spec() {
91
+ local f key
92
+ while IFS= read -r f; do
93
+ key="$(basename "$f" .md)"
94
+ jq -e --arg u "$key" '.uc_reviewed[]? == $u' "$LOCK_FILE" >/dev/null 2>&1 && continue
95
+ echo "$f"
96
+ return 0
97
+ done < <(find docs/02_ユースケース仕様 -mindepth 2 -maxdepth 2 -type f -name "UC-*.md" 2>/dev/null | sort -V)
98
+ return 1
119
99
  }
120
- load_state
121
100
 
122
- # UC ブランチで「実装に進める」ためのテスト存在判定(TDD: 当該 UC 用 spec を先に書かせる)
123
- uc_branch_has_tests_ready_for_implement() {
101
+ uc_has_tests_ready_for_implement() {
102
+ local uc_key="$1"
103
+ local uc_id=""
124
104
  [[ -d "$test_dir" ]] || return 1
125
105
  if [[ $require_uc_prefixed_tests -eq 0 ]]; then
126
106
  [[ -n "$(find "$test_dir" -type f -name "$test_pattern" 2>/dev/null | head -1)" ]]
127
107
  return $?
128
108
  fi
129
- [[ -n "$current_uc_id" ]] || return 1
109
+ [[ "$uc_key" =~ ^(UC-[0-9]+)- ]] && uc_id="${BASH_REMATCH[1]}"
110
+ [[ -n "$uc_id" ]] || return 1
130
111
  local f bn
131
112
  while IFS= read -r f; do
132
- [[ -z "$f" ]] && continue
133
113
  bn=$(basename "$f")
134
- [[ "$bn" == "${current_uc_id}-"* ]] || continue
114
+ [[ "$bn" == "${uc_id}-"* ]] || continue
135
115
  [[ "$bn" == $test_pattern ]] || continue
136
116
  return 0
137
117
  done < <(find "$test_dir" -type f 2>/dev/null)
138
118
  return 1
139
119
  }
140
120
 
141
- # ============================================================
142
- # 4) ゲート関連(小関数)
143
- # ============================================================
144
- gate_error() { echo "GATE: $1" >&2; exit 1; }
145
-
146
- resolve_steps_token() {
147
- local p="$1"
148
- [[ "$p" != steps:* ]] && { echo "$p"; return 0; }
149
- local token keypart suffix base
150
- token="${p#steps:}"
151
- keypart="${token%%/*}"
152
- suffix=""
153
- [[ "$token" == *"/"* ]] && suffix="/${token#*/}"
154
- base="$(jq -r --arg k "$keypart" '.common.docs[$k] // empty' "$STEPS_JSON")"
155
- [[ -n "$base" && "$base" != "null" ]] || gate_error "steps.json に common.docs.$keypart がありません(required_docs の $p を解決できません)"
156
- echo "${base}${suffix}"
157
- }
158
-
159
- get_required_docs_list() {
160
- local key="$1"
161
- jq -r --arg k "$key" '.required_docs[$k][]?' "$PROJECT_JSON"
162
- }
163
-
164
- assert_paths_exist() {
165
- local paths="$1"
166
- while IFS= read -r p; do
167
- [[ -z "$p" || "$p" == "null" ]] && continue
168
- p="$(resolve_steps_token "$p")"
169
- if [[ -f "$p" ]]; then
170
- :
171
- elif [[ -d "$p" ]]; then
172
- count=$(find "$p" -maxdepth 1 -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
173
- [[ "${count:-0}" -ge 1 ]] || gate_error "必須: $p に 1 件以上の .md がありません"
121
+ run_status() {
122
+ echo "=== spec-runner フェーズ状況 ==="
123
+ echo "Lock(.spec-runner/phase-locks.json):"
124
+ for sec in charter domain architecture uc_discovery test_design; do
125
+ if jq -e --arg s "$sec" '.[$s].completed == true' "$LOCK_FILE" >/dev/null 2>&1; then
126
+ echo " ✓ $sec"
174
127
  else
175
- gate_error "必須: $p が存在しません"
128
+ echo " - $sec"
176
129
  fi
177
- done <<< "$paths"
178
- }
179
-
180
- gate_charter() {
181
- local list
182
- list="$(get_required_docs_list "charter")"
183
- [[ -n "$list" ]] || gate_error "project.json の required_docs.charter が未設定です"
184
- assert_paths_exist "$list"
185
- if [[ "$1" == "LOOP1" ]]; then
186
- jq -e '.charter.completed == true' "$LOCK_FILE" >/dev/null 2>&1 || gate_error "Phase 0 未完了(phase-locks.json の charter.completed)"
187
- jq -e '.charter.reviewed_by' "$LOCK_FILE" >/dev/null 2>&1 || gate_error "憲章に署名がありません(charter.reviewed_by)"
188
- echo "Phase 0: OK"
189
- fi
190
- }
191
-
192
- gate_domain_and_arch() {
193
- local grd="$1"
194
- [[ "$grd" == "LOOP1" ]] && return 0
195
-
196
- jq -e '.domain.completed == true' "$LOCK_FILE" >/dev/null 2>&1 || gate_error "Phase 1 未完了"
197
- list="$(get_required_docs_list "domain")"
198
- [[ -n "$list" ]] || gate_error "project.json の required_docs.domain が未設定です"
199
- assert_paths_exist "$list"
200
- echo "Phase 1: OK"
201
-
202
- jq -e '.architecture.completed == true' "$LOCK_FILE" >/dev/null 2>&1 || gate_error "Phase 2 未完了"
203
- list="$(get_required_docs_list "architecture")"
204
- [[ -n "$list" ]] || gate_error "project.json の required_docs.architecture が未設定です"
205
- assert_paths_exist "$list"
206
- echo "Phase 2: OK"
207
- }
208
-
209
- gate_infra_grade_a() {
210
- [[ "$1" != "A" ]] && return 0
211
- jq -e '.infra.completed == true' "$LOCK_FILE" >/dev/null 2>&1 || gate_error "Grade A: インフラ設計未完了"
212
- list="$(get_required_docs_list "grade_a")"
213
- [[ -n "$list" ]] || gate_error "project.json の required_docs.grade_a が未設定です(Grade A 時必須)"
214
- assert_paths_exist "$list"
215
- echo "Phase 4 (Grade A): OK"
216
- }
217
-
218
- gate_uc_openapi_and_tests() {
219
- local grd="$1"
220
- [[ "$grd" != "A" && "$grd" != "B" ]] && return 0
221
-
222
- uc_count=$(find docs/02_ユースケース仕様 -mindepth 2 -maxdepth 2 -name "UC-*.md" 2>/dev/null | wc -l)
223
- if [[ "${uc_count:-0}" -gt 0 ]]; then
224
- uc_reviewed_count=$(jq '.uc_reviewed | length' "$LOCK_FILE")
225
- [[ "${uc_reviewed_count:-0}" -gt 0 ]] || gate_error "Gate 3: uc_reviewed に少なくとも1件の UC 識別子を登録してください"
226
- list="$(get_required_docs_list "gate3_openapi")"
227
- [[ -n "$list" ]] || gate_error "project.json の required_docs.gate3_openapi が未設定です"
228
- assert_paths_exist "$list"
229
- echo "Gate 3 (UC+OpenAPI): OK"
230
- fi
231
-
232
- test_design_ok=0
233
- jq -e '.test_design.completed == true' "$LOCK_FILE" >/dev/null 2>&1 && test_design_ok=1
234
- if [[ $test_design_ok -eq 1 ]] || [[ -d "$test_dir" && -n "$(find "$test_dir" -type f -name "$test_pattern" 2>/dev/null | head -1)" ]]; then
235
- echo "Gate 5 (テスト設計): OK"
236
- fi
237
- }
238
-
239
- gate_tests_green_soft() {
240
- local grd="$1"
241
- [[ "$grd" != "A" && "$grd" != "B" && "$grd" != "C" ]] && return 0
242
-
243
- if [[ -f ".spec-runner/scripts/test/require-tests-green.sh" ]]; then
244
- .spec-runner/scripts/test/require-tests-green.sh 2>/dev/null && echo "Gate 6 (テスト通過): OK" || true
245
- fi
246
- }
247
-
248
- # === ゲート確認モード ===
249
- run_gate() {
250
- GRD="${GATE_GRADE:-$grade}"
251
-
252
- gate_charter "$GRD"
253
- gate_domain_and_arch "$GRD"
254
- gate_infra_grade_a "$GRD"
255
- gate_uc_openapi_and_tests "$GRD"
256
- gate_tests_green_soft "$GRD"
257
-
258
- echo "ゲート確認: 通過"
130
+ done
259
131
  }
260
132
 
261
- # === フェーズ判定モード ===
262
133
  run_phase() {
263
134
  phase=0
264
135
  phase_name_ja=""
@@ -273,49 +144,13 @@ run_phase() {
273
144
  domain_root="$(get_steps_common_doc "domain_root")"
274
145
  architecture_root="$(get_steps_common_doc "architecture_root")"
275
146
 
147
+ doc_key() { basename "$1" .md; }
276
148
  first_md_in_dir() {
277
149
  local d="$1"
278
150
  [[ -d "$d" ]] || return 1
279
151
  find "$d" -type f -name "*.md" 2>/dev/null | sort | head -1
280
152
  }
281
153
 
282
- doc_key() {
283
- local f="$1"
284
- basename "$f" .md
285
- }
286
-
287
- quality_done() {
288
- local kind="$1" # clarified | analyzed
289
- local scope="$2" # charter | domain | architecture | uc
290
- local key="$3"
291
- # 互換性のため、quality には「ベース名」と「.md 付き」の両方を許容する
292
- local key_md="${key}.md"
293
- jq -e --arg k "$key" --arg km "$key_md" --arg s "$scope" \
294
- ".quality.${kind}[\$s][]? | select(. == \$k or . == \$km)" \
295
- "$LOCK_FILE" >/dev/null 2>&1
296
- }
297
-
298
- resolve_step() {
299
- local sid="$1"
300
- step_id="$sid"
301
- command=$(jq -r --arg id "$sid" '.steps[]? | select(.id==$id) | .name_ja' "$STEPS_JSON")
302
- local md
303
- md=$(jq -r --arg id "$sid" '.steps[]? | select(.id==$id) | .md_file' "$STEPS_JSON")
304
- [[ -n "$command" && "$command" != "null" ]] || die "spec-runner-core: steps.json に id=$sid の name_ja がありません"
305
- [[ -n "$md" && "$md" != "null" ]] || die "spec-runner-core: steps.json に id=$sid の md_file がありません"
306
- command_file="$STEPS_DIR/$md"
307
- step_commands=$(jq -c --arg id "$sid" '.steps[]? | select(.id==$id) | .commands' "$STEPS_JSON")
308
- [[ -n "$step_commands" && "$step_commands" != "null" ]] || die "spec-runner-core: steps.json に id=$sid の commands がありません"
309
- check_command=$(jq -r '.common.commands.check' "$STEPS_JSON")
310
- [[ -n "$check_command" && "$check_command" != "null" ]] || die "spec-runner-core: steps.json に common.commands.check がありません"
311
- }
312
-
313
- if [[ $on_uc_branch -eq 1 ]] && [[ -n "$current_uc_id" ]]; then
314
- for f in docs/02_ユースケース仕様/*/"${current_uc_id}-"*.md; do
315
- [[ -f "$f" ]] && feature_spec="$f" && feature_dir="$(dirname "$f")" && break
316
- done
317
- fi
318
-
319
154
  uc_count_total=$(find docs/02_ユースケース仕様 -mindepth 2 -maxdepth 2 -name "UC-*.md" 2>/dev/null | wc -l | tr -d ' ')
320
155
  uc_count_total=${uc_count_total:-0}
321
156
 
@@ -334,165 +169,111 @@ run_phase() {
334
169
  else
335
170
  phase=0; phase_name_ja="憲章策定"; resolve_step "charter"
336
171
  fi
337
- elif [[ $has_domain_lock -eq 0 && $on_uc_branch -eq 0 ]]; then
338
- # UC を洗い出している途中(uc_discovery.completed=false)の間はドメインへ進まない
339
- if [[ $uc_discovery_completed -eq 0 ]]; then
172
+ elif [[ $uc_discovery_completed -eq 0 ]]; then
173
+ uc_spec="$(latest_unreviewed_uc_spec || true)"
174
+ if [[ -n "$uc_spec" ]]; then
175
+ feature_spec="$uc_spec"
176
+ feature_dir="$(dirname "$uc_spec")"
177
+ uc_key="$(doc_key "$uc_spec")"
178
+ if ! quality_done "clarified" "uc" "$uc_key"; then
179
+ phase=1; phase_name_ja="ユースケース洗い出し中(曖昧さ解消)"; resolve_step "clarify"
180
+ elif ! quality_done "analyzed" "uc" "$uc_key"; then
181
+ phase=1; phase_name_ja="ユースケース洗い出し中(分析)"; resolve_step "analyze"
182
+ else
183
+ phase=1; phase_name_ja="ユースケース洗い出し中(レビュー通過待ち)"; resolve_step "clarify"
184
+ fi
185
+ else
340
186
  phase=1; phase_name_ja="ユースケース洗い出し中(次UC作成)"; resolve_step "uc_spec"
187
+ fi
188
+ elif [[ $has_domain_lock -eq 0 ]]; then
189
+ uc_spec="$(latest_unreviewed_uc_spec || true)"
190
+ if [[ -n "$uc_spec" ]]; then
191
+ feature_spec="$uc_spec"
192
+ feature_dir="$(dirname "$uc_spec")"
193
+ uc_key="$(doc_key "$uc_spec")"
194
+ if ! quality_done "clarified" "uc" "$uc_key"; then
195
+ phase=1; phase_name_ja="ユースケース仕様(曖昧さ解消)"; resolve_step "clarify"
196
+ elif ! quality_done "analyzed" "uc" "$uc_key"; then
197
+ phase=1; phase_name_ja="ユースケース仕様(分析)"; resolve_step "analyze"
198
+ else
199
+ phase=1; phase_name_ja="ユースケース仕様(レビュー通過まで)"; resolve_step "clarify"
200
+ fi
341
201
  else
342
- # UC 1 件以上ある場合のみ、ドメイン側の質フローを回す
343
- if [[ ${uc_count_total} -gt 0 ]]; then
344
- domain_spec="$(first_md_in_dir "$domain_root" || true)"
345
- if [[ -n "$domain_spec" ]]; then
346
- feature_spec="$domain_spec"
347
- feature_dir="$(dirname "$domain_spec")"
348
- dkey="$(doc_key "$domain_spec")"
349
- if ! quality_done "clarified" "domain" "$dkey"; then
350
- phase=2; phase_name_ja="ドメイン設計(曖昧さ解消)"; resolve_step "clarify"
351
- elif ! quality_done "analyzed" "domain" "$dkey"; then
352
- phase=2; phase_name_ja="ドメイン設計(分析)"; resolve_step "analyze"
353
- else
354
- phase=2; phase_name_ja="ドメイン設計"; resolve_step "domain"
355
- fi
202
+ domain_spec="$(first_md_in_dir "$domain_root" || true)"
203
+ if [[ -n "$domain_spec" ]]; then
204
+ feature_spec="$domain_spec"
205
+ feature_dir="$(dirname "$domain_spec")"
206
+ dkey="$(doc_key "$domain_spec")"
207
+ if ! quality_done "clarified" "domain" "$dkey"; then
208
+ phase=2; phase_name_ja="ドメイン設計(曖昧さ解消)"; resolve_step "clarify"
209
+ elif ! quality_done "analyzed" "domain" "$dkey"; then
210
+ phase=2; phase_name_ja="ドメイン設計(分析)"; resolve_step "analyze"
356
211
  else
357
212
  phase=2; phase_name_ja="ドメイン設計"; resolve_step "domain"
358
213
  fi
359
214
  else
360
- phase=1; phase_name_ja="ユースケース洗い出し中(次UC作成)"; resolve_step "uc_spec"
215
+ phase=2; phase_name_ja="ドメイン設計"; resolve_step "domain"
361
216
  fi
362
217
  fi
363
- elif [[ $has_arch_lock -eq 0 && $has_domain_lock -eq 1 ]]; then
218
+ elif [[ $has_arch_lock -eq 0 ]]; then
364
219
  arch_spec="$(first_md_in_dir "$architecture_root" || true)"
365
220
  if [[ -n "$arch_spec" ]]; then
366
221
  feature_spec="$arch_spec"
367
222
  feature_dir="$(dirname "$arch_spec")"
368
223
  akey="$(doc_key "$arch_spec")"
369
224
  if ! quality_done "clarified" "architecture" "$akey"; then
370
- phase=3; phase_name_ja="アーキテクチャ選択(曖昧さ解消)"; resolve_step "clarify"
225
+ phase=3; phase_name_ja="実装計画(曖昧さ解消)"; resolve_step "clarify"
371
226
  elif ! quality_done "analyzed" "architecture" "$akey"; then
372
- phase=3; phase_name_ja="アーキテクチャ選択(分析)"; resolve_step "analyze"
227
+ phase=3; phase_name_ja="実装計画(分析)"; resolve_step "analyze"
373
228
  else
374
- phase=3; phase_name_ja="アーキテクチャ選択"; resolve_step "architecture_plan"
229
+ phase=3; phase_name_ja="実装計画"; resolve_step "architecture_plan"
375
230
  fi
376
231
  else
377
- phase=3; phase_name_ja="アーキテクチャ選択"; resolve_step "architecture_plan"
378
- fi
379
- elif [[ $on_uc_branch -eq 1 ]]; then
380
- uc_spec=""
381
- if [[ -n "$current_uc_id" ]]; then
382
- for f in docs/02_ユースケース仕様/*/"${current_uc_id}-"*.md; do [[ -f "$f" ]] && uc_spec="$f" && break; done
232
+ phase=3; phase_name_ja="実装計画"; resolve_step "architecture_plan"
383
233
  fi
234
+ else
235
+ uc_spec="$(latest_uc_spec || true)"
384
236
  if [[ -z "$uc_spec" ]]; then
385
237
  phase=1; phase_name_ja="ユースケース仕様"; resolve_step "uc_spec"
386
238
  else
387
239
  feature_spec="$uc_spec"
388
240
  feature_dir="$(dirname "$uc_spec")"
389
- uc_dir=$(basename "$uc_spec" .md)
241
+ uc_key="$(doc_key "$uc_spec")"
390
242
  reviewed=0
391
- jq -e --arg u "$uc_dir" '.uc_reviewed[]? == $u' "$LOCK_FILE" 2>/dev/null | grep -q true && reviewed=1
243
+ jq -e --arg u "$uc_key" '.uc_reviewed[]? == $u' "$LOCK_FILE" 2>/dev/null | grep -q true && reviewed=1
392
244
  if [[ $reviewed -eq 0 ]]; then
393
- if ! quality_done "clarified" "uc" "$uc_dir"; then
245
+ if ! quality_done "clarified" "uc" "$uc_key"; then
394
246
  phase=1; phase_name_ja="ユースケース仕様(曖昧さ解消)"; resolve_step "clarify"
395
- elif ! quality_done "analyzed" "uc" "$uc_dir"; then
247
+ elif ! quality_done "analyzed" "uc" "$uc_key"; then
396
248
  phase=1; phase_name_ja="ユースケース仕様(分析)"; resolve_step "analyze"
397
249
  else
398
250
  phase=1; phase_name_ja="ユースケース仕様(レビュー通過まで)"; resolve_step "clarify"
399
251
  fi
400
252
  else
401
- # UC 洗い出し中は、レビュー済みでも次の UC 作成へ戻す(TDD/実装に進まない)
402
- if [[ $uc_discovery_completed -eq 0 ]]; then
403
- phase=1; phase_name_ja="ユースケース洗い出し中(次UC作成)"; resolve_step "uc_spec"
404
- elif [[ "$grade" == "A" ]] && [[ $has_infra_lock -eq 0 ]]; then
405
- phase=4; phase_name_ja="インフラ詳細設計"; resolve_step "infra_plan"
253
+ if uc_has_tests_ready_for_implement "$uc_key"; then
254
+ phase=6; phase_name_ja="実装"; resolve_step "implement"
406
255
  else
407
- if uc_branch_has_tests_ready_for_implement; then
408
- phase=6; phase_name_ja="実装"; resolve_step "implement"
409
- else
410
- phase=5; phase_name_ja="テスト設計(当該 UC の spec 必須)"; resolve_step "test_design"
411
- fi
256
+ phase=5; phase_name_ja="テスト設計"; resolve_step "test_design"
412
257
  fi
413
258
  fi
414
259
  fi
415
- else
416
- if [[ $on_other_work_branch -eq 1 ]]; then
417
- phase=1; phase_name_ja="その他作業(CI/CD・インフラ等)"; resolve_step "other_work"
418
- elif [[ $has_domain_lock -eq 0 ]]; then
419
- phase=1; phase_name_ja="ユースケース開始(仕様策定)"; resolve_step "uc_spec"
420
- elif [[ $has_arch_lock -eq 0 ]]; then
421
- phase=3; phase_name_ja="アーキテクチャ選択"; resolve_step "architecture_plan"
422
- else
423
- phase=1; phase_name_ja="ユースケース開始(仕様策定)"; resolve_step "uc_spec"
424
- fi
425
260
  fi
426
261
 
427
262
  if [[ "$JSON_MODE" == true ]]; then
428
- jq -cn --argjson phase "$phase" --arg name "$phase_name_ja" --arg step_id "$step_id" --arg cmd "$command" --arg file "$command_file" --arg grade "$grade" \
263
+ jq -cn --argjson phase "$phase" --arg name "$phase_name_ja" --arg step_id "$step_id" --arg cmd "$command" --arg file "$command_file" \
429
264
  --arg check "$check_command" --argjson cmds "$step_commands" \
430
265
  --arg feature_dir "$feature_dir" --arg feature_spec "$feature_spec" \
431
- '{phase:$phase, phase_name_ja:$name, step_id:$step_id, command:$cmd, command_file:$file, grade:$grade, check_command:$check, step_commands:$cmds, feature_dir:$feature_dir, feature_spec:$feature_spec}'
266
+ '{phase:$phase, phase_name_ja:$name, step_id:$step_id, command:$cmd, command_file:$file, check_command:$check, step_commands:$cmds, feature_dir:$feature_dir, feature_spec:$feature_spec}'
432
267
  else
433
268
  echo "現在フェーズ: Phase $phase($phase_name_ja)"
434
269
  echo "推奨コマンド: $command"
435
270
  echo "コマンドファイル: $command_file"
436
- echo "グレード: $grade"
437
271
  echo "チェック(毎回): $check_command"
438
- if [[ $on_uc_branch -eq 0 ]] && [[ -n "$branch" ]]; then
439
- echo ""
440
- echo "注意: main 等のままの修正は危険です。UC 用ブランチを作成してから作業してください(仕様策定ステップでブランチ作成)。"
441
- fi
442
272
  fi
443
273
  }
444
274
 
445
- # === 状態表示(lock 一覧)===
446
- run_status() {
447
- echo "=== spec-runner フェーズ状況 ==="
448
- echo "グレード: $grade"
449
- echo ""
450
- echo "Lock(.spec-runner/phase-locks.json):"
451
- for sec in charter domain architecture infra uc_discovery test_design; do
452
- if jq -e --arg s "$sec" '.[$s].completed == true' "$LOCK_FILE" >/dev/null 2>&1; then
453
- echo " ✓ $sec"
454
- else
455
- echo " - $sec"
456
- fi
457
- done
458
- }
459
-
460
- # === グレード判定チェックリスト ===
461
- run_grade() {
462
- echo "=== spec-runner グレード判定 ==="
463
- echo ""
464
- echo "STEP 1: 作業の種別を判定する"
465
- echo " → 既存UCの修正・バグfix: Grade C 確定(以降の判定不要)"
466
- echo " → 新規UC: STEP 2 へ"
467
- echo ""
468
- echo "STEP 2: Grade B 仮置きで UC 草稿を作成する"
469
- echo " → AI と対話して UC 仕様書の草稿を作成する"
470
- echo " → この時点ではまだブランチ作成・コミット不要"
471
- echo ""
472
- echo "STEP 3: UC 草稿を見て Grade を確定する"
473
- echo " 以下のいずれかが草稿に含まれていれば Grade A:"
474
- echo " □ 新しいDB・テーブル・コレクションの追加が必要"
475
- echo " □ 外部API・SaaS との新規連携が必要"
476
- echo " □ 新規クラウドサービスの追加が必要(S3バケット・Redisクラスタ等)"
477
- echo " □ ネットワーク構成の変更が必要"
478
- echo " □ CI/CDパイプラインの変更が必要"
479
- echo " → 一つでも該当: Grade A 確定 → ブランチ作成 → Phase 3(UC仕様書)へ"
480
- echo " → 全て非該当: Grade B 確定 → ブランチ作成 → Phase 3(UC仕様書)へ(草稿を流用)"
481
- echo ""
482
- echo "判定結果は .spec-runner/grade-history.json に記録する(ブランチ名には含めない)。"
483
- echo "迷ったら上位グレードを選ぶ。"
484
- echo ""
485
- echo "現在の記録: current_grade = $grade"
486
- }
487
-
488
- # === 実行 ===
489
- if [[ "$MODE" == "gate" ]]; then
490
- echo "=== ゲート確認(グレード: ${GATE_GRADE:-$grade}) ==="
491
- run_gate
492
- elif [[ "$MODE" == "status" ]]; then
275
+ if [[ "$MODE" == "status" ]]; then
493
276
  run_status
494
- elif [[ "$MODE" == "grade" ]]; then
495
- run_grade
496
277
  else
497
278
  run_phase
498
279
  fi