@walwal-harness/cli 5.0.8 → 5.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.
@@ -1,17 +1,23 @@
1
1
  # /harness-team — Team Mode 시작/재개
2
2
 
3
- Planner가 완료한 feature-list.json의 피처들을 3개 팀이 병렬로 Gen→Eval 사이클을 수행합니다.
3
+ Planner가 완료한 feature-list.json의 피처들을 최대 3개 팀이 병렬로 Gen→Eval 사이클을 수행합니다.
4
+
5
+ ## 프로세스 설계 원칙
6
+
7
+ 1. **Worker = 1 Feature Only**: 각 Worker는 단일 피처만 처리하고 반환. 자동 다음 피처 획득하지 않음.
8
+ 2. **Lead = Orchestration Loop**: Lead가 Worker 완료 알림을 받고, merge → unblock 확인 → 새 Worker 생성을 반복.
9
+ 3. **Background Agent**: Worker를 `run_in_background: true`로 생성하여 Lead가 개별 완료에 즉시 반응.
10
+ 4. **Merge 후 재투입**: Worker PASS → Lead가 worktree merge → queue pass (unblock) → 새 Worker 생성.
4
11
 
5
12
  ## 실행 절차
6
13
 
7
14
  ### Step 0: 선행 조건 확인
8
15
 
9
16
  ```bash
10
- # Planner 아티팩트 확인
11
17
  [ -f .harness/actions/feature-list.json ] && [ -f .harness/actions/api-contract.json ] && echo "READY" || echo "NOT_READY"
12
18
  ```
13
19
 
14
- - **NOT_READY** → "Planner가 먼저 완료되어야 합니다. /harness-dispatcher 또는 프롬프트로 진행하세요." 안내 후 중단.
20
+ - **NOT_READY** → "Planner가 먼저 완료되어야 합니다." 안내 후 중단.
15
21
  - **READY** → Step 1로 진행.
16
22
 
17
23
  ### Step 1: 모드 전환 + Queue 초기화
@@ -37,44 +43,110 @@ bash scripts/harness-queue-manager.sh status .
37
43
  bash scripts/harness-tmux.sh --team
38
44
  ```
39
45
 
40
- 스크립트 출력을 확인합니다:
41
- - **`Layout ready`** → tmux 레이아웃 구축 완료. Step 3로.
42
- - **`OPENED_TERMINAL=true`** → 새 Terminal.app 창에 Studio 레이아웃이 자동 구축됨. Step 3로.
43
- - **`already set up`** → 이미 구축됨. Step 3로.
44
-
45
- ### Step 3: Feature 할당 (dequeue)
46
+ ### Step 3: 초기 Worker 생성
46
47
 
47
- Queue status 결과를 확인하여 `ready` 큐에 feature가 있는지 확인합니다.
48
- ready feature 수와 설정된 concurrency(기본 3) 중 작은 값만큼 팀을 생성합니다.
49
-
50
- **각 팀마다** dequeue 명령으로 feature를 원자적으로 할당합니다:
48
+ Queue에서 ready 피처를 최대 3개 dequeue하고, **각각 background Agent로 생성**합니다.
51
49
 
52
50
  ```bash
53
- bash scripts/harness-queue-manager.sh dequeue {TEAM_NUMBER} .
51
+ # 각 팀마다 dequeue
52
+ bash scripts/harness-queue-manager.sh dequeue 1 .
53
+ bash scripts/harness-queue-manager.sh dequeue 2 .
54
+ bash scripts/harness-queue-manager.sh dequeue 3 .
54
55
  ```
55
56
 
56
- dequeue 결과로 feature ID가 반환됩니다. 빈 결과면 해당 팀은 생성하지 않습니다.
57
-
58
- ### Step 4: Agent 도구로 팀 생성 (Gen↔Eval 분리 사이클)
59
-
60
- dequeue로 할당받은 feature마다 **Agent 도구**를 호출합니다.
61
- **반드시 `isolation: "worktree"`를 사용**하여 각 팀이 독립된 코드 복사본에서 작업합니다.
62
-
63
- **독립적인 팀들은 단일 메시지에서 병렬로 호출**하세요 (한 번의 응답에 여러 Agent 도구 호출).
57
+ dequeue 성공한 피처마다 **background Agent**를 생성합니다:
64
58
 
65
59
  ```
66
60
  Agent({
67
61
  description: "Team-{N}: {FEATURE_ID}",
68
62
  isolation: "worktree",
63
+ run_in_background: true,
69
64
  prompt: "<아래 Team Worker 프롬프트>"
70
65
  })
71
66
  ```
72
67
 
73
- ### Team Worker 프롬프트
68
+ **중요: `run_in_background: true`로 생성**하면 Lead가 블록되지 않고, 각 Worker 완료 시 알림을 받습니다.
69
+
70
+ 초기 생성 후 **Step 4 (Orchestration Loop)**로 진입합니다.
71
+
72
+ ### Step 4: Orchestration Loop (Lead 핵심 루프)
73
+
74
+ **이 루프가 Team Mode의 핵심입니다. 모든 Sprint가 완료될 때까지 반복합니다.**
75
+
76
+ ```
77
+ ORCHESTRATION LOOP:
78
+
79
+ Background Agent 완료 알림을 받으면:
80
+
81
+ 1. Worker 결과 분석:
82
+ - PASS인 경우 → Step 4a (Merge + Unblock)
83
+ - FAIL (재시도 가능)인 경우 → Step 4b (Retry)
84
+ - ESCALATED인 경우 → 사용자에게 알림, 해당 팀 유휴
85
+
86
+ 2. Queue 상태 확인:
87
+ bash scripts/harness-queue-manager.sh status .
88
+
89
+ 3. ready > 0 이면:
90
+ → 새 Worker를 background Agent로 생성 (Step 3과 동일)
91
+ → LOOP 계속
92
+
93
+ 4. ready = 0 AND in_progress > 0 이면:
94
+ → 다른 Worker 완료를 대기
95
+ → LOOP 계속
96
+
97
+ 5. ready = 0 AND in_progress = 0 이면:
98
+ → Sprint 전환 시도:
99
+ bash scripts/harness-queue-manager.sh next-sprint .
100
+ - "Advancing" → Step 3으로 (새 Sprint 피처 dequeue + Worker 생성)
101
+ - "ALL SPRINTS COMPLETE" → 최종 보고. LOOP 종료.
102
+ - "Cannot advance" → 실패 피처 사용자 개입 요청. LOOP 종료.
103
+ ```
104
+
105
+ #### Step 4a: Merge + Unblock (PASS 처리)
106
+
107
+ Worker가 PASS로 반환되면:
108
+
109
+ 1. **Worktree branch 확인**: Agent 반환 결과에서 worktree path와 branch 확인
110
+ 2. **Main에 merge**:
111
+ ```bash
112
+ git merge {BRANCH_NAME} --no-edit
113
+ ```
114
+ - 충돌 시: 자동 해결 시도 → 실패 시 사용자 개입 요청
115
+ 3. **Queue 업데이트** (unblock 포함):
116
+ ```bash
117
+ bash scripts/harness-queue-manager.sh pass {FEATURE_ID} .
118
+ ```
119
+ → 의존 피처가 자동으로 blocked → ready로 전이
120
+ 4. **진행 로그**:
121
+ ```bash
122
+ echo "$(date +'%Y-%m-%d %H:%M') | lead | pass | {FEATURE_ID} merged + unblocked deps" >> .harness/progress.log
123
+ ```
124
+
125
+ #### Step 4b: Retry (FAIL 처리)
126
+
127
+ Worker가 FAIL (재시도 가능)로 반환되면:
128
+
129
+ 1. 시도 횟수 확인 (최대 5회)
130
+ 2. 5회 미만:
131
+ ```bash
132
+ bash scripts/harness-queue-manager.sh requeue {FEATURE_ID} .
133
+ bash scripts/harness-queue-manager.sh dequeue {TEAM_NUMBER} .
134
+ ```
135
+ → 새 background Agent Worker 생성 (이전 Eval feedback 포함)
136
+ 3. 5회 도달:
137
+ ```bash
138
+ bash scripts/harness-queue-manager.sh fail {FEATURE_ID} .
139
+ echo "$(date +'%Y-%m-%d %H:%M') | lead | escalate | {FEATURE_ID} ESCALATED after 5 attempts" >> .harness/progress.log
140
+ ```
141
+ → 사용자 개입 요청
142
+
143
+ ---
144
+
145
+ ## Team Worker 프롬프트
74
146
 
75
147
  ```
76
- 당신은 Harness Team-{N} 워커입니다. 하나의 Feature에 대해 Gen→Eval 사이클을 수행합니다.
77
- 완료 후 자동으로 다음 Feature를 dequeue하여 연속 작업합니다.
148
+ 당신은 Harness Team-{N} 워커입니다. **단일 Feature**에 대해 Gen→Eval 사이클을 수행합니다.
149
+ 완료 후 결과를 반환합니다. 다음 Feature는 Lead가 할당합니다.
78
150
 
79
151
  ## 할당된 Feature
80
152
  - Feature ID: {FEATURE_ID}
@@ -83,224 +155,145 @@ Agent({
83
155
 
84
156
  ## 실시간 로깅 (필수)
85
157
 
86
- Monitor 패널에서 **각 에이전트(Gen / Eval / Result)가 지금 무엇을 하고 있는지**가 보여야 합니다.
87
- Phase 전환뿐 아니라 **내부 하위 단계**(파일 읽기, 파일 쓰기, 테스트 실행, AC 검증 등)까지
88
- progress.log에 한 줄씩 남기세요. 대시보드는 3초마다 tail합니다.
89
-
90
- **로깅 원칙**
91
- - 의미 있는 동작마다 **한 줄씩** 즉시 기록 (파일 단위가 아니라 행위 단위)
92
- - ACTION 토큰은 아래 표에서 선택 (Monitor가 아이콘/색을 매핑)
93
- - DETAIL은 구체적으로 — 파일명, AC 번호, 에러 메시지 요약, 결정 사유
158
+ **로깅 설정:**
159
+ ```bash
160
+ HARNESS_ROOT=$(git worktree list | head -1 | awk '{print $1}')
161
+ LOG="$HARNESS_ROOT/.harness/progress.log"
162
+ logev() { echo "$(date +'%Y-%m-%d %H:%M') | team-{N} | $1 | $2" >> "$LOG"; }
163
+ ```
94
164
 
95
165
  | ACTION | 사용 시점 | DETAIL 예시 |
96
166
  |--------|-----------|-------------|
97
167
  | `gen-start` | Gen Phase 시작 | `F-001 start — 6 AC` |
98
168
  | `gen-read` | 소스/계약 읽기 | `read api-contract.json (POST /users)` |
99
169
  | `gen-write` | 파일 생성/수정 | `write apps/service-user/src/user.controller.ts` |
100
- | `gen-test` | 자체 게이트(tsc/eslint/jest) | `tsc OK · eslint 0 · jest 12/12` |
170
+ | `gen-test` | 자체 게이트 | `tsc OK · eslint 0 · jest 12/12` |
101
171
  | `gen-done` | Gen Phase 종료 | `F-001 done — 5 files, 142 LOC` |
102
- | `eval-start` | Evaluator Agent 호출 시작 | `F-001 spawning evaluator` |
103
- | `eval-check` | AC 개별 검증 진행 | `AC-3 — verify POST /users returns 201` |
104
- | `eval-done` | Eval 결과 수신 | `verdict=PASS score=2.95` |
105
- | `result` / `pass` | PASS 확정 | `F-001 PASS — queue.pass` |
106
- | `fail` | FAIL 확정(재시도/최종) | `FAIL #1 — AC-2 missing` |
107
- | `escalate` | 5회 초과 실패 | `F-001 ESCALATED — user intervention required` |
108
-
109
- **progress.log 기록** (하네스 루트의 progress.log에 append):
110
- ```bash
111
- echo "$(date +'%Y-%m-%d %H:%M') | team-{N} | {ACTION} | {DETAIL}" >> {HARNESS_ROOT}/.harness/progress.log
112
- ```
172
+ | `eval-start` | Evaluator 시작 | `F-001 spawning evaluator` |
173
+ | `eval-check` | AC 검증 | `AC-3 — verify POST /users returns 201` |
174
+ | `eval-done` | Eval 결과 | `verdict=PASS score=2.95` |
175
+ | `result` | PASS 확정 | `F-001 PASS` |
176
+ | `fail` | FAIL 확정 | `FAIL #1 — AC-2 missing` |
113
177
 
114
- **queue phase 업데이트** (feature-queue.json의 팀 상태 갱신):
178
+ **queue phase 업데이트:**
115
179
  ```bash
116
- bash {HARNESS_ROOT}/scripts/harness-queue-manager.sh update_phase {FEATURE_ID} {PHASE} .
180
+ bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" update_phase {FEATURE_ID} {PHASE} "$HARNESS_ROOT"
117
181
  ```
118
182
 
119
- > {HARNESS_ROOT}는 worktree의 원본 프로젝트 경로입니다. `git worktree list` 첫 줄에서 확인 가능합니다.
120
- > 워커 시작 시 먼저 실행: `HARNESS_ROOT=$(git worktree list | head -1 | awk '{print $1}')`
121
-
122
183
  ## Phase 1: Generator (코드 생성)
123
184
 
124
- **시작 시 로깅:**
125
185
  ```bash
126
- HARNESS_ROOT=$(git worktree list | head -1 | awk '{print $1}')
127
- LOG="$HARNESS_ROOT/.harness/progress.log"
128
- logev() { echo "$(date +'%Y-%m-%d %H:%M') | team-{N} | $1 | $2" >> "$LOG"; }
129
-
130
186
  logev gen-start "{FEATURE_ID} start"
131
187
  bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" update_phase {FEATURE_ID} gen "$HARNESS_ROOT"
132
188
  ```
133
189
 
134
- 1. Feature 정보 확인 — **각 읽기마다 로그**:
135
- ```bash
136
- logev gen-read "feature-list.json → {FEATURE_ID}"
137
- logev gen-read "api-contract.json → {관련 엔드포인트}"
138
- ```
139
- - `jq '.features[] | select(.id == "{FEATURE_ID}")' .harness/actions/feature-list.json`
140
- - `.harness/actions/api-contract.json`에서 관련 엔드포인트 확인
141
- - AC(Acceptance Criteria) 목록을 정확히 파악
142
-
143
- 2. 코드 생성 — **파일 쓰기마다 로그**:
144
- ```bash
145
- logev gen-write "apps/service-user/src/user.controller.ts"
146
- logev gen-write "libs/shared-dto/src/user.dto.ts"
147
- ```
148
- - AGENTS.md의 IA-MAP에 따라 올바른 디렉토리에 코드 작성
149
- - AC의 모든 항목을 충족하도록 구현
150
-
151
- 3. Pre-eval 게이트 (자체) — **결과 로그**:
152
- ```bash
153
- logev gen-test "tsc OK · eslint 0w 0e · jest 12/12"
154
- ```
155
- - tsc (타입 체크) 실행
156
- - eslint (린트) 실행
157
- - 컴파일 에러가 있으면 직접 수정 (Eval에 넘기지 않음)
190
+ 1. Feature 정보 확인 (feature-list.json, api-contract.json)
191
+ 2. 코드 생성 (AGENTS.md IA-MAP 준수, AC 전체 충족)
192
+ 3. Pre-eval 게이트 (tsc, eslint — 에러 있으면 직접 수정)
158
193
 
159
- **Gen 완료 로깅:**
160
194
  ```bash
161
- logev gen-done "{FEATURE_ID} done — {변경파일수} files, {LOC} LOC"
195
+ logev gen-done "{FEATURE_ID} done — {파일수} files"
162
196
  ```
163
197
 
164
- ## Phase 2: Evaluator (독립 평가 — Agent 도구 사용)
198
+ ## Phase 2: Evaluator (독립 평가)
165
199
 
166
- **Eval 시작 로깅:**
167
200
  ```bash
168
201
  logev eval-start "{FEATURE_ID} spawning evaluator"
169
202
  bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" update_phase {FEATURE_ID} eval "$HARNESS_ROOT"
170
203
  ```
171
204
 
172
- 코드 생성이 완료되면 **별도 Agent를 생성하여 평가**합니다.
173
- 이 Evaluator Agent는 당신(Generator)의 추론 과정을 모릅니다.
174
- 오직 코드와 AC만 보고 판단합니다.
205
+ **별도 Agent 생성** (Generator의 추론 과정을 모르는 독립 평가):
175
206
 
176
207
  ```
177
208
  Agent({
178
209
  description: "Eval: {FEATURE_ID}",
179
- prompt: "<아래 Evaluator 프롬프트>"
180
- })
181
- ```
210
+ prompt: "당신은 독립 Evaluator입니다. Generator가 작성한 코드를 AC 기준으로 냉정하게 평가합니다.
211
+ Generator의 의도나 추론 과정은 알 수 없습니다. 오직 코드와 결과만 봅니다.
182
212
 
183
- #### Evaluator 프롬프트
213
+ ## 실시간 로깅 (필수)
214
+
215
+ 평가 진행 상황을 실시간으로 기록합니다. **각 AC 검증마다 반드시 logev를 호출**하세요.
184
216
 
217
+ ```bash
218
+ HARNESS_ROOT=$(git worktree list | head -1 | awk '{print $1}')
219
+ LOG=\"$HARNESS_ROOT/.harness/progress.log\"
220
+ logev() { echo \"$(date +'%Y-%m-%d %H:%M') | team-{N} | $1 | $2\" >> \"$LOG\"; }
185
221
  ```
186
- 당신은 독립 Evaluator입니다. Generator가 작성한 코드를 AC 기준으로 냉정하게 평가합니다.
187
- Generator의 의도나 추론 과정은 알 수 없습니다. 오직 코드와 결과만 봅니다.
222
+
223
+ **로깅 시점:**
224
+ 1. 평가 시작 즉시: `logev eval-start \"{FEATURE_ID} evaluating — {AC수} ACs\"`
225
+ 2. 각 AC 검증 후: `logev eval-check \"{FEATURE_ID} AC-{N}: [PASS/FAIL] {근거 요약}\"`
226
+ 3. tsc/eslint 검증 후: `logev eval-check \"{FEATURE_ID} gate: tsc {OK/FAIL}, eslint {OK/FAIL}\"`
227
+ 4. 최종 판정: `logev eval-done \"{FEATURE_ID} VERDICT={PASS/FAIL} SCORE={X.XX}/3.00\"`
188
228
 
189
229
  ## 평가 대상
190
230
  - Feature ID: {FEATURE_ID}
191
- - AC 확인: `jq '.features[] | select(.id == "{FEATURE_ID}").acceptance_criteria' .harness/actions/feature-list.json`
231
+ - AC: jq '.features[] | select(.id == \"{FEATURE_ID}\").acceptance_criteria' .harness/actions/feature-list.json
192
232
 
193
233
  ## 평가 기준
194
- 1. AC 100% 충족 여부 (부분 통과 = FAIL)
195
- 2. api-contract.json과의 일치 여부 (엔드포인트, 요청/응답 스키마)
196
- 3. tsc, eslint 통과 여부
197
- 4. 보안 취약점 여부 (OWASP Top 10)
198
- 5. 기존 코드와의 regression 여부
234
+ 1. AC 100% 충족 (부분 통과 = FAIL)
235
+ 2. api-contract.json 일치
236
+ 3. tsc/eslint 통과
237
+ 4. OWASP Top 10 보안
238
+ 5. Regression 여부
199
239
 
200
240
  ## 출력 형식
201
- 반드시 아래 형식으로 결과를 반환하세요:
202
-
203
241
  VERDICT: PASS 또는 FAIL
204
242
  SCORE: X.XX / 3.00
205
243
  EVIDENCE:
206
244
  - AC-1: [PASS/FAIL] 근거
207
- - AC-2: [PASS/FAIL] 근거
208
245
  - ...
209
- FEEDBACK: (FAIL인 경우만) 구체적 수정 지시
246
+ FEEDBACK: (FAIL만) 구체적 수정 지시"
247
+ })
210
248
  ```
211
249
 
212
- ## Phase 3: 결과 처리
213
-
214
- Evaluator Agent 결과를 확인합니다:
250
+ ## Phase 3: 결과 처리 + 반환
215
251
 
216
- ### PASS인 경우 (VERDICT: PASS, SCORE >= 2.80):
252
+ ### PASS (SCORE >= 2.80):
217
253
  ```bash
218
- logev result "{FEATURE_ID} PASS score={SCORE} — merging"
219
- bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" pass {FEATURE_ID} "$HARNESS_ROOT"
254
+ logev result "{FEATURE_ID} PASS score={SCORE}"
220
255
  ```
221
- 변경 파일 목록과 AC 충족 요약을 Lead에게 반환.
256
+ Lead에게 반환: `PASS | {FEATURE_ID} | score={SCORE} | files={변경파일목록}`
222
257
 
223
- ### FAIL인 경우:
258
+ ### FAIL (재시도 가능):
224
259
  ```bash
225
- logev fail "{FEATURE_ID} FAIL #{ATTEMPT} — {사유요약}"
260
+ logev fail "{FEATURE_ID} FAIL #{ATTEMPT} — {사유}"
226
261
  ```
227
- 1. Evaluator의 FEEDBACK을 읽고 코드를 수정 (Phase 1로 돌아감)
228
- 2. 수정 후 다시 Phase 2 (새 Evaluator Agent 생성 — 이전 Eval 컨텍스트 없음)
229
- 3. 최대 **5회** 시도. 5회 모두 FAIL이면:
230
- ```bash
231
- logev escalate "{FEATURE_ID} ESCALATED after 5 attempts — user intervention required"
232
- bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" fail {FEATURE_ID} "$HARNESS_ROOT"
233
- ```
234
- 실패 사유와 마지막 Eval 결과를 Lead에게 반환. **사용자 개입을 요청**합니다.
235
-
236
- ## Phase 4: 자동 업무 획득
262
+ 시도 횟수가 5회 미만이면:
263
+ - Evaluator FEEDBACK으로 코드 수정 → Phase 1로 돌아감 (같은 Worker 내에서 재시도)
264
+ - 새 Evaluator Agent 생성 (이전 Eval 기억 없음)
237
265
 
238
- 피처 PASS 또는 최종 FAIL 처리 후:
239
- 1. Queue 상태 확인: `bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" status "$HARNESS_ROOT"`
240
- 2. `ready` 큐에 피처가 있으면 → dequeue → **새 Gen-Eval 루프 시작** (Phase 1부터 반복)
241
- ```bash
242
- NEW_FEATURE=$(bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" dequeue {N} "$HARNESS_ROOT")
243
- logev gen-start "$NEW_FEATURE auto-acquired"
244
- ```
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
- ```
266
+ 5회 모두 FAIL:
267
+ ```bash
268
+ logev fail "{FEATURE_ID} FINAL FAIL after 5 attempts"
255
269
  ```
256
-
257
- ### Step 5: 결과 수집 + 재투입 루프 (반드시 반복)
258
-
259
- **중요: 이 Step은 모든 Agent가 반환될 때마다 반복 실행해야 합니다.**
260
- 피처 PASS 시 blocked 피처가 unblock되므로, 유휴 팀에 즉시 새 피처를 할당해야 합니다.
261
-
270
+ Lead에게 반환: `ESCALATED | {FEATURE_ID} | attempts=5 | last_feedback={마지막_피드백}`
262
271
  ```
263
- LOOP:
264
- 1. 현재 라운드의 모든 Team Agent 결과 수집 (PASS/FAIL 확인)
265
- 2. Queue 상태 재확인:
266
- bash scripts/harness-queue-manager.sh status .
267
272
 
268
- 3. ready > 0 이면:
269
- → Step 3으로 돌아감 (새로 unblock된 피처를 dequeue하여 팀 생성)
270
- → 최대 3팀까지 병렬 Agent 재생성
271
- → 생성 후 다시 이 LOOP의 1번으로 돌아와 결과 대기
273
+ ---
272
274
 
273
- 4. ready = 0 AND in_progress > 0 이면:
274
- → 아직 작업 중인 팀이 있음. 해당 Agent 결과를 기다림
275
- → 결과 수신 후 다시 이 LOOP의 1번으로 돌아감
275
+ ## 핵심 원칙
276
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
- ```
277
+ ### Worker = 1 Feature Only
278
+ - Worker는 할당된 단일 피처만 처리하고 반환
279
+ - 다음 피처 dequeue, next-sprint 시도는 **Lead만** 수행
280
+ - Worktree는 해당 피처 전용 — 다른 피처 작업 금지
285
281
 
286
- **이 LOOP를 건너뛰지 마세요.** 1개 팀만 시작했더라도, 그 팀이 PASS하면 blocked가 풀려서 2~3개 팀을 동시에 돌릴 수 있습니다. LOOP를 돌아야 유휴 팀이 즉시 새 피처를 받습니다.
282
+ ### Lead = Merge + Orchestrate
283
+ - Worker PASS 시 Lead가 branch merge → queue pass → unblock
284
+ - 새로 ready된 피처에 즉시 Worker 재생성
285
+ - Sprint 전환도 Lead가 판단
287
286
 
288
- ## 핵심 원칙
287
+ ### Background Agent로 비차단 실행
288
+ - `run_in_background: true`로 Worker 생성
289
+ - Lead가 각 Worker 완료에 즉시 반응
290
+ - 3팀이 서로 다른 속도로 작업해도 유휴 팀 즉시 재활용
289
291
 
290
292
  ### 자기 의식 편향 차단
291
- - Generator가 자기 코드를 평가하지 않음
292
- - Evaluator는 항상 새 Agent (Generator의 추론 과정을 모름)
293
- - FAIL 후 재시도 시에도 새 Evaluator를 생성 (이전 Eval 기억 없음)
294
-
295
- ### 중복 방지
296
- - dequeue는 원자적 (lock 사용) — 같은 feature를 두 번 할당 불가
297
- - ready가 0이면 팀을 생성하지 않음
298
-
299
- ### 격리
300
- - 각 팀은 `isolation: "worktree"`로 독립 코드 복사본에서 작업
301
- - 팀 간 코드 충돌 없음
293
+ - Evaluator는 항상 새 Agent (Generator의 추론 과정 모름)
294
+ - FAIL 후 재시도 시에도 새 Evaluator 생성
302
295
 
303
296
  ### 에스컬레이션 (5회 초과)
304
297
  - 5회 연속 FAIL 시 사용자 개입 요청
305
- - escalate 로그 기록 → Dashboard에 즉시 표시
306
- - 해당 팀은 다음 피처로 이동하지 않고 대기
298
+ - 해당 피처는 failed 상태로 남음
299
+ - 다른 피처는 계속 진행 (의존하지 않는 경우)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walwal-harness/cli",
3
- "version": "5.0.8",
3
+ "version": "5.1.0",
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"
@@ -40,7 +40,7 @@ strip_ansi() {
40
40
  }
41
41
 
42
42
  get_term_width() {
43
- tput cols 2>/dev/null || echo 80
43
+ echo "${_COLS:-$(tput cols 2>/dev/null || echo 80)}"
44
44
  }
45
45
 
46
46
  get_mode() {
@@ -150,14 +150,28 @@ render_solo_prompt_history() {
150
150
  local short_ts icon color
151
151
  short_ts=$(echo "$ts" | sed 's/^[0-9]*-//')
152
152
 
153
- case "$agent" in
154
- dispatcher*) icon="▸" ; color="$MAGENTA" ;;
155
- planner*) icon="□" ; color="$YELLOW" ;;
156
- generator*) icon="▶" ; color="$GREEN" ;;
157
- eval*) icon="✦" ; color="$RED" ;;
158
- team-*) icon="◆" ; color="$CYAN" ;;
159
- user*) icon="★" ; color="$BOLD" ;;
160
- *) icon="·" ; color="$DIM" ;;
153
+ # action 기반으로 먼저 분류 (team 모드에서 agent=team-N이므로)
154
+ case "$action" in
155
+ eval-start|eval-check|eval-done|eval)
156
+ icon="✦" ; color="$MAGENTA" ;;
157
+ gen-start|gen-read|gen-write|gen-test|gen-done|gen)
158
+ icon="▶" ; color="$GREEN" ;;
159
+ result|pass)
160
+ icon="✓" ; color="$GREEN" ;;
161
+ fail)
162
+ icon="✗" ; color="$RED" ;;
163
+ *)
164
+ # fallback: agent 기반
165
+ case "$agent" in
166
+ dispatcher*) icon="▸" ; color="$MAGENTA" ;;
167
+ planner*) icon="□" ; color="$YELLOW" ;;
168
+ generator*) icon="▶" ; color="$GREEN" ;;
169
+ eval*) icon="✦" ; color="$MAGENTA" ;;
170
+ team-*) icon="◆" ; color="$CYAN" ;;
171
+ user*) icon="★" ; color="$BOLD" ;;
172
+ *) icon="·" ; color="$DIM" ;;
173
+ esac
174
+ ;;
161
175
  esac
162
176
 
163
177
  if [ ${#detail} -gt 40 ]; then detail="${detail:0:38}.."; fi
@@ -331,9 +345,10 @@ render_archive_prompts() {
331
345
  total_prompts=$(echo "$all_prompts" | wc -l | tr -d ' ')
332
346
 
333
347
  # 마지막 프롬프트를 제외한 모든 프롬프트 = archived (처리 완료)
348
+ # newest first (tac), 전체 출력 (제한 없음)
334
349
  local archived
335
350
  if [ "$total_prompts" -le 1 ]; then return; fi
336
- archived=$(echo "$all_prompts" | head -n $((total_prompts - 1)) | tail -8)
351
+ archived=$(echo "$all_prompts" | head -n $((total_prompts - 1)) | tac)
337
352
 
338
353
  echo ""
339
354
  echo -e "${BOLD}Archive Prompt${RESET} ${DIM}(처리 완료)${RESET}"
@@ -350,10 +365,6 @@ render_archive_prompts() {
350
365
 
351
366
  echo -e " ${GREEN}✓${RESET} ${DIM}${short_ts}${RESET} ${detail}"
352
367
  done
353
-
354
- if [ "$total_prompts" -gt 9 ]; then
355
- echo -e " ${DIM}… +$((total_prompts - 9)) more${RESET}"
356
- fi
357
368
  }
358
369
 
359
370
  # ══════════════════════════════════════════
@@ -396,6 +407,9 @@ trap 'tput cnorm 2>/dev/null; exit 0' EXIT INT TERM
396
407
  clear
397
408
 
398
409
  while true; do
410
+ _ROWS=$(tput lines 2>/dev/null || echo 30)
411
+ _COLS=$(tput cols 2>/dev/null || echo 80)
412
+ export _ROWS _COLS
399
413
  buf=$(render_dashboard 2>&1)
400
414
  tput cup 0 0 2>/dev/null
401
415
  echo "$buf"
@@ -159,8 +159,14 @@ render_team_section() {
159
159
  case "$action" in
160
160
  gen|gen-start|gen-read|gen-write|gen-test|gen-done)
161
161
  a_icon="▶"; a_color="$GREEN"; a_label="Gen" ;;
162
- eval|eval-start|eval-check|eval-done)
163
- a_icon="✦"; a_color="$BLUE"; a_label="Eval" ;;
162
+ eval-start)
163
+ a_icon="✦"; a_color="$MAGENTA"; a_label="Eval" ;;
164
+ eval-check)
165
+ a_icon="·"; a_color="$MAGENTA"; a_label="Eval" ;;
166
+ eval-done)
167
+ a_icon="✦"; a_color="${BOLD}${MAGENTA}"; a_label="Eval" ;;
168
+ eval)
169
+ a_icon="✦"; a_color="$MAGENTA"; a_label="Eval" ;;
164
170
  result|pass)
165
171
  a_icon="✓"; a_color="$GREEN"; a_label="Result" ;;
166
172
  fail)
@@ -96,34 +96,30 @@ render_processing_status() {
96
96
  echo ""
97
97
  }
98
98
 
99
- # 사용자 프롬프트만 필터하여 표시
99
+ # 사용자 프롬프트만 필터하여 표시 (newest first, 전체 출력)
100
100
  render_user_prompts() {
101
101
  if [ ! -f "$PROGRESS_LOG" ]; then
102
102
  echo -e " ${DIM}(프롬프트 기록 없음)${RESET}"
103
103
  return
104
104
  fi
105
105
 
106
- local term_height
107
- term_height=$(tput lines 2>/dev/null || echo 30)
108
- local max_lines=$((term_height - 10))
109
- if [ "$max_lines" -lt 5 ]; then max_lines=5; fi
110
-
111
- # user-prompt 행만 필터
106
+ # user-prompt 행만 필터 → tac으로 newest first
112
107
  local prompts
113
- prompts=$(grep '| user-prompt |' "$PROGRESS_LOG" 2>/dev/null | tail -"$max_lines")
108
+ prompts=$(grep '| user-prompt |' "$PROGRESS_LOG" 2>/dev/null | tac)
114
109
 
115
110
  if [ -z "$prompts" ]; then
116
111
  echo -e " ${DIM}(사용자 프롬프트 없음)${RESET}"
117
112
  return
118
113
  fi
119
114
 
120
- local line_num=0
121
115
  local total_prompts
122
- total_prompts=$(grep -c '| user-prompt |' "$PROGRESS_LOG" 2>/dev/null || echo 0)
116
+ total_prompts=$(echo "$prompts" | wc -l | tr -d ' ')
123
117
 
124
- echo "$prompts" | while IFS= read -r line; do
125
- line_num=$((line_num + 1))
118
+ # 터미널 폭: main loop에서 측정한 _COLS 사용 (subshell 내 tput 불가 대비)
119
+ local max_width=$(( ${_COLS:-80} - 12 ))
120
+ if [ "$max_width" -lt 20 ]; then max_width=20; fi
126
121
 
122
+ echo "$prompts" | while IFS= read -r line; do
127
123
  local ts detail
128
124
  ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
129
125
  detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
@@ -131,20 +127,10 @@ render_user_prompts() {
131
127
  local short_ts
132
128
  short_ts=$(echo "$ts" | grep -oE '[0-9]{2}:[0-9]{2}' | tail -1 || echo "$ts")
133
129
 
134
- # 프롬프트 내용 truncate
135
- local max_width
136
- max_width=$(( $(tput cols 2>/dev/null || echo 40) - 12 ))
137
- if [ "$max_width" -lt 20 ]; then max_width=20; fi
138
130
  if [ ${#detail} -gt "$max_width" ]; then
139
131
  detail="${detail:0:$((max_width - 2))}.."
140
132
  fi
141
133
 
142
- # 처리 상태 찾기: 이 프롬프트 이후의 첫 번째 에이전트 액션
143
- local processed_by=""
144
- local prompt_ts_epoch
145
- prompt_ts_epoch=$(echo "$ts" | sed 's/[^0-9:]//g')
146
-
147
- # 간단히: 마지막 프롬프트인지 확인 → 현재 처리 중 표시
148
134
  echo -e " ${DIM}${short_ts}${RESET} ${WHITE}${detail}${RESET}"
149
135
  done
150
136
 
@@ -164,6 +150,10 @@ trap 'tput cnorm 2>/dev/null; exit 0' EXIT INT TERM
164
150
  clear
165
151
 
166
152
  while true; do
153
+ # subshell 내 tput 불가 → 미리 터미널 크기 측정
154
+ _ROWS=$(tput lines 2>/dev/null || echo 30)
155
+ _COLS=$(tput cols 2>/dev/null || echo 80)
156
+ export _ROWS _COLS
167
157
  buf=$(render_all 2>&1)
168
158
  tput cup 0 0 2>/dev/null
169
159
  echo "$buf"
@@ -75,10 +75,58 @@ disable-model-invocation: true
75
75
  ## Constraints
76
76
 
77
77
  - 기술 구현 세부사항은 Generator에 위임
78
- - Sprint당 기능 3-5개 권장
79
78
  - 각 기능에 `layer`, `service`, `depends_on` 명시
80
79
  - API 계약의 스키마는 Pydantic/class-validator로 직접 변환 가능한 수준
81
80
 
81
+ ## Team 병렬 스케줄링 규칙 (필수)
82
+
83
+ Team Mode는 **최대 3팀이 동시 작업**한다. Planner는 feature-list.json 설계 시 다음 규칙을 반드시 준수한다.
84
+
85
+ ### 핵심 원칙: Sprint 시작 시 ready ≥ 3
86
+
87
+ Sprint 시작 시점에 `depends_on`이 모두 충족된(또는 비어 있는) feature가 **최소 3개** 있어야 3팀이 즉시 가동된다. ready가 1~2개이면 나머지 팀은 유휴 상태가 된다.
88
+
89
+ ### 의존성 그래프 형태
90
+
91
+ **금지 — 직렬 체인:**
92
+ ```
93
+ F-001 → F-002 → F-003 → F-004 (ready=1, 1팀만 작업)
94
+ ```
95
+
96
+ **권장 — 넓은 DAG (fan-out):**
97
+ ```
98
+ ┌→ F-002 (no deps)
99
+ Foundation → F-003 (no deps) (ready=3, 3팀 동시)
100
+ └→ F-004 (no deps)
101
+ ↓
102
+ F-005 (depends_on: [F-002, F-003])
103
+ ```
104
+
105
+ ### Sprint 설계 체크리스트
106
+
107
+ 1. **Sprint당 feature 수**: `팀수 × 2` 이상 (3팀 = 최소 6개)
108
+ - 3개는 즉시 시작, 나머지는 앞선 feature 완료 시 투입
109
+ - 팀이 PASS 후 대기하지 않고 바로 다음 feature를 가져감
110
+ 2. **동시 ready 보장**: Sprint 내 feature 중 `depends_on: []`인 것이 ≥ 3개
111
+ 3. **의존성 깊이(critical path) 최소화**: 같은 Sprint 내 체인 깊이 ≤ 2단계
112
+ 4. **layer 분산**: 같은 Sprint에 backend-only, frontend-only, fullstack을 혼합하여 서로 독립적으로 작업 가능
113
+ 5. **Feature 분할**: 하나의 큰 feature 대신 독립 AC 그룹으로 분할
114
+ - 예: "대시보드 전체" (1개) → "통계 카드" + "차트 영역" + "최근 활동" (3개, 동시 작업 가능)
115
+
116
+ ### 검증: ready count 시뮬레이션
117
+
118
+ feature-list.json 완성 후, Sprint별 ready count를 머릿속으로 시뮬레이션한다:
119
+
120
+ ```
121
+ Sprint N 시작 → ready 목록 계산
122
+ ready ≥ 3 → OK (3팀 동시 가동)
123
+ ready = 2 → WARNING (1팀 유휴)
124
+ ready = 1 → FAIL → feature 분할 또는 의존성 제거 필요
125
+ ready = 0 → CRITICAL → 이전 Sprint 의존성 재설계 필요
126
+ ```
127
+
128
+ ready가 3 미만인 Sprint가 있으면 **feature를 더 작게 분할하거나 의존성을 제거**하여 수정한다.
129
+
82
130
  ## After Completion
83
131
 
84
132
  1. 사용자에게 plan.md + api-contract.json 리뷰 요청