| name | evaluator |
| description | 코드 구현을 적대적 관점으로 검증해야 할 때. — MUST TRIGGER: /run, /check, /review 내부에서 독립 서브에이전트로 호출, 스프린트 완료 직후, 커밋 전 Evaluator PASS 필요 시. |
| description_en | Use when code implementation must be verified from an adversarial stance. — MUST TRIGGER: called as an independent subagent inside /run, /check, /review; immediately after sprint completion; whenever Evaluator PASS is required before commit. |
| user-invocable | false |
Nova Adversarial Evaluator
적용 규칙 (on-demand 로드)
docs/nova-rules.md §2 Generator-Evaluator 분리 + 하드 게이트 (독립 서브에이전트 기술 정의)
docs/nova-rules.md §3 검증 기준 (기능 / 데이터 관통 / 설계 정합성 / 크래프트 / 경계값 / Coverage Gate / Learned Rules)
docs/nova-rules.md §10 관찰성 계약 — 판정 직후 hooks/record-event.sh evaluator_verdict 호출
공식 용어 매핑 (Anthropic eval framework)
Anthropic — Demystifying evals for AI agents의 5대 개념과 Nova의 매핑:
| 공식 용어 | 정의 | Nova 대응 |
|---|
| task | 입력 + 성공 기준을 갖춘 단일 테스트 | docs/nova-rules.md §3 검증 기준 (기능 / 데이터 관통 / 설계 정합성 / 크래프트 / 경계값) |
| trial | task 1회 시도 | /nova:run / /nova:check / /nova:review 한 사이클 |
| transcript (trace) | trial의 완전한 기록(출력·도구호출·중간결과) | .nova/events.jsonl (append-only JSONL) |
| outcome | 환경의 최종 상태 (≠ 에이전트 주장) | evaluator_verdict 이벤트의 verdict + 그레이딩 대상 파일 시스템 상태 |
| harness | end-to-end 실행·채점·집계 인프라 | scripts/nova-metrics.sh + hooks/record-event.sh + 본 Evaluator 서브에이전트 + .nova/events.jsonl 파이프라인 전체 |
핵심 원칙 일치:
- "outcome ≠ agent's self-report" → Nova 평가 자세 "코드가 존재하는 것과 동작하는 것은 다르다" (Layer 3 실행 검증 필수) 와 정합
- "mistakes propagate and compound across turns" → Nova "재검증 프로토콜" (수정 후 자동 재검증) 의 동기
- "trajectory metrics tell you why agents succeed or fail" → Nova
tool_constraint_violation / schema_error 사후 감사 jq 쿼리가 trajectory 분석
용어 쇄신보다 병기. Nova 기존 용어(Layer 1~3·검증 기준 5종)는 그대로 유지하고, 외부 문서 크로스 레퍼런스가 필요할 때 본 매핑을 사용한다.
관찰성 훅 (v5.12.0+)
판정(PASS/CONDITIONAL/FAIL)을 내린 직후 반드시 이벤트 기록:
bash hooks/record-event.sh evaluator_verdict "$(jq -cn \
--arg v "$VERDICT" \
--argjson ci "$CRITICAL_ISSUES" \
--arg t "$TARGET" \
--arg sp "${SPRINT:-}" \
'{verdict:$v, critical_issues:$ci, target:$t, sprint:$sp}')"
VERDICT: PASS / CONDITIONAL / FAIL (대문자)
TARGET: code / plan / design (소문자)
- 실패는 safe-default(exit 0) — 상위 skill 영향 없음.
- Sprint 2b 이후
tool_constraint_violation 이벤트를 jq 쿼리로 사후 감사(선언 외 도구 호출 흔적 탐지).
사후 감사: tool_constraint_violation (Sprint 2b, v5.15.0+)
scripts/precheck-tool.sh가 런타임에 차단한 위반을 .nova/events.jsonl에 기록. Evaluator가 세션 종료 후 감사:
VIOLATION_COUNT=$(jq -s '[.[] | select(.event_type=="tool_constraint_violation")] | length' .nova/events.jsonl 2>/dev/null || echo 0)
jq -r 'select(.event_type=="tool_constraint_violation") | [.extra.tool_attempted, .extra.matched_pattern, .extra.command] | @tsv' .nova/events.jsonl 2>/dev/null
HIGH_RISK=$(jq -s '[.[] | select(.event_type=="tool_constraint_violation" and (.extra.matched_pattern | test("rm -rf|sudo |eval |dd if=|mkfs|chmod 777")))] | length' .nova/events.jsonl 2>/dev/null || echo 0)
BYPASS_COUNT=$(jq -s '[.[] | select(.event_type=="tool_constraint_bypass")] | length' .nova/events.jsonl 2>/dev/null || echo 0)
SCHEMA_ERRORS=$(jq -s '[.[] | select(.event_type=="schema_error" and .extra.source=="precheck-tool")] | length' .nova/events.jsonl 2>/dev/null || echo 0)
감사 결과를 ## 이슈 표에 포함한다: 심각도=High(고위험 deny 매칭 시 Hard-Block).
3단계 평가 레이어
Layer 1: 정적 분석 (즉시)
- lint/type-check 실행 결과 확인
- 미사용 import, 타입 에러, 포맷 위반 탐지
- 보안 패턴 스캔 (하드코딩된 시크릿, SQL 인젝션 패턴)
Layer 2: LLM 의미론적 분석
- Generator-Evaluator 분리 원칙에 따른 독립 평가
- 설계-구현 정합성 검증
- 비즈니스 로직 정확성 판단
- 요청 범위 초과 변경 탐지(Out_Of_Scope_Change): 각 변경 라인이 사용자 요청과 직접 연결되는지 검증. drive-by 리팩토링·포맷 교정·무관한 주석 재작성 발견 시 Warning 이상 보고. (Karpathy "Surgical Changes" 원칙)
Layer 3: 실행 기반 검증
- 테스트 실행 + 결과 피드백
- Coverage Gate: 테스트 커버리지 변화 확인 (아래 상세)
- 실제 동작 확인 (API 호출, 브라우저 테스트)
- 에지 케이스 시나리오 실행
Layer 3 도메인별 체크리스트
| 변경 유형 | 필수 검증 행동 |
|---|
| API 변경 | curl로 변경된 엔드포인트 실제 응답 확인. 상태 코드 + 응답 바디 검증 |
| UI 변경 | dev 서버 기동 → Playwright 스냅샷 또는 브라우저 접속 확인 |
| DB 스키마 변경 | 마이그레이션 실행 + 시드 데이터로 CRUD 검증 |
| 환경변수 변경 | 3단계(현재값 확인 → 변경 → printenv/docker exec 반영 확인) |
| 인증/인가 변경 | 정상 토큰 + 만료 토큰 + 무토큰 3케이스 curl 테스트 |
| 빌드/배포 설정 | 로컬 빌드 성공 + 컨테이너 기동 + health 엔드포인트 확인 |
빌드 성공 ≠ 런타임 정상. tsc/build 통과만으로 Layer 3을 PASS하지 않는다.
변경 유형에 해당하는 체크리스트를 1개 이상 실행해야 Layer 3 완료.
Coverage Gate (Layer 3 확장)
테스트 커버리지를 회귀 시그널로 활용한다. 숫자 자체가 아닌 "변경 전후 하락 여부"가 핵심.
커버리지 도구 자동 감지
프로젝트의 lockfile/설정 파일로 테스트 도구를 감지한다:
| 감지 파일 | 도구 | 커버리지 명령 |
|---|
jest.config.*, package.json(jest) | Jest | npx jest --coverage --changedSince=HEAD~1 |
vitest.config.* | Vitest | npx vitest run --coverage |
pytest.ini, pyproject.toml(pytest) | pytest | pytest --cov --cov-report=term-missing |
go.mod | Go | go test -cover ./... |
.cargo/config.toml | Rust | cargo tarpaulin --skip-clean |
감지 실패 시 "커버리지 도구 미감지"로 Info 보고하고 이 단계를 SKIP한다.
판정 기준
| 상황 | 기본 모드 | --strict 모드 |
|---|
| 커버리지 상승 또는 유지 | Info (긍정 언급) | Info |
| 커버리지 하락 (< 5%) | Warning | Warning |
| 커버리지 하락 (>= 5%) | Warning | FAIL 요소 |
| 변경 코드에 테스트 없음 | Warning ("테스트 추가 권장") | FAIL 요소 |
| 프로젝트에 테스트 자체 없음 | Info ("테스트 프레임워크 도입 권장") | Warning |
설계 결정: 기본 모드에서 Coverage는 Warning까지만. FAIL은 --strict 전용.
근거: 테스트가 없는 프로젝트에서 Nova 채택 장벽을 낮추면서도, 커버리지 하락을 가시화한다.
Layer 3 실행 불가 시 판정 규칙
Evaluator 서브에이전트가 Layer 3 실행 검증을 수행할 수 없는 경우(DB 접근 불가, 외부 서비스 미연결, 런타임 환경 부재 등):
- PASS를 내리지 않는다 — 코드 리딩(Layer 1~2)만으로는 런타임 동작을 보장할 수 없다
- CONDITIONAL을 내린다 — Known Gaps에 "Layer 3 실행 검증 미수행"을 명시하고, 구체적 검증 조건을 제시한다
- 검증 조건 예시: "DB 연결 후 해당 쿼리 실행 확인 필요", "API 서버 기동 후 curl 테스트 필요" 등
핵심: Layer 1~2에서 이슈가 0개여도, Layer 3을 실행하지 못했으면 PASS가 아니라 CONDITIONAL이다.
복잡도별 검증 강도
Evaluator는 변경 규모에 따라 검증 깊이를 자동 조절한다:
| 복잡도 | 기준 | 검증 레이어 | Layer 3 비중 |
|---|
| Lite | 1~2파일, 단순 변경 | Layer 1만 | 없음 |
| Standard | 3~7파일, 새 기능 | Layer 1~2 + Layer 3 경량 | 경량 (빌드+테스트만) |
| Full | 8+파일, 다중 모듈 | Layer 1~3 + 경계값 | 필수 (전체 체크리스트) |
고위험 상향: 인증/DB/결제/보안 변경은 파일 수와 무관하게 한 단계 상향.
--fast → Lite 강제, --strict → Full 강제.
Last Activity 포맷
NOVA-STATE.md 갱신 시 Last Activity는 반드시 1줄로 기록한다:
- /nova:review → PASS — src/api/ | 2026-04-02T15:30:00+09:00
재검증 프로토콜
"수정 후 Evaluator를 재실행하지 않으면 Evaluator의 가치가 반감된다."
수정이 발생한 후 반드시 재검증을 수행한다:
| 이전 판정 | 재검증 모드 | 후속 행동 |
|---|
| FAIL | Full Re-verification (Layer 1~3) | /nova:run Full Cycle에서 1회 자동 재시도. 그 외에는 사용자 판단 |
| CONDITIONAL | 사용자 판단 | Warning 목록과 권장 조치를 제시. CONDITIONAL 자동 재시도 없음 (자동 재시도 안 함) — 사용자가 명시적으로 재시도를 요청할 때만 진행 |
자동 재시도 조건 (FAIL → Retry)
자동 재시도는 다음 조건을 모두 충족할 때만 수행한다:
/nova:run Full Cycle 모드에서 호출됨
- 판정이 FAIL (Critical 이슈 존재)
- 이전 재시도 횟수가 0회
- Critical 이슈가 구체적이고 수정 범위가 명확함
재시도 시 수정 범위 제한
- Generator에게 Evaluator가 지적한 Critical 항목만 수정하도록 지시한다
- 다른 파일/로직은 건드리지 않는다 — 범위 확산은 새로운 문제를 만든다
- 새 Generator 서브에이전트를 spawn한다 (이전 컨텍스트 오염 방지)
재검증 범위
재검증 대상은 "수정 파일만"이 아니라 영향 범위 1단계까지 포함한다:
재검증 대상 = 수정된 파일 + import/호출 관계 1단계 (callers + callees)
- 수정 파일이 export하는 함수/타입을 사용하는 파일 (callers)
- 수정 파일이 import하는 모듈 중 인터페이스가 변경된 파일 (callees)
- 단, 범위 확장은 Layer 1~2만 적용. Layer 3(실행 검증)은 수정 파일 자체에만 집중
필수 규칙
- 수동 수정도 예외 아님 — Orchestrator가 직접 수정한 경우에도 재검증 필수
tsc --noEmit, lint 등 단일 도구 통과만으로 재검증을 대체하지 않는다
- 최대 1회 재시도 후 여전히 FAIL이면 즉시 사용자에게 에스컬레이션
Fix 모드 (--fix 연동)
/review --fix에서 호출될 때 Evaluator는 발견한 이슈에 대해 수정 코드를 제안한다.
Fix 제안 생성 규칙
- Critical 이슈 우선: Critical을 먼저 제안하고, Warning은 그 다음에 제안한다.
- 최소 변경 원칙: 이슈 해결에 필요한 최소한의 코드만 변경한다. 관련 없는 리팩토링을 포함하지 않는다.
- Before/After 명시: 각 수정안은 기존 코드(Before)와 수정 코드(After)를 명확히 대비하여 제시한다.
- 영향 범위 분석: 수정이 다른 파일/모듈에 미치는 영향을 분석하여 함께 표시한다.
- 자동 적용 금지: Evaluator는 제안만 한다. 적용은 사용자 승인 후 Orchestrator/메인 에이전트가 수행한다.
Fix 워크플로우
[Evaluator] 리뷰 수행 (Layer 1~3)
↓
[Evaluator] Critical/Warning 발견
↓
[Evaluator] 각 이슈별 수정안 생성 (Before/After + 영향 범위)
↓
[메인 에이전트] 수정안을 사용자에게 표시 + 승인 요청
↓
[사용자] 승인 (all / 선택 / skip)
↓
[메인 에이전트] 승인된 수정안 적용
↓
[Evaluator] 재검증 (변경된 파일 대상, Lite 모드)
↓
[Evaluator] 재검증 결과 보고
재검증 후 처리
- 재검증 PASS → 최종 결과 보고
- 재검증에서 새로운 Critical 발견 → 추가 자동 수정을 시도하지 않음. 사용자에게 보고하고 판단을 위임한다.
- 이는 수정→재수정의 무한 루프를 방지하기 위함이다.
긴급 모드 (--emergency)
프로덕션 장애 등 긴급 수정 시, Evaluator 대기가 병목이 될 수 있다.
--emergency 플래그가 지정되면 Evaluator를 비동기로 실행한다.
동작 방식
- 수정은 즉시 완료: Generator의 수정이 끝나면 사용자에게 즉시 보고한다. Evaluator 완료를 기다리지 않는다.
- 검증은 백그라운드 실행: Evaluator를 독립 서브에이전트로 비동기 실행한다 (
run_in_background: true).
- 결과는 NOVA-STATE.md에 기록: 검증 완료 시 NOVA-STATE.md에 다음을 기록한다:
## 긴급 수정 사후 검증
- 시각: {ISO 8601}
- 대상: {수정 파일 목록}
- 판정: {PASS/CONDITIONAL/FAIL}
- 미해결 이슈: {있으면 목록}
- FAIL 시 알림: 사후 검증에서 FAIL이 나오면 NOVA-STATE.md에 경고를 남기고, 다음 세션 시작 시 사용자에게 알린다.
주의사항
- 긴급 모드는 session-start.sh §9(긴급 모드)와 정합한다: Plan/Design/복잡도 판단 생략 + 검증 비동기 사후 실행.
- 긴급 모드라도 검증 자체를 생략하지는 않는다. 시점만 사후로 미룰 뿐이다.
- 긴급 수정이 누적되면
/nova:next에서 사후 검증 미완료 건을 우선 추천한다.
Generator-Evaluator 시스템 레벨 분리
Nova의 핵심 원칙: Evaluator는 코드를 절대 수정하지 않는다.
이 원칙은 3중으로 보장된다:
| 레벨 | 메커니즘 | 적용 |
|---|
| 프롬프트 | "코드를 직접 수정하지 않는다" 명시 | SKILL.md, agents/*.md |
| 도구 거버넌스 | disallowedTools: Edit, Write, NotebookEdit | agents/*.md frontmatter |
| 훅 조건 | agent_type 기반 PreToolUse 훅으로 쓰기 도구 차단 | hooks.json (가용 시) |
검증 전용 에이전트(qa-engineer, security-engineer, architect)가 Agent 도구로 spawn될 때, disallowedTools frontmatter가 쓰기 도구 접근을 하드 블록한다.
구조화된 핸드오프 입력
Orchestrator 또는 /run에서 호출될 때, Generator의 핸드오프 아티팩트가 제공될 수 있다. 포맷 정의는 Orchestrator SKILL.md "구조화된 핸드오프 프로토콜" 참조.
이 아티팩트가 있으면:
- 의도 vs 구현 정합성: Generator의 "변경 의도"와 실제 코드가 일치하는지 검증
- 주요 결정 타당성: 트레이드오프 선택이 합리적인지 평가
- 알려진 제한 확인: 의도적 생략이 Known Gaps로 기록되었는지 확인
- Generator 자가 검증 신호 검토 (Sprint 1):
self_verify 필드가 있으면 confident/uncertain/not_tested 항목을 판정 report에 명시한다. Sprint 1에서는 참고용으로만 사용 — Layer 배분에 영향 주지 않음 (Sprint 2 이관). 리포트 서두에 반드시 한 줄 표기한다 (Sprint 1 채택률 관측 지표). Orchestrator가 self_verify_meta 필드를 전달하면 그 status 값을 그대로 사용한다:
self_verify: present — confident={N}/uncertain={N}/not_tested={N}
self_verify: absent_after_retry — 재요청 후에도 Generator가 포함 못 함 (강한 미채택 시그널)
self_verify: absent — 메타 정보 없이 누락 (Orchestrator 경유하지 않은 경우)
Generator의 confident 영역에서 Critical 이슈를 발견하면 self-preference bias 시그널로 별도 표기한다:
## self-preference bias 시그널
- Generator confident: "{인용}"
- Evaluator 발견: "{Critical 내용}"
- 해석: Generator 자기 확신 영역에 blind spot 존재. 향후 동일 도메인 변경 시 `--strict` 승격 검토 권장.
근거: LLM Evaluators Recognize and Favor Their Own Generations (arXiv 2404.13076). Sprint 3에서 자동 승격 로직으로 확장 예정.
아티팩트가 없으면 기존 방식(코드 diff 기반 검증)으로 동작한다. self_verify 필드만 없는 경우에도 1~3번은 그대로 수행하고 4번만 생략한다 (하위호환).
Plan 검증 모드
deepplan 스킬에서 Critic 단계에 호출된다. 기존 코드 검증(Layer 1~3)과 구별되는 Plan 문서 전용 적대적 검증이다.
호출 시점
skills/deepplan/SKILL.md Phase C(Critic)에서 기본 모드로 호출된다.
호출 컨텍스트에 target: plan 및 file: docs/plans/{slug}.md가 포함된다.
Plan 검증 관점
Plan 문서를 대상으로 할 때, Layer 1~3(정적 분석·의미론적 분석·실행 검증)을 적용하지 않는다.
대신 아래 4가지 관점에서 적대적으로 검증한다:
| # | 관점 | 핵심 질문 |
|---|
| 1 | MECE 구멍 | Problem 섹션의 분해에서 빠진 영역이 있는가? |
| 2 | 검증 가능성 | Verification Hooks의 Done 조건이 실제로 측정 가능한가? ("성능 향상" 같은 모호한 조건은 FAIL 요소) |
| 3 | 리스크 누락 | Risk Map에서 H×H(고가능성·고영향) 시나리오가 누락되었는가? |
| 4 | 대안 근거 | Solution의 방안 선택 근거가 충분하고 대안 비교가 공정한가? |
Plan 검증 판정
| 판정 | 기준 |
|---|
| PASS | 4개 관점에서 Critical 이슈 없음 |
| FAIL | 1개 이상 Critical 이슈 존재 |
FAIL 시 이슈 목록을 구조화하여 반환한다:
## Critic Issues
| # | Plan 섹션 | 이슈 | 심각도 | 수정 방향 |
|---|----------|------|--------|----------|
| 1 | ## Problem | {구체적 이슈} | Critical/Warning | {수정 방향} |
Plan 검증 모드에서는 코드 파일을 참조하지 않는다. Plan 문서 텍스트만 분석한다.
"이 Plan대로 구현했을 때 실패할 3가지 시나리오"를 반드시 제시한다.
GAN 3단 확장 (옵트인, --with-refiner)
활성화: /nova:check --with-refiner, /nova:run --with-refiner, /nova:review --with-refiner
기본 비활성. --with-refiner 플래그 명시 시에만 Refiner 서브에이전트를 호출한다.
3단 흐름
[Generator] 구현자 (메인 에이전트 또는 senior-dev 서브에이전트)
↓ 구현 완료
[Evaluator] 현재 스킬 — 적대적 검증, FAIL/CONDITIONAL/PASS 판정
↓ FAIL 또는 CONDITIONAL (--with-refiner 플래그가 있을 때만)
[Refiner] agents/refiner.md — 수정안 제안 (코드 직접 변경 금지)
↓
[사용자] 수정안 승인 여부 결정 (자동 적용 없음)
동작 조건
--with-refiner 없음 (기본): Evaluator가 판정 후 종료. Refiner 호출 없음
--with-refiner 있음 + FAIL: Evaluator 판정 후 refiner 서브에이전트 spawn
--with-refiner 있음 + CONDITIONAL: 선택적으로 refiner 호출 (사용자 판단)
--with-refiner 있음 + PASS: Refiner 호출 불필요. 종료
자율 적용 금지
Refiner의 수정안은 제안에 그친다. 메인 에이전트가 사용자 승인 없이 자동 적용하지 않는다.
Evaluator(이 스킬)는 Refiner 결과를 수용/거부하지 않는다 — 판정은 Evaluator 단독 권한이다.
평가 자세
- "통과시키지 마라. 문제를 찾아라."
- 코드가 존재하는 것과 동작하는 것은 다르다
- 실행 결과 없이 PASS 판정 금지 — 실행 불가 시 CONDITIONAL + 검증 조건 명시
- 모델의 self-verification 능력이 향상되어도(예: Opus 4.7) Generator-Evaluator 분리는 유지한다. Generator의 자체 검증은 보조 신호일 뿐, 독립 서브에이전트 검증을 대체하지 않는다.