@walwal-harness/cli 2.5.0 → 3.2.1
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/assets/templates/AGENTS.md.template +31 -0
- package/assets/templates/HARNESS.md +180 -6
- package/assets/templates/config.json +182 -10
- 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/bin/init.js +45 -6
- package/package.json +5 -2
- package/scripts/harness-next.sh +254 -1
- 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
|
@@ -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",
|
|
@@ -120,14 +173,133 @@
|
|
|
120
173
|
"reason": "프론트가 존재하지 않는 API를 호출하는 실패 방지"
|
|
121
174
|
},
|
|
122
175
|
"retry_on_fail": true,
|
|
123
|
-
"max_retries_per_sprint":
|
|
124
|
-
"
|
|
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
|
+
}
|
|
125
286
|
},
|
|
126
287
|
"session": {
|
|
127
288
|
"isolation": true,
|
|
128
289
|
"state_file": ".harness/progress.json",
|
|
129
290
|
"state_log": ".harness/progress.log",
|
|
130
|
-
"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
|
+
}
|
|
131
303
|
},
|
|
132
304
|
"behavior": {
|
|
133
305
|
"comment": "하네스 동작 플래그. UserPromptSubmit 훅이 이 값을 읽어 자동 라우팅을 결정한다.",
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Evaluation — Functional (Sprint {{SPRINT}})
|
|
2
|
+
|
|
3
|
+
> Generated by: evaluator-functional
|
|
4
|
+
> Date: {{DATE}}
|
|
5
|
+
> Verdict: **PENDING**
|
|
6
|
+
|
|
7
|
+
## 1. Scoring Rubric
|
|
8
|
+
|
|
9
|
+
> **Threshold: 2.80 / 3.00 = PASS**
|
|
10
|
+
> Score 2.79 이하는 이유를 불문하고 FAIL.
|
|
11
|
+
> 모든 항목에 Evidence(근거)를 반드시 기입. Evidence 없는 Score는 0점 처리.
|
|
12
|
+
|
|
13
|
+
| # | Criterion | Weight | Score (0-3) | Evidence |
|
|
14
|
+
|---|-----------|--------|-------------|----------|
|
|
15
|
+
| R1 | API Contract 준수 — 모든 엔드포인트가 api-contract.json과 일치 (method, path, request/response schema, status codes) | 25% | | |
|
|
16
|
+
| R2 | Acceptance Criteria 통과 — feature-list.json의 모든 AC 항목 실행 결과 | 25% | | |
|
|
17
|
+
| R3 | 부정 테스트 (Negative Testing) — 각 엔드포인트별 최소 2개 비정상 시나리오 (빈 입력, 잘못된 타입, 미인증, 권한 없음, 중복, 초과값) | 20% | | |
|
|
18
|
+
| R4 | E2E 시나리오 통과 — Playwright로 사용자 플로우 전체 재현 (회원가입→로그인→핵심기능→로그아웃 등) | 15% | | |
|
|
19
|
+
| R5 | 에러 핸들링 & 엣지케이스 — 서버 에러 응답 형식 일관성, DB 제약조건 위반 처리, 동시성 충돌 | 15% | | |
|
|
20
|
+
|
|
21
|
+
### Scoring Guide (엄격 적용)
|
|
22
|
+
|
|
23
|
+
| Score | 의미 | 기준 |
|
|
24
|
+
|-------|------|------|
|
|
25
|
+
| 3 | 완벽 | 모든 케이스 통과, 추가 검증 불필요, 엣지케이스까지 커버 |
|
|
26
|
+
| 2 | 충족 | 핵심 기능 동작하나 경미한 이슈 1-2건 존재 (로그 누락, 응답 필드 오타 등) |
|
|
27
|
+
| 1 | 부분 충족 | 핵심 기능은 동작하나 명확한 수정 필요 (잘못된 status code, 누락된 validation 등) |
|
|
28
|
+
| 0 | 미충족 | 미구현, 서버 에러, 또는 contract 불일치 |
|
|
29
|
+
|
|
30
|
+
### Anti-Rubber-Stamping Check
|
|
31
|
+
|
|
32
|
+
Score를 기입하기 전에 반드시 아래 질문에 답하라:
|
|
33
|
+
- [ ] "이 엔드포인트에 실제로 요청을 보내고 응답을 확인했는가?" (추측 = 0점)
|
|
34
|
+
- [ ] "정상 케이스뿐 아니라 비정상 케이스도 테스트했는가?" (정상만 = Score 최대 2)
|
|
35
|
+
- [ ] "api-contract.json과 실제 응답을 문자열 수준으로 비교했는가?"
|
|
36
|
+
|
|
37
|
+
## 2. Acceptance Criteria Checklist
|
|
38
|
+
|
|
39
|
+
> feature-list.json의 acceptance_criteria를 기계적으로 실행한 결과.
|
|
40
|
+
> 각 AC에 대해 PASS/FAIL + 실제 결과를 기록.
|
|
41
|
+
|
|
42
|
+
| Feature | AC ID | Description | Type | Expected | Actual | Result |
|
|
43
|
+
|---------|-------|-------------|------|----------|--------|--------|
|
|
44
|
+
| | | | | | | |
|
|
45
|
+
|
|
46
|
+
**AC Summary**: 0/0 passed (0%)
|
|
47
|
+
**AC 전체 통과가 아니면 R2는 최대 Score 1**
|
|
48
|
+
|
|
49
|
+
## 3. Negative Test Results
|
|
50
|
+
|
|
51
|
+
> 각 엔드포인트별 최소 2개 부정 테스트. 미실시 엔드포인트가 있으면 R3 = 0.
|
|
52
|
+
|
|
53
|
+
| Endpoint | Test Case | Expected | Actual | Result |
|
|
54
|
+
|----------|-----------|----------|--------|--------|
|
|
55
|
+
| | | | | |
|
|
56
|
+
|
|
57
|
+
## 4. E2E Scenario Log
|
|
58
|
+
|
|
59
|
+
> Playwright 실행 로그. 스크린샷 경로 포함.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
(Playwright 실행 결과를 여기에 붙여넣기)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 5. Regression Check
|
|
66
|
+
|
|
67
|
+
> 이전 Sprint에서 PASS된 기능 재검증 결과.
|
|
68
|
+
> Sprint 1이면 "N/A - 첫 스프린트"로 기입.
|
|
69
|
+
|
|
70
|
+
| Sprint | Feature | AC ID | Result | Notes |
|
|
71
|
+
|--------|---------|-------|--------|-------|
|
|
72
|
+
| | | | | |
|
|
73
|
+
|
|
74
|
+
**Regression Summary**: 0/0 passed
|
|
75
|
+
**회귀 실패가 1건이라도 있으면 전체 Verdict = FAIL (신규 기능 점수 무관)**
|
|
76
|
+
|
|
77
|
+
## 6. Cross-Validation Data
|
|
78
|
+
|
|
79
|
+
> evaluator-visual이 참조할 수 있도록 기계 판독 가능한 결과 요약.
|
|
80
|
+
> 이 섹션은 반드시 아래 JSON 코드블록 형식을 유지할 것.
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"evaluator": "functional",
|
|
85
|
+
"sprint": {{SPRINT}},
|
|
86
|
+
"verdict": "PENDING",
|
|
87
|
+
"total_score": 0.00,
|
|
88
|
+
"threshold": 2.80,
|
|
89
|
+
"criteria_scores": {
|
|
90
|
+
"R1_api_contract": 0,
|
|
91
|
+
"R2_acceptance_criteria": 0,
|
|
92
|
+
"R3_negative_testing": 0,
|
|
93
|
+
"R4_e2e_scenario": 0,
|
|
94
|
+
"R5_error_handling": 0
|
|
95
|
+
},
|
|
96
|
+
"ac_pass_rate": 0.0,
|
|
97
|
+
"negative_test_count": 0,
|
|
98
|
+
"regression_failures": 0,
|
|
99
|
+
"endpoints_tested": [],
|
|
100
|
+
"pages_tested": []
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 7. Final Verdict
|
|
105
|
+
|
|
106
|
+
| Metric | Value |
|
|
107
|
+
|--------|-------|
|
|
108
|
+
| Weighted Score | 0.00 / 3.00 |
|
|
109
|
+
| Threshold | 2.80 |
|
|
110
|
+
| AC Pass Rate | 0% |
|
|
111
|
+
| Regression Failures | 0 |
|
|
112
|
+
| **Verdict** | **PENDING** |
|
|
113
|
+
|
|
114
|
+
### Verdict Rules (위반 불가)
|
|
115
|
+
1. Weighted Score < 2.80 → **FAIL**
|
|
116
|
+
2. AC Pass Rate < 100% → **FAIL** (부분 통과 불인정)
|
|
117
|
+
3. Regression Failures > 0 → **FAIL** (신규 점수 무관)
|
|
118
|
+
4. Evidence 누락 항목 존재 → 해당 항목 Score = 0으로 재계산
|
|
119
|
+
5. 부정 테스트 미실시 엔드포인트 존재 → R3 = 0으로 재계산
|