@walwal-harness/cli 5.0.6 → 5.0.8
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 +39 -7
- package/package.json +1 -1
- package/scripts/harness-queue-manager.sh +88 -16
package/commands/harness-team.md
CHANGED
|
@@ -242,16 +242,48 @@ 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: 결과 수집 + 재투입 루프 (반드시 반복)
|
|
258
|
+
|
|
259
|
+
**중요: 이 Step은 모든 Agent가 반환될 때마다 반복 실행해야 합니다.**
|
|
260
|
+
피처 PASS 시 blocked 피처가 unblock되므로, 유휴 팀에 즉시 새 피처를 할당해야 합니다.
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
LOOP:
|
|
264
|
+
1. 현재 라운드의 모든 Team Agent 결과 수집 (PASS/FAIL 확인)
|
|
265
|
+
2. Queue 상태 재확인:
|
|
266
|
+
bash scripts/harness-queue-manager.sh status .
|
|
267
|
+
|
|
268
|
+
3. ready > 0 이면:
|
|
269
|
+
→ Step 3으로 돌아감 (새로 unblock된 피처를 dequeue하여 팀 생성)
|
|
270
|
+
→ 최대 3팀까지 병렬 Agent 재생성
|
|
271
|
+
→ 생성 후 다시 이 LOOP의 1번으로 돌아와 결과 대기
|
|
272
|
+
|
|
273
|
+
4. ready = 0 AND in_progress > 0 이면:
|
|
274
|
+
→ 아직 작업 중인 팀이 있음. 해당 Agent 결과를 기다림
|
|
275
|
+
→ 결과 수신 후 다시 이 LOOP의 1번으로 돌아감
|
|
276
|
+
|
|
277
|
+
5. ready = 0 AND in_progress = 0 이면:
|
|
278
|
+
→ 현재 Sprint의 모든 피처 처리 완료
|
|
279
|
+
→ 자동 Sprint 전환 시도:
|
|
280
|
+
bash scripts/harness-queue-manager.sh next-sprint .
|
|
281
|
+
- "Advancing to Sprint N" → Step 3으로 돌아가 팀 생성
|
|
282
|
+
- "ALL SPRINTS COMPLETE" → 전체 프로젝트 완료 보고. LOOP 종료.
|
|
283
|
+
- "Cannot advance: N failed" → 실패 피처 사용자 개입 요청. LOOP 종료.
|
|
284
|
+
```
|
|
249
285
|
|
|
250
|
-
|
|
251
|
-
1. 각 Agent 반환 메시지에서 PASS/FAIL 확인
|
|
252
|
-
2. Queue 상태 재확인: `bash scripts/harness-queue-manager.sh status .`
|
|
253
|
-
3. ready 큐에 새로 unblock된 feature가 있으면 → **Step 3으로 돌아가서** 추가 팀 생성
|
|
254
|
-
4. ready=0, in_progress=0이면 → 최종 결과 보고
|
|
286
|
+
**이 LOOP를 건너뛰지 마세요.** 1개 팀만 시작했더라도, 그 팀이 PASS하면 blocked가 풀려서 2~3개 팀을 동시에 돌릴 수 있습니다. LOOP를 돌아야 유휴 팀이 즉시 새 피처를 받습니다.
|
|
255
287
|
|
|
256
288
|
## 핵심 원칙
|
|
257
289
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.8",
|
|
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
|