@walwal-harness/cli 3.3.2 → 3.5.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/assets/templates/CONVENTIONS.md +61 -0
- package/assets/templates/HARNESS.md +35 -3
- package/assets/templates/config.json +25 -0
- package/bin/init.js +8 -0
- package/package.json +1 -1
- package/scripts/harness-next.sh +21 -3
- package/scripts/harness-session-start.sh +17 -10
- package/scripts/lib/harness-audit.sh +113 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# CONVENTIONS.md — Project Conventions
|
|
2
|
+
|
|
3
|
+
> 이 파일은 프로젝트의 코딩 컨벤션, 선호 패턴, 금지 패턴을 자유 형식으로 정의합니다.
|
|
4
|
+
> 모든 에이전트는 세션 시작 시 이 파일을 읽고 규칙을 적용합니다.
|
|
5
|
+
>
|
|
6
|
+
> **진실의 원천**: 이 파일이 유일한 컨벤션 정의 소스입니다.
|
|
7
|
+
> Gotcha나 AGENTS.md에 복사하지 마세요 — 에이전트가 직접 이 파일을 참조합니다.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
<!-- 아키텍처 수준의 규칙을 여기에 작성하세요 -->
|
|
12
|
+
<!-- 예시:
|
|
13
|
+
- 서비스 간 직접 DB 접근 금지 — 반드시 API 또는 메시지 패턴 사용
|
|
14
|
+
- 모노리스보다 모듈러 아키텍처 선호
|
|
15
|
+
-->
|
|
16
|
+
|
|
17
|
+
## Code Style
|
|
18
|
+
|
|
19
|
+
<!-- 코드 스타일 규칙을 여기에 작성하세요 -->
|
|
20
|
+
<!-- 예시:
|
|
21
|
+
- any 타입 사용 금지
|
|
22
|
+
- console.log 대신 structured logger 사용
|
|
23
|
+
- 함수는 30줄 이내, 파일은 300줄 이내
|
|
24
|
+
-->
|
|
25
|
+
|
|
26
|
+
## Preferred Patterns
|
|
27
|
+
|
|
28
|
+
<!-- 선호하는 라이브러리, 패턴, 접근 방식 -->
|
|
29
|
+
<!-- 예시:
|
|
30
|
+
- 상태관리: Zustand
|
|
31
|
+
- API 호출: TanStack Query
|
|
32
|
+
- 폼 처리: React Hook Form + Zod
|
|
33
|
+
-->
|
|
34
|
+
|
|
35
|
+
## Avoid
|
|
36
|
+
|
|
37
|
+
<!-- 사용을 피해야 하는 패턴, 라이브러리, 접근 방식 -->
|
|
38
|
+
<!-- 예시:
|
|
39
|
+
- CSS-in-JS (styled-components, emotion)
|
|
40
|
+
- Class components
|
|
41
|
+
- Redux (Zustand으로 대체)
|
|
42
|
+
-->
|
|
43
|
+
|
|
44
|
+
## Testing
|
|
45
|
+
|
|
46
|
+
<!-- 테스트 관련 규칙 -->
|
|
47
|
+
<!-- 예시:
|
|
48
|
+
- E2E 테스트는 핵심 사용자 플로우만
|
|
49
|
+
- 단위 테스트 커버리지 80% 이상
|
|
50
|
+
- mock 최소화, 실제 DB 사용 선호
|
|
51
|
+
-->
|
|
52
|
+
|
|
53
|
+
## Naming
|
|
54
|
+
|
|
55
|
+
<!-- 네이밍 컨벤션 -->
|
|
56
|
+
<!-- 예시:
|
|
57
|
+
- 컴포넌트: PascalCase
|
|
58
|
+
- 변수/함수: camelCase
|
|
59
|
+
- 파일: kebab-case
|
|
60
|
+
- DB 테이블: snake_case
|
|
61
|
+
-->
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
## 디렉토리 구조
|
|
8
8
|
|
|
9
9
|
```
|
|
10
|
+
CONVENTIONS.md # 프로젝트 컨벤션 (사용자 작성, 에이전트 읽기 전용)
|
|
10
11
|
.harness/
|
|
11
12
|
├── HARNESS.md # 이 파일
|
|
12
13
|
├── config.json # 하네스 설정
|
|
@@ -142,8 +143,39 @@ pending → draft → reviewed → approved
|
|
|
142
143
|
| 파일 | 역할 |
|
|
143
144
|
|------|------|
|
|
144
145
|
| `.harness/progress.json` | 기계 판독 상태 (현재 에이전트, 파이프라인, 실패 정보) |
|
|
145
|
-
| `.harness/progress.log` | 사람 판독 히스토리 (append-only) |
|
|
146
|
-
| `.harness/handoff.json` | 세션 전환 문서 (prompt, model, thinking_mode, artifacts
|
|
146
|
+
| `.harness/progress.log` | 사람 판독 히스토리 (append-only, 전체) |
|
|
147
|
+
| `.harness/handoff.json` | 세션 전환 문서 (prompt, model, thinking_mode, artifacts) |
|
|
148
|
+
| `.harness/actions/audit.log` | Sprint cycle 단위 실행 추적 (Planner→Eval 통과) |
|
|
149
|
+
|
|
150
|
+
### Audit Log
|
|
151
|
+
|
|
152
|
+
1 sprint cycle(Planner/Dispatcher 시작 → Eval 통과) 단위의 상세 실행 추적입니다.
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
# 예시:
|
|
156
|
+
TIMESTAMP | AGENT | ACTION | STATUS | TARGET | DETAIL
|
|
157
|
+
2026-04-09T14:30:00Z | planner | plan | start | plan.md | Sprint 1 설계 시작
|
|
158
|
+
2026-04-09T14:35:00Z | planner | plan | complete | api-contract.json | 3 endpoints, 2 services
|
|
159
|
+
2026-04-09T14:35:01Z | planner | handoff | complete | →gen-backend |
|
|
160
|
+
2026-04-09T14:36:00Z | system | gate | pass | pre-eval | gen-backend
|
|
161
|
+
2026-04-09T14:40:00Z | gen-backend | develop | start | apps/service-user/ | User CRUD 구현
|
|
162
|
+
2026-04-09T14:50:00Z | gen-backend | develop | complete | apps/service-user/ | 4 endpoints
|
|
163
|
+
2026-04-09T14:50:01Z | gen-backend | handoff | complete | →eval-functional |
|
|
164
|
+
2026-04-09T14:55:00Z | eval-func | review | start | POST /api/auth/register | AC-001~003 검증
|
|
165
|
+
2026-04-09T14:58:00Z | eval-func | review | fail | POST /api/auth/register | 409→400 contract 불일치
|
|
166
|
+
2026-04-09T14:58:01Z | eval-func | handoff | complete | →gen-backend | Re-Generate
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**라이프사이클**: 새 Planner/Dispatcher 사이클 시작 시 이전 로그는 archive로 이동, 새 로그 시작.
|
|
170
|
+
|
|
171
|
+
**에이전트 기록 의무**: 모든 에이전트는 세션 중 주요 작업의 시작/완료를 audit에 기록해야 합니다.
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# 에이전트 스킬에서 호출:
|
|
175
|
+
source scripts/lib/harness-audit.sh && init_audit .
|
|
176
|
+
audit_log "gen-backend" "develop" "start" "apps/service-user/" "User CRUD 구현"
|
|
177
|
+
audit_log "gen-backend" "develop" "complete" "apps/service-user/" "4 endpoints 완료"
|
|
178
|
+
```
|
|
147
179
|
|
|
148
180
|
### 실행 방법
|
|
149
181
|
|
|
@@ -190,7 +222,7 @@ jq . .harness/progress.json # 현재 상태 확인
|
|
|
190
222
|
|
|
191
223
|
모든 에이전트 스킬에 내장된 프로토콜:
|
|
192
224
|
|
|
193
|
-
- **On Start**: `progress.json` 읽기 → `agent_status: "running"` 설정 → `handoff.json` 참조
|
|
225
|
+
- **On Start**: `progress.json` 읽기 → `agent_status: "running"` 설정 → `handoff.json` 참조 → `CONVENTIONS.md` 읽기 (존재 시)
|
|
194
226
|
- **On Complete**: `progress.json` 업데이트 → 아티팩트 상태 갱신 → `next_agent` 계산 → **STOP**
|
|
195
227
|
- **On Fail** (Evaluator): `failure` 정보 기록 → `retry_target` 설정 → **STOP**
|
|
196
228
|
- **On Transition**: 파일 소유권 검증 → Pre-Eval Gate (해당 시) → 아티팩트 선행조건 검증
|
|
@@ -333,9 +333,34 @@
|
|
|
333
333
|
"actions": ".harness/actions",
|
|
334
334
|
"archive": ".harness/archive",
|
|
335
335
|
"gotchas": ".harness/gotchas",
|
|
336
|
+
"conventions": "CONVENTIONS.md",
|
|
336
337
|
"progress_state": ".harness/progress.json",
|
|
337
338
|
"progress_log": ".harness/progress.log"
|
|
338
339
|
},
|
|
340
|
+
"audit": {
|
|
341
|
+
"comment": "Audit log — 1 sprint cycle 단위의 실행 추적. Planner/Dispatcher 시작 시 리셋.",
|
|
342
|
+
"file": ".harness/actions/audit.log",
|
|
343
|
+
"lifecycle": "planner_to_eval_pass",
|
|
344
|
+
"reset_on": ["planner", "dispatcher"],
|
|
345
|
+
"archive_previous": true,
|
|
346
|
+
"format": "TIMESTAMP | AGENT | ACTION | STATUS | TARGET | DETAIL",
|
|
347
|
+
"actions": {
|
|
348
|
+
"develop": "코드 생성/수정 (Generator)",
|
|
349
|
+
"review": "검수/평가 (Evaluator)",
|
|
350
|
+
"handoff": "에이전트 전환",
|
|
351
|
+
"plan": "설계/계획 (Planner)",
|
|
352
|
+
"gate": "자동 게이트 체크 (system)",
|
|
353
|
+
"escalate": "에스컬레이션 (system)"
|
|
354
|
+
},
|
|
355
|
+
"statuses": ["start", "complete", "fail", "pass", "skip", "warn"]
|
|
356
|
+
},
|
|
357
|
+
"conventions": {
|
|
358
|
+
"comment": "CONVENTIONS.md — 사용자가 자유 형식으로 작성하는 프로젝트 컨벤션. 모든 에이전트가 세션 시작 시 직접 읽음. Gotcha/AGENTS.md에 복사하지 않음.",
|
|
359
|
+
"file": "CONVENTIONS.md",
|
|
360
|
+
"read_by": "all_agents",
|
|
361
|
+
"write_by": "user_only",
|
|
362
|
+
"read_timing": "session_start"
|
|
363
|
+
},
|
|
339
364
|
"recommended_skills": {
|
|
340
365
|
"comment": "Claude Code에 설치하면 하네스 품질이 향상되는 외부 스킬 목록",
|
|
341
366
|
"vercel": {
|
package/bin/init.js
CHANGED
|
@@ -148,6 +148,14 @@ function scaffoldHarness() {
|
|
|
148
148
|
copyFile(memorySrc, memoryDest);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
// Copy CONVENTIONS.md to project root
|
|
152
|
+
const conventionsSrc = path.join(PKG_ROOT, 'assets', 'templates', 'CONVENTIONS.md');
|
|
153
|
+
const conventionsDest = path.join(PROJECT_ROOT, 'CONVENTIONS.md');
|
|
154
|
+
if (fs.existsSync(conventionsSrc) && (!fileExists(conventionsDest) || isForce)) {
|
|
155
|
+
copyFile(conventionsSrc, conventionsDest);
|
|
156
|
+
log('CONVENTIONS.md created — edit to define your project conventions');
|
|
157
|
+
}
|
|
158
|
+
|
|
151
159
|
// Create progress.log
|
|
152
160
|
const progressLog = path.join(HARNESS_DIR, 'progress.log');
|
|
153
161
|
if (!fileExists(progressLog) || isForce) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Production harness for AI agent engineering — Planner, Generator(BE/FE), Evaluator(Func/Visual), optional Brainstormer (requirements refinement). Supports React and Flutter FE stacks.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"walwal-harness": "bin/init.js"
|
package/scripts/harness-next.sh
CHANGED
|
@@ -7,6 +7,7 @@ set -e
|
|
|
7
7
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
8
|
source "$SCRIPT_DIR/lib/harness-render-progress.sh"
|
|
9
9
|
source "$SCRIPT_DIR/lib/harness-guardrail.sh"
|
|
10
|
+
source "$SCRIPT_DIR/lib/harness-audit.sh"
|
|
10
11
|
|
|
11
12
|
# ─────────────────────────────────────────
|
|
12
13
|
# Resolve project root
|
|
@@ -24,6 +25,9 @@ HANDOFF="$PROJECT_ROOT/.harness/handoff.json"
|
|
|
24
25
|
|
|
25
26
|
check_jq || exit 1
|
|
26
27
|
|
|
28
|
+
# Initialize audit log
|
|
29
|
+
init_audit "$PROJECT_ROOT"
|
|
30
|
+
|
|
27
31
|
if [ ! -f "$PROGRESS" ]; then
|
|
28
32
|
echo "[harness] ERROR: progress.json not found" >&2
|
|
29
33
|
echo " Run 'npx walwal-harness --force' to reinitialize." >&2
|
|
@@ -277,15 +281,25 @@ verify_artifact_prerequisites() {
|
|
|
277
281
|
# Runtime Guardrail — 파일 소유권 검증
|
|
278
282
|
# ─────────────────────────────────────────
|
|
279
283
|
if [ "$agent_status" = "completed" ]; then
|
|
280
|
-
|
|
284
|
+
audit_log "$current_agent" "develop" "complete" "" "agent completed"
|
|
285
|
+
verify_file_ownership "$PROJECT_ROOT" || audit_gate "file-ownership" "warn" "boundary violation detected"
|
|
281
286
|
fi
|
|
282
287
|
|
|
283
288
|
# ─────────────────────────────────────────
|
|
284
289
|
# Run Pre-Eval Gate (if applicable)
|
|
285
290
|
# ─────────────────────────────────────────
|
|
286
291
|
if [ "$agent_status" = "completed" ]; then
|
|
287
|
-
|
|
288
|
-
|
|
292
|
+
audit_gate "pre-eval" "start" "$next_agent"
|
|
293
|
+
if run_pre_eval_gate "$next_agent"; then
|
|
294
|
+
audit_gate "pre-eval" "pass" "$next_agent"
|
|
295
|
+
else
|
|
296
|
+
audit_gate "pre-eval" "fail" "rerouted to $current_agent"
|
|
297
|
+
fi
|
|
298
|
+
if verify_artifact_prerequisites "$next_agent"; then
|
|
299
|
+
audit_gate "artifact-prereq" "pass" "$next_agent"
|
|
300
|
+
else
|
|
301
|
+
audit_gate "artifact-prereq" "fail" "$next_agent"
|
|
302
|
+
fi
|
|
289
303
|
fi
|
|
290
304
|
|
|
291
305
|
# ─────────────────────────────────────────
|
|
@@ -303,6 +317,7 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
|
|
|
303
317
|
escalate_after=$(jq -r '.flow.escalate_to_planner_after // 3' "$CONFIG" 2>/dev/null || echo 3)
|
|
304
318
|
if [ "$retry_count" -ge "$escalate_after" ] && [ "$sprint_status" = "failed" ] && [ "$next_agent" != "planner" ]; then
|
|
305
319
|
echo " ⚠ Escalation: ${retry_count}회 실패 — Planner에게 scope 축소/접근 변경 요청"
|
|
320
|
+
audit_log "system" "escalate" "start" "→planner" "retry ${retry_count}회 초과"
|
|
306
321
|
next_agent="planner"
|
|
307
322
|
jq --arg msg "Escalated after ${retry_count} failures. Planner must review scope or approach." \
|
|
308
323
|
'.next_agent = "planner" |
|
|
@@ -442,7 +457,10 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
|
|
|
442
457
|
timestamp: $timestamp
|
|
443
458
|
}' > "$HANDOFF"
|
|
444
459
|
|
|
460
|
+
audit_handoff "${current_agent:-dispatcher}" "$next_agent" "complete"
|
|
461
|
+
|
|
445
462
|
elif [ "$next_agent" = "archive" ]; then
|
|
463
|
+
audit_log "system" "archive" "start" "sprint-${sprint_num}" "sprint cycle complete"
|
|
446
464
|
jq -n \
|
|
447
465
|
--arg from "${current_agent:-evaluator}" \
|
|
448
466
|
--argjson sprint "$sprint_num" \
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# harness-session-start.sh — SessionStart 훅
|
|
3
3
|
# 새 세션 시작 시 자동으로:
|
|
4
|
-
# 1) 이전 에이전트가 completed이면 harness-next.sh
|
|
5
|
-
# 2)
|
|
6
|
-
# 3)
|
|
7
|
-
#
|
|
8
|
-
# 사용자가 수동으로 harness-next.sh를 실행할 필요가 없도록 자동화.
|
|
4
|
+
# 1) 이전 에이전트가 completed이면 harness-next.sh 실행 (게이트 + handoff)
|
|
5
|
+
# 2) Planner/Dispatcher 사이클이면 audit 리셋
|
|
6
|
+
# 3) 다음 에이전트 안내 출력
|
|
9
7
|
|
|
10
8
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
11
9
|
LIB="$SCRIPT_DIR/lib/harness-render-progress.sh"
|
|
10
|
+
AUDIT_LIB="$SCRIPT_DIR/lib/harness-audit.sh"
|
|
12
11
|
|
|
13
12
|
if [ ! -f "$LIB" ]; then exit 0; fi
|
|
14
13
|
source "$LIB"
|
|
14
|
+
[ -f "$AUDIT_LIB" ] && source "$AUDIT_LIB"
|
|
15
15
|
command -v jq &>/dev/null || exit 0
|
|
16
16
|
|
|
17
17
|
PROJECT_ROOT="$(resolve_harness_root "." 2>/dev/null)" || exit 0
|
|
@@ -21,6 +21,7 @@ HANDOFF="$PROJECT_ROOT/.harness/handoff.json"
|
|
|
21
21
|
[ -f "$PROGRESS" ] || exit 0
|
|
22
22
|
|
|
23
23
|
sprint_status=$(jq -r '.sprint.status // "init"' "$PROGRESS" 2>/dev/null)
|
|
24
|
+
sprint_num=$(jq -r '.sprint.number // 0' "$PROGRESS" 2>/dev/null)
|
|
24
25
|
current_agent=$(jq -r '.current_agent // "none"' "$PROGRESS" 2>/dev/null)
|
|
25
26
|
next_agent=$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null)
|
|
26
27
|
agent_status=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
|
|
@@ -34,14 +35,21 @@ if [ "$sprint_status" = "init" ]; then
|
|
|
34
35
|
fi
|
|
35
36
|
|
|
36
37
|
# ─────────────────────────────────────────
|
|
37
|
-
#
|
|
38
|
-
#
|
|
38
|
+
# Audit lifecycle: Planner/Dispatcher 시작 시 리셋
|
|
39
|
+
# ─────────────────────────────────────────
|
|
40
|
+
init_audit "$PROJECT_ROOT"
|
|
41
|
+
if [ "$next_agent" = "planner" ] || [ "$next_agent" = "dispatcher" ]; then
|
|
42
|
+
# 새 사이클 — 이전 audit을 archive로 이동하고 새로 시작
|
|
43
|
+
reset_audit "$PROJECT_ROOT" "$sprint_num"
|
|
44
|
+
audit_log "system" "cycle" "start" "sprint-${sprint_num}" "new plan/dispatch cycle"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# ─────────────────────────────────────────
|
|
48
|
+
# 이전 에이전트가 completed/failed → 자동 전환
|
|
39
49
|
# ─────────────────────────────────────────
|
|
40
50
|
if [ "$agent_status" = "completed" ] || [ "$agent_status" = "failed" ]; then
|
|
41
|
-
# harness-next.sh를 백그라운드로 실행하면 안 됨 — 결과가 필요
|
|
42
51
|
bash "$SCRIPT_DIR/harness-next.sh" "$PROJECT_ROOT" 2>/dev/null
|
|
43
52
|
|
|
44
|
-
# harness-next.sh가 progress.json과 handoff.json을 업데이트했으므로 다시 읽기
|
|
45
53
|
next_agent=$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null)
|
|
46
54
|
agent_status=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
|
|
47
55
|
fi
|
|
@@ -53,7 +61,6 @@ if [ "$agent_status" = "blocked" ]; then
|
|
|
53
61
|
echo "# Harness BLOCKED — retry limit reached, user intervention required"
|
|
54
62
|
|
|
55
63
|
elif [ -f "$HANDOFF" ] && [ "$next_agent" != "none" ] && [ "$next_agent" != "null" ]; then
|
|
56
|
-
# handoff.json에서 모델/모드 정보 읽기
|
|
57
64
|
handoff_model=$(jq -r '.model // "opus"' "$HANDOFF" 2>/dev/null)
|
|
58
65
|
handoff_thinking=$(jq -r '.thinking_mode // empty' "$HANDOFF" 2>/dev/null)
|
|
59
66
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-audit.sh — Audit log 헬퍼
|
|
3
|
+
#
|
|
4
|
+
# 라이프사이클: Planner 시작 → Eval 통과 (1 sprint cycle)
|
|
5
|
+
# 새 Planner/Dispatcher 사이클 시작 시 이전 로그는 archive로 이동.
|
|
6
|
+
#
|
|
7
|
+
# 사용법:
|
|
8
|
+
# source scripts/lib/harness-audit.sh
|
|
9
|
+
# audit_log "gen-backend" "develop" "start" "apps/service-user/" "User 서비스 CRUD 구현"
|
|
10
|
+
# audit_log "eval-func" "review" "complete" "POST /api/auth" "FAIL: 409→400 contract 불일치"
|
|
11
|
+
# audit_log "gen-backend" "handoff" "start" "→eval-functional" ""
|
|
12
|
+
|
|
13
|
+
AUDIT_LOG_FILE=""
|
|
14
|
+
|
|
15
|
+
# ─────────────────────────────────────────
|
|
16
|
+
# init_audit — audit.log 경로 설정 + 헤더 생성
|
|
17
|
+
# Args: $1 = project root
|
|
18
|
+
# ─────────────────────────────────────────
|
|
19
|
+
init_audit() {
|
|
20
|
+
local project_root="${1:-.}"
|
|
21
|
+
AUDIT_LOG_FILE="$project_root/.harness/actions/audit.log"
|
|
22
|
+
|
|
23
|
+
# 디렉토리 보장
|
|
24
|
+
mkdir -p "$(dirname "$AUDIT_LOG_FILE")"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# ─────────────────────────────────────────
|
|
28
|
+
# reset_audit — 새 사이클 시작 시 로그 초기화
|
|
29
|
+
# 이전 로그가 있으면 archive로 이동
|
|
30
|
+
# Args: $1 = project root, $2 = sprint number (optional)
|
|
31
|
+
# ─────────────────────────────────────────
|
|
32
|
+
reset_audit() {
|
|
33
|
+
local project_root="${1:-.}"
|
|
34
|
+
local sprint_num="${2:-0}"
|
|
35
|
+
init_audit "$project_root"
|
|
36
|
+
|
|
37
|
+
# 기존 로그가 있으면 archive로 이동
|
|
38
|
+
if [ -f "$AUDIT_LOG_FILE" ] && [ -s "$AUDIT_LOG_FILE" ]; then
|
|
39
|
+
local archive_dir="$project_root/.harness/archive"
|
|
40
|
+
mkdir -p "$archive_dir"
|
|
41
|
+
local ts
|
|
42
|
+
ts=$(date +%Y%m%d_%H%M%S)
|
|
43
|
+
mv "$AUDIT_LOG_FILE" "$archive_dir/audit_s${sprint_num}_${ts}.log"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# 새 로그 헤더
|
|
47
|
+
cat > "$AUDIT_LOG_FILE" <<HEADER
|
|
48
|
+
# Harness Audit Log
|
|
49
|
+
# Cycle started: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
50
|
+
# Format: TIMESTAMP | AGENT | ACTION | STATUS | TARGET | DETAIL
|
|
51
|
+
#─────────────────────────────────────────────────────────────────────────
|
|
52
|
+
HEADER
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# ─────────────────────────────────────────
|
|
56
|
+
# audit_log — 로그 엔트리 추가
|
|
57
|
+
#
|
|
58
|
+
# Args:
|
|
59
|
+
# $1 = agent (planner, gen-backend, gen-frontend, eval-func, eval-visual)
|
|
60
|
+
# $2 = action (develop, review, handoff, plan, gate)
|
|
61
|
+
# $3 = status (start, complete, fail, skip)
|
|
62
|
+
# $4 = target (파일 경로, 엔드포인트, 에이전트명 등)
|
|
63
|
+
# $5 = detail (설명, 결과 등)
|
|
64
|
+
# ─────────────────────────────────────────
|
|
65
|
+
audit_log() {
|
|
66
|
+
if [ -z "$AUDIT_LOG_FILE" ] || [ ! -f "$AUDIT_LOG_FILE" ]; then
|
|
67
|
+
return 0 # audit 미초기화 시 silent pass
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
local agent="${1:-unknown}"
|
|
71
|
+
local action="${2:-unknown}"
|
|
72
|
+
local log_status="${3:-unknown}"
|
|
73
|
+
local target="${4:-}"
|
|
74
|
+
local detail="${5:-}"
|
|
75
|
+
local ts
|
|
76
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
77
|
+
|
|
78
|
+
# 고정폭 포맷
|
|
79
|
+
printf "%-20s | %-14s | %-8s | %-8s | %-30s | %s\n" \
|
|
80
|
+
"$ts" "$agent" "$action" "$log_status" "$target" "$detail" >> "$AUDIT_LOG_FILE"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# ─────────────────────────────────────────
|
|
84
|
+
# audit_handoff — 핸즈오프 기록 (편의 함수)
|
|
85
|
+
# Args: $1=from_agent, $2=to_agent, $3=status(start|complete)
|
|
86
|
+
# ─────────────────────────────────────────
|
|
87
|
+
audit_handoff() {
|
|
88
|
+
audit_log "$1" "handoff" "$3" "→${2}" ""
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# ─────────────────────────────────────────
|
|
92
|
+
# audit_gate — 게이트 체크 기록 (편의 함수)
|
|
93
|
+
# Args: $1=gate_name, $2=status(pass|fail), $3=detail
|
|
94
|
+
# ─────────────────────────────────────────
|
|
95
|
+
audit_gate() {
|
|
96
|
+
audit_log "system" "gate" "$2" "$1" "$3"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# ─────────────────────────────────────────
|
|
100
|
+
# render_audit — audit.log를 읽기 좋게 출력
|
|
101
|
+
# Args: $1 = project root (optional)
|
|
102
|
+
# ─────────────────────────────────────────
|
|
103
|
+
render_audit() {
|
|
104
|
+
local project_root="${1:-.}"
|
|
105
|
+
local log_file="$project_root/.harness/actions/audit.log"
|
|
106
|
+
|
|
107
|
+
if [ ! -f "$log_file" ]; then
|
|
108
|
+
echo " (audit log not yet created)"
|
|
109
|
+
return 0
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
cat "$log_file"
|
|
113
|
+
}
|