spec-runner 1.0.24 → 1.1.2
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 -7
- package/bin/spec-runner.js +28 -23
- package/docs/flow.md +59 -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 +137 -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 +3 -3
- 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" 'any(.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=""
|
|
@@ -272,52 +143,24 @@ run_phase() {
|
|
|
272
143
|
charter_doc="$(get_steps_common_doc "charter")"
|
|
273
144
|
domain_root="$(get_steps_common_doc "domain_root")"
|
|
274
145
|
architecture_root="$(get_steps_common_doc "architecture_root")"
|
|
146
|
+
domain_spec_present=0
|
|
147
|
+
arch_spec_present=0
|
|
275
148
|
|
|
149
|
+
doc_key() { basename "$1" .md; }
|
|
276
150
|
first_md_in_dir() {
|
|
277
151
|
local d="$1"
|
|
278
152
|
[[ -d "$d" ]] || return 1
|
|
279
153
|
find "$d" -type f -name "*.md" 2>/dev/null | sort | head -1
|
|
280
154
|
}
|
|
281
155
|
|
|
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
156
|
uc_count_total=$(find docs/02_ユースケース仕様 -mindepth 2 -maxdepth 2 -name "UC-*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
320
157
|
uc_count_total=${uc_count_total:-0}
|
|
158
|
+
[[ -n "$(first_md_in_dir "$domain_root" || true)" ]] && domain_spec_present=1
|
|
159
|
+
[[ -n "$(first_md_in_dir "$architecture_root" || true)" ]] && arch_spec_present=1
|
|
160
|
+
|
|
161
|
+
# lock が先に立っていても、設計成果物が無い場合は必ず設計フェーズへ戻す
|
|
162
|
+
[[ $domain_spec_present -eq 0 ]] && has_domain_lock=0
|
|
163
|
+
[[ $arch_spec_present -eq 0 ]] && has_arch_lock=0
|
|
321
164
|
|
|
322
165
|
if [[ $has_charter_lock -eq 0 ]]; then
|
|
323
166
|
if [[ -f "$charter_doc" ]]; then
|
|
@@ -334,165 +177,111 @@ run_phase() {
|
|
|
334
177
|
else
|
|
335
178
|
phase=0; phase_name_ja="憲章策定"; resolve_step "charter"
|
|
336
179
|
fi
|
|
337
|
-
elif [[ $
|
|
338
|
-
|
|
339
|
-
if [[
|
|
180
|
+
elif [[ $uc_discovery_completed -eq 0 ]]; then
|
|
181
|
+
uc_spec="$(latest_unreviewed_uc_spec || true)"
|
|
182
|
+
if [[ -n "$uc_spec" ]]; then
|
|
183
|
+
feature_spec="$uc_spec"
|
|
184
|
+
feature_dir="$(dirname "$uc_spec")"
|
|
185
|
+
uc_key="$(doc_key "$uc_spec")"
|
|
186
|
+
if ! quality_done "clarified" "uc" "$uc_key"; then
|
|
187
|
+
phase=1; phase_name_ja="ユースケース洗い出し中(曖昧さ解消)"; resolve_step "clarify"
|
|
188
|
+
elif ! quality_done "analyzed" "uc" "$uc_key"; then
|
|
189
|
+
phase=1; phase_name_ja="ユースケース洗い出し中(分析)"; resolve_step "analyze"
|
|
190
|
+
else
|
|
191
|
+
phase=1; phase_name_ja="ユースケース洗い出し中(レビュー通過待ち)"; resolve_step "clarify"
|
|
192
|
+
fi
|
|
193
|
+
else
|
|
340
194
|
phase=1; phase_name_ja="ユースケース洗い出し中(次UC作成)"; resolve_step "uc_spec"
|
|
195
|
+
fi
|
|
196
|
+
elif [[ $has_domain_lock -eq 0 ]]; then
|
|
197
|
+
uc_spec="$(latest_unreviewed_uc_spec || true)"
|
|
198
|
+
if [[ -n "$uc_spec" ]]; then
|
|
199
|
+
feature_spec="$uc_spec"
|
|
200
|
+
feature_dir="$(dirname "$uc_spec")"
|
|
201
|
+
uc_key="$(doc_key "$uc_spec")"
|
|
202
|
+
if ! quality_done "clarified" "uc" "$uc_key"; then
|
|
203
|
+
phase=1; phase_name_ja="ユースケース仕様(曖昧さ解消)"; resolve_step "clarify"
|
|
204
|
+
elif ! quality_done "analyzed" "uc" "$uc_key"; then
|
|
205
|
+
phase=1; phase_name_ja="ユースケース仕様(分析)"; resolve_step "analyze"
|
|
206
|
+
else
|
|
207
|
+
phase=1; phase_name_ja="ユースケース仕様(レビュー通過まで)"; resolve_step "clarify"
|
|
208
|
+
fi
|
|
341
209
|
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
|
|
210
|
+
domain_spec="$(first_md_in_dir "$domain_root" || true)"
|
|
211
|
+
if [[ -n "$domain_spec" ]]; then
|
|
212
|
+
feature_spec="$domain_spec"
|
|
213
|
+
feature_dir="$(dirname "$domain_spec")"
|
|
214
|
+
dkey="$(doc_key "$domain_spec")"
|
|
215
|
+
if ! quality_done "clarified" "domain" "$dkey"; then
|
|
216
|
+
phase=2; phase_name_ja="ドメイン設計(曖昧さ解消)"; resolve_step "clarify"
|
|
217
|
+
elif ! quality_done "analyzed" "domain" "$dkey"; then
|
|
218
|
+
phase=2; phase_name_ja="ドメイン設計(分析)"; resolve_step "analyze"
|
|
356
219
|
else
|
|
357
220
|
phase=2; phase_name_ja="ドメイン設計"; resolve_step "domain"
|
|
358
221
|
fi
|
|
359
222
|
else
|
|
360
|
-
phase=
|
|
223
|
+
phase=2; phase_name_ja="ドメイン設計"; resolve_step "domain"
|
|
361
224
|
fi
|
|
362
225
|
fi
|
|
363
|
-
elif [[ $has_arch_lock -eq 0
|
|
226
|
+
elif [[ $has_arch_lock -eq 0 ]]; then
|
|
364
227
|
arch_spec="$(first_md_in_dir "$architecture_root" || true)"
|
|
365
228
|
if [[ -n "$arch_spec" ]]; then
|
|
366
229
|
feature_spec="$arch_spec"
|
|
367
230
|
feature_dir="$(dirname "$arch_spec")"
|
|
368
231
|
akey="$(doc_key "$arch_spec")"
|
|
369
232
|
if ! quality_done "clarified" "architecture" "$akey"; then
|
|
370
|
-
phase=3; phase_name_ja="
|
|
233
|
+
phase=3; phase_name_ja="実装計画(曖昧さ解消)"; resolve_step "clarify"
|
|
371
234
|
elif ! quality_done "analyzed" "architecture" "$akey"; then
|
|
372
|
-
phase=3; phase_name_ja="
|
|
235
|
+
phase=3; phase_name_ja="実装計画(分析)"; resolve_step "analyze"
|
|
373
236
|
else
|
|
374
|
-
phase=3; phase_name_ja="
|
|
237
|
+
phase=3; phase_name_ja="実装計画"; resolve_step "architecture_plan"
|
|
375
238
|
fi
|
|
376
239
|
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
|
|
240
|
+
phase=3; phase_name_ja="実装計画"; resolve_step "architecture_plan"
|
|
383
241
|
fi
|
|
242
|
+
else
|
|
243
|
+
uc_spec="$(latest_uc_spec || true)"
|
|
384
244
|
if [[ -z "$uc_spec" ]]; then
|
|
385
245
|
phase=1; phase_name_ja="ユースケース仕様"; resolve_step "uc_spec"
|
|
386
246
|
else
|
|
387
247
|
feature_spec="$uc_spec"
|
|
388
248
|
feature_dir="$(dirname "$uc_spec")"
|
|
389
|
-
|
|
249
|
+
uc_key="$(doc_key "$uc_spec")"
|
|
390
250
|
reviewed=0
|
|
391
|
-
jq -e --arg u "$
|
|
251
|
+
jq -e --arg u "$uc_key" 'any(.uc_reviewed[]?; . == $u)' "$LOCK_FILE" >/dev/null 2>&1 && reviewed=1
|
|
392
252
|
if [[ $reviewed -eq 0 ]]; then
|
|
393
|
-
if ! quality_done "clarified" "uc" "$
|
|
253
|
+
if ! quality_done "clarified" "uc" "$uc_key"; then
|
|
394
254
|
phase=1; phase_name_ja="ユースケース仕様(曖昧さ解消)"; resolve_step "clarify"
|
|
395
|
-
elif ! quality_done "analyzed" "uc" "$
|
|
255
|
+
elif ! quality_done "analyzed" "uc" "$uc_key"; then
|
|
396
256
|
phase=1; phase_name_ja="ユースケース仕様(分析)"; resolve_step "analyze"
|
|
397
257
|
else
|
|
398
258
|
phase=1; phase_name_ja="ユースケース仕様(レビュー通過まで)"; resolve_step "clarify"
|
|
399
259
|
fi
|
|
400
260
|
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"
|
|
261
|
+
if uc_has_tests_ready_for_implement "$uc_key"; then
|
|
262
|
+
phase=6; phase_name_ja="実装"; resolve_step "implement"
|
|
406
263
|
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
|
|
264
|
+
phase=5; phase_name_ja="テスト設計"; resolve_step "test_design"
|
|
412
265
|
fi
|
|
413
266
|
fi
|
|
414
267
|
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
268
|
fi
|
|
426
269
|
|
|
427
270
|
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"
|
|
271
|
+
jq -cn --argjson phase "$phase" --arg name "$phase_name_ja" --arg step_id "$step_id" --arg cmd "$command" --arg file "$command_file" \
|
|
429
272
|
--arg check "$check_command" --argjson cmds "$step_commands" \
|
|
430
273
|
--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,
|
|
274
|
+
'{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
275
|
else
|
|
433
276
|
echo "現在フェーズ: Phase $phase($phase_name_ja)"
|
|
434
277
|
echo "推奨コマンド: $command"
|
|
435
278
|
echo "コマンドファイル: $command_file"
|
|
436
|
-
echo "グレード: $grade"
|
|
437
279
|
echo "チェック(毎回): $check_command"
|
|
438
|
-
if [[ $on_uc_branch -eq 0 ]] && [[ -n "$branch" ]]; then
|
|
439
|
-
echo ""
|
|
440
|
-
echo "注意: main 等のままの修正は危険です。UC 用ブランチを作成してから作業してください(仕様策定ステップでブランチ作成)。"
|
|
441
|
-
fi
|
|
442
280
|
fi
|
|
443
281
|
}
|
|
444
282
|
|
|
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
|
|
283
|
+
if [[ "$MODE" == "status" ]]; then
|
|
493
284
|
run_status
|
|
494
|
-
elif [[ "$MODE" == "grade" ]]; then
|
|
495
|
-
run_grade
|
|
496
285
|
else
|
|
497
286
|
run_phase
|
|
498
287
|
fi
|