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.
- package/README.md +15 -6
- package/bin/spec-runner.js +28 -18
- package/docs/flow.md +58 -201
- package/package.json +1 -1
- package/templates/.spec-runner/project.json.example +1 -1
- package/templates/.spec-runner/scripts/check.sh +2 -38
- package/templates/.spec-runner/scripts/spec-runner-core.sh +129 -348
- package/templates/.spec-runner/scripts/uc-next-start.sh +154 -0
- package/templates/.spec-runner/spec-runner.sh +2 -3
- package/templates/.spec-runner/steps/steps.json +22 -97
- package/templates/.spec-runner/steps//343/203/206/343/202/271/343/203/210/350/250/255/350/250/210.md +10 -9
- 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
- package/templates/.spec-runner/steps//344/273/225/346/247/230/347/255/226/345/256/232.md +16 -14
- package/templates/.spec-runner/steps//345/210/206/346/236/220.md +2 -2
- package/templates/.spec-runner/steps//345/256/237/350/243/205.md +8 -7
- package/templates/.spec-runner/steps//345/256/237/350/243/205/350/250/210/347/224/273.md +10 -10
- package/templates/.spec-runner/steps//346/206/262/347/253/240.md +1 -1
- package/templates/.spec-runner/steps//346/233/226/346/230/247/343/201/225/350/247/243/346/266/210.md +1 -1
- package/templates/mkdocs-scaffold/docs/index.md +2 -2
- package/templates/skills/uc-k1-work-card-init/SKILL.md +76 -0
- package/templates/skills/uc-k2-pre-commit-check/SKILL.md +57 -0
- package/templates/skills/uc-k3-spec-impl-diff-review/SKILL.md +57 -0
- package/templates/spec-runner-command.md +4 -3
- package/templates/.spec-runner/hooks/pre-commit +0 -47
- package/templates/.spec-runner/hooks/pre-push +0 -9
- package/templates/.spec-runner/scripts/branch/uc-next-start.sh +0 -224
- package/templates/.spec-runner/scripts/docs-serve.sh +0 -21
- package/templates/.spec-runner/scripts/test/require-tests-green.sh +0 -27
- package/templates/.spec-runner/steps//343/201/235/343/201/256/344/273/226/344/275/234/346/245/255.md +0 -34
- package/templates/.spec-runner/steps//343/202/277/343/202/271/343/202/257/344/270/200/350/246/247.md +0 -95
- 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
- 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] | --
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
123
|
-
|
|
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
|
-
[[
|
|
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" == "${
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
128
|
+
echo " - $sec"
|
|
176
129
|
fi
|
|
177
|
-
done
|
|
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 [[ $
|
|
338
|
-
|
|
339
|
-
if [[
|
|
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
|
-
|
|
343
|
-
if [[
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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=
|
|
215
|
+
phase=2; phase_name_ja="ドメイン設計"; resolve_step "domain"
|
|
361
216
|
fi
|
|
362
217
|
fi
|
|
363
|
-
elif [[ $has_arch_lock -eq 0
|
|
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="
|
|
225
|
+
phase=3; phase_name_ja="実装計画(曖昧さ解消)"; resolve_step "clarify"
|
|
371
226
|
elif ! quality_done "analyzed" "architecture" "$akey"; then
|
|
372
|
-
phase=3; phase_name_ja="
|
|
227
|
+
phase=3; phase_name_ja="実装計画(分析)"; resolve_step "analyze"
|
|
373
228
|
else
|
|
374
|
-
phase=3; phase_name_ja="
|
|
229
|
+
phase=3; phase_name_ja="実装計画"; resolve_step "architecture_plan"
|
|
375
230
|
fi
|
|
376
231
|
else
|
|
377
|
-
phase=3; phase_name_ja="
|
|
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
|
-
|
|
241
|
+
uc_key="$(doc_key "$uc_spec")"
|
|
390
242
|
reviewed=0
|
|
391
|
-
jq -e --arg u "$
|
|
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" "$
|
|
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" "$
|
|
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
|
-
|
|
402
|
-
|
|
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
|
-
|
|
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"
|
|
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,
|
|
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
|
-
|
|
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
|