@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.
@@ -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`이면 → "Team-{N} 대기 중 — 모든 할당 피처 완료" 반환
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
- 모든 Team Agent가 완료되면:
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.6",
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 .features[] as $f (
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
- classify($passed) as $classified |
117
+ target_features as $targets |
118
+ classify($targets; $passed) as $classified |
108
119
  {
109
- version: "4.0",
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) cmd_init ;;
359
- dequeue) cmd_dequeue "$@" ;;
360
- pass) cmd_pass "$@" ;;
361
- fail) cmd_fail "$@" ;;
362
- requeue) cmd_requeue "$@" ;;
363
- update_phase) cmd_update_phase "$@" ;;
364
- recover) cmd_recover ;;
365
- status) cmd_status ;;
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