@walwal-harness/cli 2.4.0 → 3.2.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.
- package/README.md +46 -22
- package/assets/templates/AGENTS.md.template +31 -0
- package/assets/templates/HARNESS.md +180 -6
- package/assets/templates/config.json +205 -14
- package/assets/templates/evaluation-functional.md.template +119 -0
- package/assets/templates/evaluation-visual.md.template +151 -0
- package/assets/templates/memory.md +20 -0
- package/assets/templates/progress.json.template +6 -0
- package/package.json +5 -2
- package/scripts/harness-next.sh +266 -6
- package/scripts/harness-session-start.sh +15 -12
- package/scripts/harness-statusline.sh +93 -0
- package/scripts/harness-user-prompt-submit.sh +31 -72
- package/scripts/lib/harness-guardrail.sh +143 -0
- package/scripts/lib/harness-render-progress.sh +8 -3
- package/scripts/scan-project.sh +33 -3
- package/skills/dispatcher/references/pipeline-definitions.md +20 -9
- package/skills/evaluator-functional-flutter/SKILL.md +10 -3
- package/skills/generator-frontend-flutter/SKILL.md +36 -2
- package/skills/generator-frontend-flutter/references/anti-patterns.md +44 -12
- package/skills/generator-frontend-flutter/references/flutter-web-pattern.md +273 -0
- package/skills/planner/references/fe-stack-detection.md +62 -15
package/README.md
CHANGED
|
@@ -155,17 +155,18 @@ Brainstormer? → Planner → Generator-BE → Evaluator-Func(API-only)
|
|
|
155
155
|
|
|
156
156
|
> `Brainstormer?` = Dispatcher가 사용자에게 확인 후 조건부 실행. 명확한 PRD/OpenAPI가 있으면 생략 가능.
|
|
157
157
|
|
|
158
|
-
### Flutter 프로젝트 자동 감지
|
|
158
|
+
### Flutter 프로젝트 자동 감지 (Web / Mobile / Desktop)
|
|
159
159
|
|
|
160
|
-
`pubspec.yaml` + `flutter:` 키가 감지되면 FE 에이전트가 **자동
|
|
160
|
+
`pubspec.yaml` + `flutter:` 키가 감지되면 FE 에이전트가 **자동 치환**됩니다. **Flutter Web** 은 컴파일 결과가 HTML+JS+CSS 이므로 React 와 동일하게 Playwright 기반 evaluator 를 그대로 사용하고, **Mobile/Desktop** 만 정적 분석 evaluator 로 교체됩니다.
|
|
161
161
|
|
|
162
|
-
|
|
|
163
|
-
|
|
164
|
-
| `generator-frontend` | `
|
|
165
|
-
| `evaluator-functional
|
|
166
|
-
| `
|
|
162
|
+
| fe_stack | fe_target | Generator | Eval-Functional | Eval-Visual |
|
|
163
|
+
|----------|-----------|-----------|----------------|-------------|
|
|
164
|
+
| react | (n/a) | `generator-frontend` | `evaluator-functional` (Playwright) | `evaluator-visual` |
|
|
165
|
+
| **flutter** | **web** | `generator-frontend-flutter` | **`evaluator-functional`** (Playwright!) | **`evaluator-visual`** (Playwright!) |
|
|
166
|
+
| **flutter** | mobile | `generator-frontend-flutter` | `evaluator-functional-flutter` (flutter analyze/test) | **SKIP** |
|
|
167
|
+
| **flutter** | desktop | `generator-frontend-flutter` | `evaluator-functional-flutter` (flutter analyze/test) | **SKIP** |
|
|
167
168
|
|
|
168
|
-
치환은 `pipeline.json.fe_stack`
|
|
169
|
+
**감지 방식**: `scan-project.sh` 가 Flutter 프로젝트의 `web/index.html`, `android/`, `ios/`, `macos/`, `windows/`, `linux/` 디렉터리 존재 여부로 `fe_target` 을 자동 판정. 멀티 타겟 또는 모호한 경우 Planner 가 사용자에게 확인합니다. 치환은 `pipeline.json.fe_stack + fe_target` 조합에 의해 `harness-next.sh` 가 자동 처리합니다.
|
|
169
170
|
|
|
170
171
|
---
|
|
171
172
|
|
|
@@ -269,20 +270,24 @@ Evaluator는 기능 테스트 전에 AGENTS.md의 IA-MAP과 실제 폴더 구조
|
|
|
269
270
|
| Evaluator-Visual | `harness-evaluator-visual` — 스크린샷 기반 디자인/접근성/반응형 검증 |
|
|
270
271
|
| 레퍼런스 | Vercel Best Practices, Design System Rules, AI Forbidden Patterns, Component Patterns |
|
|
271
272
|
|
|
272
|
-
### Flutter (Dart)
|
|
273
|
+
### Flutter (Dart) — Web / Mobile / Desktop
|
|
273
274
|
|
|
274
|
-
| 항목 |
|
|
275
|
-
|
|
276
|
-
| Generator | `harness-generator-frontend-flutter`
|
|
277
|
-
|
|
|
278
|
-
|
|
|
279
|
-
| 레퍼런스 | API Layer Pattern, Riverpod Pattern, i18n Pattern, Anti-Patterns
|
|
275
|
+
| 항목 | Web (`fe_target=web`) | Mobile/Desktop (`fe_target=mobile`/`desktop`) |
|
|
276
|
+
|------|----------------------|---------------------------------------------|
|
|
277
|
+
| Generator | `harness-generator-frontend-flutter` (Web 가이드 활성) | `harness-generator-frontend-flutter` (Mobile/Desktop 가이드) |
|
|
278
|
+
| Eval-Func | **`harness-evaluator-functional`** (Playwright MCP) | `harness-evaluator-functional-flutter` (`flutter analyze` + `flutter test` + build_runner drift + FL-01~FL-08 정적 검증) |
|
|
279
|
+
| Eval-Visual | **`harness-evaluator-visual`** (Playwright 스크린샷/반응형/접근성) | **SKIP** (브라우저 없음) |
|
|
280
|
+
| 레퍼런스 공통 | API Layer Pattern, Riverpod Pattern, i18n Pattern, Anti-Patterns | 동일 |
|
|
281
|
+
| 레퍼런스 추가 | **Flutter Web Pattern** (`dart:html`/`package:web` 허용, go_router, CORS, PWA, 빌드/호스팅) | — |
|
|
282
|
+
| dart:html / package:web | **허용** (가능하면 conditional import 권장) | **금지** (모바일/데스크톱 빌드 실패 유발) |
|
|
283
|
+
| 빌드 명령 | `flutter build web --release` / `flutter run -d chrome` | `flutter build apk` / `flutter build ios` / `flutter build macos` 등 |
|
|
280
284
|
|
|
281
|
-
### FE 스택 감지
|
|
285
|
+
### FE 스택 + 타겟 감지
|
|
282
286
|
|
|
283
|
-
1. `scan-project.sh`가 `pubspec.yaml` + `flutter:` 키를
|
|
284
|
-
2.
|
|
285
|
-
3.
|
|
287
|
+
1. `scan-project.sh`가 `pubspec.yaml` + `flutter:` 키를 탐지하여 `fe_stack = "flutter"` 설정
|
|
288
|
+
2. 같은 스캔이 `web/index.html`, `android/`, `ios/`, `macos/`/`windows/`/`linux/` 존재 여부로 `fe_target` 자동 판정 (`web` / `mobile` / `desktop`)
|
|
289
|
+
3. Planner 가 모호한 경우(멀티 타겟, unknown) 사용자에게 단 한 번 확인하고 `pipeline.json.fe_target` 확정
|
|
290
|
+
4. `harness-next.sh`가 `fe_stack + fe_target` 조합에 따라 FE 에이전트를 자동 치환 (Agent Bar에도 반영)
|
|
286
291
|
|
|
287
292
|
---
|
|
288
293
|
|
|
@@ -340,7 +345,7 @@ Evaluator-Functional / Evaluator-Visual이 브라우저 테스트를 수행하
|
|
|
340
345
|
}
|
|
341
346
|
```
|
|
342
347
|
|
|
343
|
-
> Flutter
|
|
348
|
+
> **Flutter Web (`fe_target=web`) 프로젝트도 Playwright 가 필요합니다** — 컴파일 결과가 일반 웹앱과 동일하므로 React 와 같은 evaluator 를 재사용합니다. **Flutter Mobile/Desktop (`fe_target=mobile`/`desktop`) 프로젝트만** Playwright 없이 `flutter analyze` + `flutter test` 기반으로 동작합니다.
|
|
344
349
|
|
|
345
350
|
---
|
|
346
351
|
|
|
@@ -384,10 +389,29 @@ grep "flutter:" pubspec.yaml
|
|
|
384
389
|
|
|
385
390
|
# 재스캔
|
|
386
391
|
bash scripts/scan-project.sh .
|
|
387
|
-
# pipeline.json의 fe_stack 값 확인
|
|
388
|
-
cat .harness/actions/pipeline.json | jq '.fe_stack'
|
|
392
|
+
# pipeline.json의 fe_stack + fe_target 값 확인
|
|
393
|
+
cat .harness/actions/pipeline.json | jq '.fe_stack, .fe_target'
|
|
389
394
|
```
|
|
390
395
|
|
|
396
|
+
### Flutter Web 프로젝트인데 evaluator-functional-flutter (정적 분석) 가 실행돼요
|
|
397
|
+
|
|
398
|
+
`fe_target` 이 `web` 으로 감지되지 않아서 발생합니다. 다음 두 가지를 확인:
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
# 1) Flutter 프로젝트 루트에 web/index.html 이 존재하는가?
|
|
402
|
+
ls -la web/index.html
|
|
403
|
+
# 없으면 Flutter Web 활성화:
|
|
404
|
+
flutter config --enable-web
|
|
405
|
+
flutter create --platforms=web .
|
|
406
|
+
|
|
407
|
+
# 2) pipeline.json 의 fe_target 값 확인
|
|
408
|
+
jq '.fe_target' .harness/actions/pipeline.json
|
|
409
|
+
# "mobile" 또는 "unknown" 이면 수동 수정 또는 재스캔
|
|
410
|
+
bash scripts/scan-project.sh .
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`fe_target = web` 으로 확정되면 Playwright 기반 `evaluator-functional` + `evaluator-visual` 이 자동 사용됩니다.
|
|
414
|
+
|
|
391
415
|
### Auto-routing을 끄고 싶어요
|
|
392
416
|
|
|
393
417
|
```json
|
|
@@ -50,3 +50,34 @@
|
|
|
50
50
|
- 서비스 간 직접 DB 접근
|
|
51
51
|
- 테스트 삭제/약화
|
|
52
52
|
- archive/ 내 파일 수정
|
|
53
|
+
- 아티팩트 상태가 `draft` 미만인 선행 아티팩트에 의존하여 작업 시작
|
|
54
|
+
|
|
55
|
+
### 품질 게이트 (v3.1)
|
|
56
|
+
|
|
57
|
+
| 게이트 | 시점 | 내용 |
|
|
58
|
+
|--------|------|------|
|
|
59
|
+
| **Pre-Eval Gate** | Generator → Evaluator 전환 | tsc, eslint, jest/vitest 자동 실행. 실패 시 Generator 리라우팅 |
|
|
60
|
+
| **파일 소유권 검증** | 에이전트 전환 시 | git diff로 권한 밖 파일 수정 감지 |
|
|
61
|
+
| **아티팩트 선행조건** | 에이전트 시작 전 | progress.json.artifacts 상태 확인 |
|
|
62
|
+
| **에스컬레이션** | 3회 연속 실패 | Planner에게 scope 축소/접근 변경 요청 |
|
|
63
|
+
|
|
64
|
+
### Evaluation System (v3.2)
|
|
65
|
+
|
|
66
|
+
| 설정 | 값 |
|
|
67
|
+
|------|------|
|
|
68
|
+
| PASS 기준 | **2.80 / 3.00 이상** |
|
|
69
|
+
| FAIL 기준 | 2.79 이하 (예외 없음) |
|
|
70
|
+
| Evidence 없는 Score | 0점 강제 |
|
|
71
|
+
| AC 부분 통과 | FAIL (100% 필수) |
|
|
72
|
+
| Regression 실패 1건+ | FAIL (신규 점수 무관) |
|
|
73
|
+
|
|
74
|
+
- Planner는 feature-list.json에 **Executable AC** (type: api/visual/e2e + verify 조건) 필수 작성
|
|
75
|
+
- Evaluator는 Adversarial Rules에 따라 적대적으로 검증 (rubber-stamping 금지)
|
|
76
|
+
- 이전 Sprint PASS 기능은 Regression Checkpoint로 재검증
|
|
77
|
+
- Eval-Functional ↔ Eval-Visual 간 Cross-Validation으로 불일치 감지
|
|
78
|
+
|
|
79
|
+
### 메모리 오염 방어
|
|
80
|
+
|
|
81
|
+
- gotcha/memory 항목은 `unverified` 상태로 시작, Planner 리뷰 후 `verified` 승격
|
|
82
|
+
- TTL 만료 항목은 Planner 스프린트 전환 시 리뷰 (갱신 또는 삭제)
|
|
83
|
+
- 코드/git으로 검증 불가한 항목은 즉시 삭제
|
|
@@ -69,11 +69,68 @@ Planner → Gen-BE → Eval-Func(API-only) → Archive
|
|
|
69
69
|
### 공통 — 실패 시 루프
|
|
70
70
|
|
|
71
71
|
```
|
|
72
|
-
Eval-Func FAIL → failure_location에 따라 Gen-BE 또는 Gen-FE 재작업 (max
|
|
73
|
-
Eval-Visual FAIL → Gen-FE 재작업 (max
|
|
74
|
-
|
|
72
|
+
Eval-Func FAIL → failure_location에 따라 Gen-BE 또는 Gen-FE 재작업 (max 5회)
|
|
73
|
+
Eval-Visual FAIL → Gen-FE 재작업 (max 5회)
|
|
74
|
+
3회 실패 → Planner 에스컬레이션 (scope 축소/접근 변경)
|
|
75
|
+
5회 초과 → 사용자 개입 요청
|
|
75
76
|
```
|
|
76
77
|
|
|
78
|
+
### Pre-Eval Gate (Deterministic Checks)
|
|
79
|
+
|
|
80
|
+
Generator → Evaluator 전환 전, 결정론적 검증을 자동 실행합니다:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
Generator 완료 → [tsc --noEmit] → [eslint] → [jest/vitest --bail] → Evaluator
|
|
84
|
+
↓ FAIL
|
|
85
|
+
Generator로 리라우팅 (Evaluator 세션 미개설)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- Backend: `tsc --noEmit`, `eslint . --max-warnings=0`, `jest --bail`
|
|
89
|
+
- Frontend: `tsc --noEmit`, `eslint . --max-warnings=0`, `vitest run --bail 1`
|
|
90
|
+
- `config.json`의 `flow.pre_eval_gate`에서 커스터마이징 가능
|
|
91
|
+
|
|
92
|
+
### Runtime Guardrail (파일 소유권 검증)
|
|
93
|
+
|
|
94
|
+
에이전트 전환 시 `git diff`로 이전 에이전트가 권한 밖 파일을 수정했는지 검증합니다.
|
|
95
|
+
위반 발견 시 경고를 출력하고 리뷰를 요청합니다.
|
|
96
|
+
|
|
97
|
+
### Context Isolation Guard (컨텍스트 분리 가드레일)
|
|
98
|
+
|
|
99
|
+
한 세션에서 여러 에이전트를 실행하면 컨텍스트가 오염됩니다.
|
|
100
|
+
`UserPromptSubmit` 훅이 다음 위반을 실시간 감지합니다:
|
|
101
|
+
|
|
102
|
+
- `current_agent`가 running인데 다른 `/harness-*` 스킬 호출 시 경고 주입
|
|
103
|
+
- `agent_status`를 completed로 변경하지 않고 다음 에이전트 호출 시 경고
|
|
104
|
+
|
|
105
|
+
### Statusline (상시 상태 표시)
|
|
106
|
+
|
|
107
|
+
터미널 하단에 항상 고정되는 1줄 compact 상태:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
[S1] FULL | >backend | 2/5 feat | ctx 45% | $1.23
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- `scripts/harness-statusline.sh`가 3초 간격으로 `progress.json`을 읽어 갱신
|
|
114
|
+
- `.claude/settings.json`의 `statusLine` 설정으로 활성화
|
|
115
|
+
- 세션 시작 시 장황한 프로그래스 출력 대신 statusline으로 대체
|
|
116
|
+
|
|
117
|
+
### Artifact State Machine
|
|
118
|
+
|
|
119
|
+
주요 아티팩트는 상태를 추적합니다:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
pending → draft → reviewed → approved
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
| 아티팩트 | 생성 에이전트 | 필수 상태 (다음 에이전트 진행 조건) |
|
|
126
|
+
|----------|-------------|----------------------------------|
|
|
127
|
+
| plan.md | Planner | draft 이상 → Generator |
|
|
128
|
+
| api-contract.json | Planner | draft 이상 → Generator |
|
|
129
|
+
| feature-list.json | Planner | draft 이상 → Generator |
|
|
130
|
+
| sprint-contract.md | Generator | draft 이상 → Evaluator |
|
|
131
|
+
|
|
132
|
+
상태는 `progress.json.artifacts`에서 추적됩니다.
|
|
133
|
+
|
|
77
134
|
## 세션 오케스트레이션
|
|
78
135
|
|
|
79
136
|
### 핵심: 한 세션에 1 에이전트 단계
|
|
@@ -117,12 +174,42 @@ claude --prompt "$(cat .harness/next-prompt.txt)"
|
|
|
117
174
|
|
|
118
175
|
모든 에이전트 스킬에 내장된 프로토콜:
|
|
119
176
|
|
|
120
|
-
- **On Start**: `progress.json` 읽기 → `agent_status: "running"` 설정
|
|
121
|
-
- **On Complete**: `progress.json` 업데이트 → `next_agent` 계산 → **STOP**
|
|
177
|
+
- **On Start**: `progress.json` 읽기 → `agent_status: "running"` 설정 → `handoff.json` 참조
|
|
178
|
+
- **On Complete**: `progress.json` 업데이트 → 아티팩트 상태 갱신 → `next_agent` 계산 → **STOP**
|
|
122
179
|
- **On Fail** (Evaluator): `failure` 정보 기록 → `retry_target` 설정 → **STOP**
|
|
180
|
+
- **On Transition**: 파일 소유권 검증 → Pre-Eval Gate (해당 시) → 아티팩트 선행조건 검증
|
|
123
181
|
|
|
124
182
|
에이전트는 절대 다음 에이전트를 직접 호출하지 않습니다.
|
|
125
183
|
|
|
184
|
+
### Handoff Document
|
|
185
|
+
|
|
186
|
+
에이전트 전환 시 `.harness/handoff.json`이 자동 생성됩니다:
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"from": "planner",
|
|
191
|
+
"to": "generator-backend",
|
|
192
|
+
"sprint": 1,
|
|
193
|
+
"retry_count": 0,
|
|
194
|
+
"sprint_status": "running",
|
|
195
|
+
"failure_context": null,
|
|
196
|
+
"artifacts_ready": ["plan.md", "api-contract.json", "feature-list.json"],
|
|
197
|
+
"focus_features": ["F-001", "F-002"],
|
|
198
|
+
"warnings": [],
|
|
199
|
+
"timestamp": "2026-04-09T12:00:00Z"
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
각 에이전트는 세션 시작 시 이 파일을 읽어 컨텍스트를 확보합니다.
|
|
204
|
+
|
|
205
|
+
### Escalation Protocol
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
1-2회 실패: 동일 에이전트 재시도 (실패 원인 요약 포함)
|
|
209
|
+
3회 실패: Planner 에스컬레이션 (scope 축소 또는 접근 변경)
|
|
210
|
+
5회 실패: BLOCKED — 사용자 개입 요청
|
|
211
|
+
```
|
|
212
|
+
|
|
126
213
|
## 핵심 원칙
|
|
127
214
|
|
|
128
215
|
1. **Backend First** — API가 안정된 후 Frontend 연동 (없는 API 호출 방지)
|
|
@@ -130,10 +217,97 @@ claude --prompt "$(cat .harness/next-prompt.txt)"
|
|
|
130
217
|
3. **한 세션에 1 에이전트 단계** — 컨텍스트 소진 방지, Session Boundary Protocol 준수
|
|
131
218
|
4. **feature-list.json의 passes만 수정** — 기능 정의는 Planner만 변경
|
|
132
219
|
5. **테스트 삭제/약화 금지** — 테스트는 계약이다
|
|
133
|
-
6. **Evaluator는
|
|
220
|
+
6. **Evaluator는 적대적** — Rubber-stamping 금지, 2.80/3.00 미만 = FAIL, Evidence 없는 Score = 0
|
|
134
221
|
7. **아카이브 불변** — 완료 문서 수정 금지
|
|
135
222
|
8. **MSA 경계 존수** — 서비스 간 직접 DB 접근 금지, 반드시 메시지 패턴
|
|
136
223
|
|
|
224
|
+
## Evaluation System (v3.2)
|
|
225
|
+
|
|
226
|
+
### 정량 채점 (Rubric Scoring)
|
|
227
|
+
|
|
228
|
+
모든 Evaluator는 구조화된 Rubric으로 채점합니다:
|
|
229
|
+
|
|
230
|
+
| 설정 | 값 |
|
|
231
|
+
|------|------|
|
|
232
|
+
| 척도 | 0-3 (항목별) |
|
|
233
|
+
| PASS 기준 | **2.80 / 3.00 이상** |
|
|
234
|
+
| FAIL 기준 | 2.79 이하 (예외 없음) |
|
|
235
|
+
| Evidence 없는 항목 | Score = 0으로 강제 재계산 |
|
|
236
|
+
|
|
237
|
+
### Evaluator-Functional 채점 항목 (R1-R5)
|
|
238
|
+
|
|
239
|
+
| # | Criterion | Weight |
|
|
240
|
+
|---|-----------|--------|
|
|
241
|
+
| R1 | API Contract 준수 | 25% |
|
|
242
|
+
| R2 | Acceptance Criteria 전수 통과 | 25% |
|
|
243
|
+
| R3 | 부정 테스트 (엔드포인트당 2개+) | 20% |
|
|
244
|
+
| R4 | E2E 시나리오 (Playwright) | 15% |
|
|
245
|
+
| R5 | 에러 핸들링 & 엣지케이스 | 15% |
|
|
246
|
+
|
|
247
|
+
### Evaluator-Visual 채점 항목 (V1-V5)
|
|
248
|
+
|
|
249
|
+
| # | Criterion | Weight |
|
|
250
|
+
|---|-----------|--------|
|
|
251
|
+
| V1 | 레이아웃 정확성 | 20% |
|
|
252
|
+
| V2 | 반응형 (375/768/1280px) | 20% |
|
|
253
|
+
| V3 | 접근성 WCAG 2.1 AA | 20% |
|
|
254
|
+
| V4 | 시각적 일관성 + AI슬롭 감지 | 20% |
|
|
255
|
+
| V5 | 인터랙션 상태 (로딩/에러/빈/호버/포커스) | 20% |
|
|
256
|
+
|
|
257
|
+
### 자동 FAIL 조건 (Verdict Rules)
|
|
258
|
+
|
|
259
|
+
어떤 상황에서도 아래 조건 충족 시 FAIL:
|
|
260
|
+
|
|
261
|
+
1. Weighted Score < 2.80
|
|
262
|
+
2. AC 100% 미통과 (부분 통과 불인정)
|
|
263
|
+
3. Regression 실패 1건 이상 (신규 점수 무관)
|
|
264
|
+
4. Evidence 누락 항목 존재 → 해당 Score = 0 재계산
|
|
265
|
+
5. Cross-Validation 불일치 1건 이상 → CONDITIONAL FAIL
|
|
266
|
+
6. (Visual) a11y Critical/Serious 위반 1건 이상 → V3 = 0
|
|
267
|
+
7. (Visual) AI Slop 2건 이상 → V4 최대 1점
|
|
268
|
+
|
|
269
|
+
### Executable Acceptance Criteria
|
|
270
|
+
|
|
271
|
+
Planner는 feature-list.json에 기능을 정의할 때 **실행 가능한 검증 조건(AC)**을 반드시 작성합니다:
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
{
|
|
275
|
+
"id": "AC-001",
|
|
276
|
+
"description": "유효한 이메일로 가입 시 201 응답",
|
|
277
|
+
"type": "api",
|
|
278
|
+
"verify": {
|
|
279
|
+
"method": "POST",
|
|
280
|
+
"path": "/api/auth/register",
|
|
281
|
+
"body": { "email": "test@test.com", "password": "Test1234!" },
|
|
282
|
+
"expect": { "status": 201 }
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
AC 타입: `api` (HTTP 요청), `visual` (UI 요소 존재), `e2e` (사용자 플로우)
|
|
288
|
+
|
|
289
|
+
### Regression Checkpoint
|
|
290
|
+
|
|
291
|
+
Sprint N의 Evaluator는 이전 Sprint에서 PASS된 AC를 재검증합니다:
|
|
292
|
+
- archive에서 이전 feature-list.json의 passed AC를 로드
|
|
293
|
+
- handoff.json의 `regression` 필드로 전달
|
|
294
|
+
- **1건이라도 회귀 실패하면 전체 FAIL**
|
|
295
|
+
|
|
296
|
+
### Cross-Validation
|
|
297
|
+
|
|
298
|
+
Eval-Functional의 결과를 Eval-Visual이 교차 검증합니다:
|
|
299
|
+
- evaluation-functional.md 내 JSON 블록 → handoff.json의 `cross_validation_from_functional`
|
|
300
|
+
- API 성공인데 UI에 에러 표시 = 불일치 = FAIL 사유
|
|
301
|
+
|
|
302
|
+
### Adversarial Rules (적대적 행동 규칙)
|
|
303
|
+
|
|
304
|
+
Evaluator 에이전트에게 강제되는 행동 규칙:
|
|
305
|
+
- Generator의 '완료' 주장을 신뢰하지 않고 직접 검증
|
|
306
|
+
- 정상 1개당 비정상 2개 이상 테스트
|
|
307
|
+
- PASS 전 자문: "내가 이 코드로 PR을 올리겠는가?"
|
|
308
|
+
- '전반적으로 잘 되었습니다' 류의 모호한 긍정 평가 **금지**
|
|
309
|
+
- '시간 제약상 일부만 테스트' **금지** — 전수 불가 시 FAIL 처리
|
|
310
|
+
|
|
137
311
|
## Tech Stack
|
|
138
312
|
|
|
139
313
|
| 영역 | 기술 |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": {
|
|
3
3
|
"name": "6-Agent Production Harness",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.2.0",
|
|
5
5
|
"description": "Dispatcher + NestJS MSA + React/Next.js + Playwright 기반 실무 하네스",
|
|
6
6
|
"source": "https://www.anthropic.com/engineering/harness-design-long-running-apps"
|
|
7
7
|
},
|
|
@@ -13,12 +13,25 @@
|
|
|
13
13
|
"always_first": true
|
|
14
14
|
},
|
|
15
15
|
"brainstorming": {
|
|
16
|
-
"role": "사용자의 러프한 요구사항을 대화형으로 구체화하여 Planner가 바로 쓸 수 있는 brainstorm-spec.md로 변환.
|
|
16
|
+
"role": "사용자의 러프한 요구사항을 대화형으로 구체화하여 Planner가 바로 쓸 수 있는 brainstorm-spec.md로 변환.",
|
|
17
17
|
"skill": "harness-brainstorming",
|
|
18
18
|
"inputs": ["(user conversation)"],
|
|
19
19
|
"outputs": ["actions/brainstorm-spec.md"],
|
|
20
20
|
"conditional": true,
|
|
21
|
-
"
|
|
21
|
+
"trigger_conditions": {
|
|
22
|
+
"comment": "Dispatcher가 아래 조건 중 하나 이상 충족 시 사용자에게 브레인스토밍 제안",
|
|
23
|
+
"rules": [
|
|
24
|
+
"사용자 요청에 구체적 기능명/엔드포인트/화면명이 없고 추상적 목표만 있을 때",
|
|
25
|
+
"PRD/OpenAPI 등 구조화된 입력 문서가 없을 때",
|
|
26
|
+
"'뭔가 만들고 싶은데', '아이디어가 있는데' 등 탐색적 표현이 포함될 때"
|
|
27
|
+
],
|
|
28
|
+
"skip_when": [
|
|
29
|
+
"사용자가 PRD, OpenAPI spec, 또는 구체적 feature list를 제공한 경우",
|
|
30
|
+
"사용자가 '브레인스토밍 없이', 'skip brainstorming'을 명시한 경우",
|
|
31
|
+
"이미 brainstorm-spec.md가 actions/에 존재하는 경우"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"invoked_by": "dispatcher (user opt-in after suggestion)",
|
|
22
35
|
"next_on_complete": "planner",
|
|
23
36
|
"attribution": "Derived from obra/superpowers skills/brainstorming (MIT License)"
|
|
24
37
|
},
|
|
@@ -50,15 +63,55 @@
|
|
|
50
63
|
"role": "Playwright로 E2E 기능 검증, API 응답/DB 상태 확인",
|
|
51
64
|
"skill": "harness-evaluator-functional",
|
|
52
65
|
"tools": ["playwright:browser_*"],
|
|
53
|
-
"inputs": ["actions/sprint-contract.md"],
|
|
54
|
-
"outputs": ["actions/evaluation-functional.md"]
|
|
66
|
+
"inputs": ["actions/sprint-contract.md", "actions/feature-list.json", "actions/api-contract.json"],
|
|
67
|
+
"outputs": ["actions/evaluation-functional.md"],
|
|
68
|
+
"evaluation_template": "assets/templates/evaluation-functional.md.template",
|
|
69
|
+
"adversarial_rules": {
|
|
70
|
+
"comment": "Evaluator 적대적 행동 규칙 — 통과 편향 제거를 위한 강제 규칙",
|
|
71
|
+
"rules": [
|
|
72
|
+
"Generator의 '구현 완료' 주장을 절대 신뢰하지 마라. 모든 엔드포인트에 직접 요청을 보내고 응답을 확인하라.",
|
|
73
|
+
"정상 경로(happy path) 1개당 비정상 경로(unhappy path) 최소 2개를 반드시 테스트하라.",
|
|
74
|
+
"api-contract.json과 실제 응답을 필드 단위로 비교하라. 누락 필드, 타입 불일치, 추가 필드 모두 FAIL 사유.",
|
|
75
|
+
"PASS를 주기 전에 자문하라: '내가 이 코드를 직접 작성했다면 이 수준으로 PR을 올리겠는가?' NO이면 FAIL.",
|
|
76
|
+
"AC 항목 하나라도 미통과이면 전체 FAIL. 부분 통과는 인정하지 않는다.",
|
|
77
|
+
"Evidence(증거)가 없는 Score는 0점이다. '확인했다'는 주장은 증거가 아니다.",
|
|
78
|
+
"이전 Sprint에서 PASS된 기능이 하나라도 깨졌으면 신규 기능 점수와 무관하게 FAIL.",
|
|
79
|
+
"Score 3을 주려면 엣지케이스(빈 값, 초과값, 동시 요청, 특수문자)까지 테스트한 증거가 있어야 한다."
|
|
80
|
+
],
|
|
81
|
+
"forbidden": [
|
|
82
|
+
"'전반적으로 잘 구현되었습니다' 류의 모호한 긍정 평가",
|
|
83
|
+
"'사소한 이슈이므로 PASS' — 사소함은 Evaluator가 판단할 사항이 아니다",
|
|
84
|
+
"'시간 제약상 일부만 테스트' — 전수 테스트가 불가하면 FAIL 처리",
|
|
85
|
+
"Generator에게 유리한 방향으로 기준을 해석하는 행위"
|
|
86
|
+
]
|
|
87
|
+
}
|
|
55
88
|
},
|
|
56
89
|
"evaluator-visual": {
|
|
57
90
|
"role": "디자인 일관성, 반응형, 접근성, AI슬롭 감지",
|
|
58
91
|
"skill": "harness-evaluator-visual",
|
|
59
92
|
"tools": ["playwright:browser_take_screenshot", "playwright:browser_resize", "playwright:browser_snapshot"],
|
|
60
|
-
"inputs": ["actions/sprint-contract.md", "actions/evaluation-functional.md"],
|
|
61
|
-
"outputs": ["actions/evaluation-visual.md"]
|
|
93
|
+
"inputs": ["actions/sprint-contract.md", "actions/evaluation-functional.md", "actions/feature-list.json"],
|
|
94
|
+
"outputs": ["actions/evaluation-visual.md"],
|
|
95
|
+
"evaluation_template": "assets/templates/evaluation-visual.md.template",
|
|
96
|
+
"adversarial_rules": {
|
|
97
|
+
"comment": "Visual Evaluator 적대적 행동 규칙",
|
|
98
|
+
"rules": [
|
|
99
|
+
"모든 페이지를 반드시 3개 뷰포트(375px, 768px, 1280px)에서 스크린샷 촬영하라. 1개라도 누락이면 해당 페이지 Score 산정 불가.",
|
|
100
|
+
"axe-core 접근성 검사를 실제로 실행하라. Critical 또는 Serious 위반이 1건이라도 있으면 V3 = 0.",
|
|
101
|
+
"AI슬롭 패턴(무의미한 그라데이션, 과도한 그림자, 장식 아이콘 남발)을 적극 탐지하라. 2건 이상이면 V4 최대 1점.",
|
|
102
|
+
"evaluation-functional.md의 Cross-Validation Data와 교차 검증하라. API 성공인데 UI에 에러 표시 = 불일치 = FAIL 사유.",
|
|
103
|
+
"이전 Sprint 스크린샷 대비 의도치 않은 시각 변경이 있으면 점수 무관 FAIL.",
|
|
104
|
+
"PASS를 주기 전에 자문하라: '이 UI를 실사용자에게 보여줄 수 있는가?' NO이면 FAIL.",
|
|
105
|
+
"로딩/에러/빈 상태가 하나라도 미구현이면 V5 최대 1점.",
|
|
106
|
+
"Evidence(스크린샷 경로 또는 DOM 스냅샷)가 없는 Score는 0점이다."
|
|
107
|
+
],
|
|
108
|
+
"forbidden": [
|
|
109
|
+
"'디자인이 깔끔합니다' 류의 주관적 긍정 평가",
|
|
110
|
+
"'접근성은 추후 개선' — 접근성은 기본 요건이지 부가 기능이 아니다",
|
|
111
|
+
"'모바일 뷰포트는 생략' — 3개 뷰포트 전수 검사는 필수",
|
|
112
|
+
"스크린샷 없이 '확인했다'고 주장하는 행위"
|
|
113
|
+
]
|
|
114
|
+
}
|
|
62
115
|
},
|
|
63
116
|
"generator-frontend-flutter": {
|
|
64
117
|
"role": "Flutter 앱 개발 — Riverpod, integrated_data_layer(Retrofit), i18n(ARB), build_runner",
|
|
@@ -87,11 +140,30 @@
|
|
|
87
140
|
"BE-ONLY": ["planner", "generator-backend", "evaluator-functional:api-only"]
|
|
88
141
|
},
|
|
89
142
|
"fe_stack_substitution": {
|
|
90
|
-
"comment": "pipeline.json.fe_stack
|
|
143
|
+
"comment": "pipeline.json.fe_stack + fe_target 에 따라 FE 에이전트 치환. Flutter Web 은 React 와 동일한 Playwright 기반 evaluator 를 사용한다.",
|
|
91
144
|
"flutter": {
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
|
|
145
|
+
"_doc": "fe_target = web | mobile | desktop. by_target 으로 분기.",
|
|
146
|
+
"by_target": {
|
|
147
|
+
"web": {
|
|
148
|
+
"_doc": "Flutter Web — 컴파일 결과가 HTML+JS+CSS 이므로 Playwright evaluator 사용 가능",
|
|
149
|
+
"generator-frontend": "generator-frontend-flutter",
|
|
150
|
+
"evaluator-functional": "evaluator-functional",
|
|
151
|
+
"evaluator-visual": "evaluator-visual"
|
|
152
|
+
},
|
|
153
|
+
"mobile": {
|
|
154
|
+
"_doc": "Flutter Mobile (Android/iOS) — 브라우저 없음, 정적 분석 evaluator 사용",
|
|
155
|
+
"generator-frontend": "generator-frontend-flutter",
|
|
156
|
+
"evaluator-functional": "evaluator-functional-flutter",
|
|
157
|
+
"evaluator-visual": "__skip__"
|
|
158
|
+
},
|
|
159
|
+
"desktop": {
|
|
160
|
+
"_doc": "Flutter Desktop (macOS/Windows/Linux) — 브라우저 없음, 정적 분석 evaluator 사용",
|
|
161
|
+
"generator-frontend": "generator-frontend-flutter",
|
|
162
|
+
"evaluator-functional": "evaluator-functional-flutter",
|
|
163
|
+
"evaluator-visual": "__skip__"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"_default_target": "mobile"
|
|
95
167
|
}
|
|
96
168
|
}
|
|
97
169
|
},
|
|
@@ -101,14 +173,133 @@
|
|
|
101
173
|
"reason": "프론트가 존재하지 않는 API를 호출하는 실패 방지"
|
|
102
174
|
},
|
|
103
175
|
"retry_on_fail": true,
|
|
104
|
-
"max_retries_per_sprint":
|
|
105
|
-
"
|
|
176
|
+
"max_retries_per_sprint": 5,
|
|
177
|
+
"escalate_to_planner_after": 3,
|
|
178
|
+
"archive_on_sprint_complete": true,
|
|
179
|
+
"pre_eval_gate": {
|
|
180
|
+
"comment": "Generator → Evaluator 전환 전 결정론적 검증. 실패 시 Evaluator 세션을 열지 않고 Generator로 자동 리라우팅.",
|
|
181
|
+
"enabled": true,
|
|
182
|
+
"backend_checks": ["npx tsc --noEmit", "npx eslint . --max-warnings=0", "npx jest --bail --passWithNoTests"],
|
|
183
|
+
"frontend_checks": ["npx tsc --noEmit", "npx eslint . --max-warnings=0", "npx vitest run --bail 1"],
|
|
184
|
+
"timeout_seconds": 120,
|
|
185
|
+
"on_fail": "reroute_to_generator"
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
"evaluation": {
|
|
189
|
+
"comment": "Evaluator 공통 설정",
|
|
190
|
+
"scoring": {
|
|
191
|
+
"scale": 3,
|
|
192
|
+
"pass_threshold": 2.80,
|
|
193
|
+
"fail_threshold": 2.79,
|
|
194
|
+
"verdict_rules": [
|
|
195
|
+
"Weighted Score < 2.80 → FAIL",
|
|
196
|
+
"AC Pass Rate < 100% → FAIL (부분 통과 불인정)",
|
|
197
|
+
"Regression Failures > 0 → FAIL (신규 점수 무관)",
|
|
198
|
+
"Evidence 누락 항목 → 해당 Score = 0으로 재계산",
|
|
199
|
+
"Cross-Validation 불일치 > 0 → CONDITIONAL FAIL"
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
"regression": {
|
|
203
|
+
"enabled": true,
|
|
204
|
+
"source": "archive",
|
|
205
|
+
"scope": "all_passed_features",
|
|
206
|
+
"comment": "이전 Sprint에서 PASS된 모든 AC를 현재 Sprint에서 재검증. 1건이라도 실패하면 전체 FAIL."
|
|
207
|
+
},
|
|
208
|
+
"cross_validation": {
|
|
209
|
+
"enabled": true,
|
|
210
|
+
"comment": "evaluator-functional과 evaluator-visual이 서로의 결과를 참조하여 불일치를 감지. evaluation-*.md의 Cross-Validation Data JSON 블록을 기계적으로 파싱."
|
|
211
|
+
},
|
|
212
|
+
"acceptance_criteria_schema": {
|
|
213
|
+
"comment": "Planner가 feature-list.json에 기능 정의 시 반드시 작성해야 하는 실행 가능한 검증 조건 스키마. Evaluator는 이 조건을 기계적으로 실행한다.",
|
|
214
|
+
"required_fields": ["id", "description", "type", "verify"],
|
|
215
|
+
"types": {
|
|
216
|
+
"api": {
|
|
217
|
+
"verify_fields": ["method", "path", "body", "expect"],
|
|
218
|
+
"expect_fields": ["status"],
|
|
219
|
+
"optional_expect": ["body_contains", "body_schema", "headers"]
|
|
220
|
+
},
|
|
221
|
+
"visual": {
|
|
222
|
+
"verify_fields": ["url", "elements"],
|
|
223
|
+
"optional_verify": ["viewport", "state", "screenshot_match"]
|
|
224
|
+
},
|
|
225
|
+
"e2e": {
|
|
226
|
+
"verify_fields": ["steps"],
|
|
227
|
+
"step_fields": ["action", "target", "value", "expect"]
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
"example": {
|
|
231
|
+
"id": "AC-001",
|
|
232
|
+
"description": "유효한 이메일로 가입 시 201 응답",
|
|
233
|
+
"type": "api",
|
|
234
|
+
"verify": {
|
|
235
|
+
"method": "POST",
|
|
236
|
+
"path": "/api/auth/register",
|
|
237
|
+
"body": { "email": "test-{{timestamp}}@test.com", "password": "Test1234!" },
|
|
238
|
+
"expect": { "status": 201, "body_contains": { "id": "string", "email": "string" } }
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
"negative_example": {
|
|
242
|
+
"id": "AC-002",
|
|
243
|
+
"description": "중복 이메일 시 409 응답",
|
|
244
|
+
"type": "api",
|
|
245
|
+
"verify": {
|
|
246
|
+
"method": "POST",
|
|
247
|
+
"path": "/api/auth/register",
|
|
248
|
+
"body": { "email": "duplicate@test.com", "password": "Test1234!" },
|
|
249
|
+
"expect": { "status": 409 }
|
|
250
|
+
},
|
|
251
|
+
"is_negative": true
|
|
252
|
+
},
|
|
253
|
+
"visual_example": {
|
|
254
|
+
"id": "AC-003",
|
|
255
|
+
"description": "회원가입 폼 렌더링 확인",
|
|
256
|
+
"type": "visual",
|
|
257
|
+
"verify": {
|
|
258
|
+
"url": "/register",
|
|
259
|
+
"elements": ["input[name=email]", "input[name=password]", "button[type=submit]"],
|
|
260
|
+
"viewport": [375, 768, 1280]
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
"artifacts": {
|
|
266
|
+
"comment": "아티팩트 상태 머신. 각 에이전트 실행 전 선행 아티팩트가 required_status 이상인지 검증.",
|
|
267
|
+
"states": ["pending", "draft", "reviewed", "approved"],
|
|
268
|
+
"prerequisites": {
|
|
269
|
+
"generator-backend": {
|
|
270
|
+
"plan.md": "draft",
|
|
271
|
+
"api-contract.json": "draft",
|
|
272
|
+
"feature-list.json": "draft"
|
|
273
|
+
},
|
|
274
|
+
"generator-frontend": {
|
|
275
|
+
"plan.md": "draft",
|
|
276
|
+
"api-contract.json": "draft",
|
|
277
|
+
"feature-list.json": "draft"
|
|
278
|
+
},
|
|
279
|
+
"evaluator-functional": {
|
|
280
|
+
"sprint-contract.md": "draft"
|
|
281
|
+
},
|
|
282
|
+
"evaluator-visual": {
|
|
283
|
+
"sprint-contract.md": "draft"
|
|
284
|
+
}
|
|
285
|
+
}
|
|
106
286
|
},
|
|
107
287
|
"session": {
|
|
108
288
|
"isolation": true,
|
|
109
289
|
"state_file": ".harness/progress.json",
|
|
110
290
|
"state_log": ".harness/progress.log",
|
|
111
|
-
"next_prompt_file": ".harness/next-prompt.txt"
|
|
291
|
+
"next_prompt_file": ".harness/next-prompt.txt",
|
|
292
|
+
"handoff_file": ".harness/handoff.json",
|
|
293
|
+
"context_guard": {
|
|
294
|
+
"comment": "컨텍스트 분리 하드 가드레일. UserPromptSubmit 훅이 위반을 감지하면 경고를 주입한다.",
|
|
295
|
+
"enabled": true,
|
|
296
|
+
"rules": [
|
|
297
|
+
"current_agent가 running 상태일 때 다른 에이전트 스킬 호출 금지",
|
|
298
|
+
"한 세션에서 2개 이상의 에이전트 스킬 실행 금지",
|
|
299
|
+
"agent_status를 completed로 변경하지 않고 다음 에이전트를 호출하면 경고"
|
|
300
|
+
],
|
|
301
|
+
"on_violation": "warn_and_block"
|
|
302
|
+
}
|
|
112
303
|
},
|
|
113
304
|
"behavior": {
|
|
114
305
|
"comment": "하네스 동작 플래그. UserPromptSubmit 훅이 이 값을 읽어 자동 라우팅을 결정한다.",
|