@walwal-harness/cli 5.0.6 → 5.0.7
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/commands/harness-team.md +18 -3
- package/package.json +1 -1
- package/scripts/harness-queue-manager.sh +88 -16
package/commands/harness-team.md
CHANGED
|
@@ -242,16 +242,31 @@ logev fail "{FEATURE_ID} FAIL #{ATTEMPT} — {사유요약}"
|
|
|
242
242
|
NEW_FEATURE=$(bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" dequeue {N} "$HARNESS_ROOT")
|
|
243
243
|
logev gen-start "$NEW_FEATURE auto-acquired"
|
|
244
244
|
```
|
|
245
|
-
3. `ready=0`이면 →
|
|
245
|
+
3. `ready=0`이면 → next-sprint 시도:
|
|
246
|
+
```bash
|
|
247
|
+
NEXT=$(bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" next-sprint "$HARNESS_ROOT" 2>&1)
|
|
248
|
+
if echo "$NEXT" | grep -q "Advancing"; then
|
|
249
|
+
NEW_FEATURE=$(bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" dequeue {N} "$HARNESS_ROOT")
|
|
250
|
+
logev gen-start "$NEW_FEATURE auto-acquired (next sprint)"
|
|
251
|
+
else
|
|
252
|
+
logev result "Team-{N} 완료 — 모든 Sprint 처리됨"
|
|
253
|
+
fi
|
|
254
|
+
```
|
|
246
255
|
```
|
|
247
256
|
|
|
248
|
-
### Step 5: 결과 수집 및 다음 라운드
|
|
257
|
+
### Step 5: 결과 수집 및 다음 라운드 (자동 Sprint 전환 포함)
|
|
249
258
|
|
|
250
259
|
모든 Team Agent가 완료되면:
|
|
251
260
|
1. 각 Agent 반환 메시지에서 PASS/FAIL 확인
|
|
252
261
|
2. Queue 상태 재확인: `bash scripts/harness-queue-manager.sh status .`
|
|
253
262
|
3. ready 큐에 새로 unblock된 feature가 있으면 → **Step 3으로 돌아가서** 추가 팀 생성
|
|
254
|
-
4. ready=0, in_progress=0이면 →
|
|
263
|
+
4. ready=0, in_progress=0이면 → **자동 Sprint 전환 시도**:
|
|
264
|
+
```bash
|
|
265
|
+
bash scripts/harness-queue-manager.sh next-sprint .
|
|
266
|
+
```
|
|
267
|
+
- `"Advancing to Sprint N"` → 새 Sprint 피처가 queue에 로드됨 → **Step 3으로 돌아가서** 팀 생성
|
|
268
|
+
- `"ALL SPRINTS COMPLETE"` → 전체 프로젝트 완료 보고
|
|
269
|
+
- `"Cannot advance: N failed"` → 실패 피처 해결 필요, 사용자 개입 요청
|
|
255
270
|
|
|
256
271
|
## 핵심 원칙
|
|
257
272
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.7",
|
|
4
4
|
"description": "Production harness for AI agent engineering — Solo/Team mode, Planner, Generator(BE/FE), Evaluator(Func/Visual), optional Brainstormer. Supports React, Next.js, and Flutter FE stacks.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"walwal-harness": "bin/init.js"
|
|
@@ -65,14 +65,18 @@ release_queue_lock() {
|
|
|
65
65
|
# ── Concurrency from config ──
|
|
66
66
|
CONCURRENCY=3
|
|
67
67
|
if [ -f "$CONFIG" ]; then
|
|
68
|
-
_c=$(jq -r '.flow.parallel.concurrency // 3' "$CONFIG" 2>/dev/null)
|
|
68
|
+
_c=$(jq -r '.flow.team.concurrency // .flow.parallel.concurrency // 3' "$CONFIG" 2>/dev/null)
|
|
69
69
|
if [ "$_c" -gt 0 ] 2>/dev/null; then CONCURRENCY=$_c; fi
|
|
70
70
|
fi
|
|
71
71
|
|
|
72
72
|
# ══════════════════════════════════════════
|
|
73
73
|
# init — Build queue from feature-list.json
|
|
74
|
+
# Usage: init [sprint_number]
|
|
75
|
+
# sprint_number: optional, filter features by sprint (default: all)
|
|
74
76
|
# ══════════════════════════════════════════
|
|
75
77
|
cmd_init() {
|
|
78
|
+
local sprint_filter="${1:-all}"
|
|
79
|
+
|
|
76
80
|
if [ ! -f "$FEATURES" ]; then
|
|
77
81
|
echo "[queue] feature-list.json not found."
|
|
78
82
|
exit 1
|
|
@@ -80,7 +84,7 @@ cmd_init() {
|
|
|
80
84
|
|
|
81
85
|
# Build dependency graph and topological sort
|
|
82
86
|
# Output: feature-queue.json with ready (no deps) and blocked (has deps)
|
|
83
|
-
jq --argjson concurrency "$CONCURRENCY" '
|
|
87
|
+
jq --argjson concurrency "$CONCURRENCY" --arg sprint "$sprint_filter" '
|
|
84
88
|
# Build passed set (features already passed by evaluator)
|
|
85
89
|
def passed_set:
|
|
86
90
|
[.features[] | select(
|
|
@@ -88,9 +92,15 @@ cmd_init() {
|
|
|
88
92
|
((.passes // []) | any(. == "evaluator-functional"))
|
|
89
93
|
) | .id] ;
|
|
90
94
|
|
|
95
|
+
# Filter features by sprint if specified
|
|
96
|
+
def target_features:
|
|
97
|
+
if $sprint == "all" then .features
|
|
98
|
+
else [.features[] | select((.sprint // 1) == ($sprint | tonumber))]
|
|
99
|
+
end ;
|
|
100
|
+
|
|
91
101
|
# Separate ready vs blocked
|
|
92
|
-
def classify(passed):
|
|
93
|
-
reduce
|
|
102
|
+
def classify(features; passed):
|
|
103
|
+
reduce features[] as $f (
|
|
94
104
|
{ ready: [], blocked: {} };
|
|
95
105
|
($f.depends_on // []) as $deps |
|
|
96
106
|
if ($f.id | IN(passed[])) then . # already passed, skip
|
|
@@ -104,10 +114,12 @@ cmd_init() {
|
|
|
104
114
|
) ;
|
|
105
115
|
|
|
106
116
|
passed_set as $passed |
|
|
107
|
-
|
|
117
|
+
target_features as $targets |
|
|
118
|
+
classify($targets; $passed) as $classified |
|
|
108
119
|
{
|
|
109
|
-
version: "
|
|
120
|
+
version: "5.0",
|
|
110
121
|
concurrency: $concurrency,
|
|
122
|
+
current_sprint: (if $sprint == "all" then null else ($sprint | tonumber) end),
|
|
111
123
|
queue: {
|
|
112
124
|
ready: $classified.ready,
|
|
113
125
|
blocked: $classified.blocked,
|
|
@@ -129,10 +141,69 @@ cmd_init() {
|
|
|
129
141
|
blocked_count=$(jq '.queue.blocked | length' "$QUEUE")
|
|
130
142
|
passed_count=$(jq '.queue.passed | length' "$QUEUE")
|
|
131
143
|
|
|
132
|
-
echo "[queue] Initialized: $ready_count ready, $blocked_count blocked, $passed_count already passed"
|
|
144
|
+
echo "[queue] Initialized (sprint=$sprint_filter): $ready_count ready, $blocked_count blocked, $passed_count already passed"
|
|
133
145
|
echo "[queue] Concurrency: $CONCURRENCY teams"
|
|
134
146
|
}
|
|
135
147
|
|
|
148
|
+
# ══════════════════════════════════════════
|
|
149
|
+
# next-sprint — Auto-advance to next sprint
|
|
150
|
+
# Checks if current sprint is complete, loads next sprint features
|
|
151
|
+
# ══════════════════════════════════════════
|
|
152
|
+
cmd_next_sprint() {
|
|
153
|
+
if [ ! -f "$QUEUE" ]; then echo "[queue] Run 'init' first."; exit 1; fi
|
|
154
|
+
if [ ! -f "$FEATURES" ]; then echo "[queue] feature-list.json not found."; exit 1; fi
|
|
155
|
+
|
|
156
|
+
acquire_queue_lock
|
|
157
|
+
|
|
158
|
+
local ready in_prog failed
|
|
159
|
+
ready=$(jq '.queue.ready | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
160
|
+
in_prog=$(jq '.queue.in_progress | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
161
|
+
failed=$(jq '.queue.failed | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
162
|
+
|
|
163
|
+
# Current sprint still has work
|
|
164
|
+
if [ "$ready" -gt 0 ] || [ "$in_prog" -gt 0 ]; then
|
|
165
|
+
release_queue_lock
|
|
166
|
+
echo "[queue] Current sprint still active: $ready ready, $in_prog in progress"
|
|
167
|
+
return 1
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# Failed features block advancement
|
|
171
|
+
if [ "$failed" -gt 0 ]; then
|
|
172
|
+
release_queue_lock
|
|
173
|
+
echo "[queue] Cannot advance: $failed failed features. Requeue or fix them first."
|
|
174
|
+
return 1
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# Find current sprint number
|
|
178
|
+
local current_sprint
|
|
179
|
+
current_sprint=$(jq -r '.current_sprint // 0' "$QUEUE" 2>/dev/null)
|
|
180
|
+
if [ "$current_sprint" = "null" ] || [ "$current_sprint" = "0" ]; then
|
|
181
|
+
# Detect from passed features
|
|
182
|
+
current_sprint=$(jq -r --slurpfile q "$QUEUE" '
|
|
183
|
+
[.features[] | select(.id as $fid | $q[0].queue.passed | index($fid)) | .sprint // 1] | max // 1
|
|
184
|
+
' "$FEATURES" 2>/dev/null)
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
local next_sprint=$((current_sprint + 1))
|
|
188
|
+
|
|
189
|
+
# Check if next sprint features exist
|
|
190
|
+
local next_count
|
|
191
|
+
next_count=$(jq --arg s "$next_sprint" '[.features[] | select((.sprint // 1) == ($s | tonumber))] | length' "$FEATURES" 2>/dev/null)
|
|
192
|
+
|
|
193
|
+
if [ "$next_count" -eq 0 ]; then
|
|
194
|
+
release_queue_lock
|
|
195
|
+
echo "[queue] ALL SPRINTS COMPLETE. No Sprint $next_sprint features found."
|
|
196
|
+
echo "[queue] Total passed: $(jq '.queue.passed | length' "$QUEUE")"
|
|
197
|
+
return 0
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
release_queue_lock
|
|
201
|
+
|
|
202
|
+
# Re-init with next sprint (preserves passed from previous sprints)
|
|
203
|
+
echo "[queue] Sprint $current_sprint complete! Advancing to Sprint $next_sprint ($next_count features)"
|
|
204
|
+
cmd_init "$next_sprint"
|
|
205
|
+
}
|
|
206
|
+
|
|
136
207
|
# ══════════════════════════════════════════
|
|
137
208
|
# dequeue — Assign next ready feature to team
|
|
138
209
|
# ══════════════════════════════════════════
|
|
@@ -355,16 +426,17 @@ cmd_recover() {
|
|
|
355
426
|
|
|
356
427
|
# ── Dispatch ──
|
|
357
428
|
case "$CMD" in
|
|
358
|
-
init)
|
|
359
|
-
dequeue)
|
|
360
|
-
pass)
|
|
361
|
-
fail)
|
|
362
|
-
requeue)
|
|
363
|
-
update_phase)
|
|
364
|
-
recover)
|
|
365
|
-
|
|
429
|
+
init) cmd_init "$@" ;;
|
|
430
|
+
dequeue) cmd_dequeue "$@" ;;
|
|
431
|
+
pass) cmd_pass "$@" ;;
|
|
432
|
+
fail) cmd_fail "$@" ;;
|
|
433
|
+
requeue) cmd_requeue "$@" ;;
|
|
434
|
+
update_phase) cmd_update_phase "$@" ;;
|
|
435
|
+
recover) cmd_recover ;;
|
|
436
|
+
next-sprint) cmd_next_sprint ;;
|
|
437
|
+
status) cmd_status ;;
|
|
366
438
|
*)
|
|
367
|
-
echo "Usage: harness-queue-manager.sh <init|dequeue|pass|fail|requeue|recover|update_phase|status> [args]"
|
|
439
|
+
echo "Usage: harness-queue-manager.sh <init|dequeue|pass|fail|requeue|recover|next-sprint|update_phase|status> [args]"
|
|
368
440
|
exit 1
|
|
369
441
|
;;
|
|
370
442
|
esac
|