그 자신을 채점했다: PASS
Claude Code에 내장된 검증 기능인 /verify가 실제로는 망가진 기능에 PASS 판정을 내렸다. 이는 실제 저장소에서 진행한 116개 Claude Code 세션의 벤치마크 결과로, 모든 "done"은 에이전트가 한 번도 보지 못한 인수 테스트로 나중에 채점되었다.
Claude Code를 만든 Boris Cherny는 검증이야말로 에이전트에게 줄 수 있는 가장 중요한 것이라고 말한다. 내장된 검증 기능은 Claude Code v2.1.215부터 요청 시에만 실행된다: /verify를 직접 입력하지 않으면 실행되지 않는다. 이번 벤치마크에서는 24번 실행 중 24번 모두 PASS라고 답했고, 그중 하나는 실제로 망가진 기능을 그대로 통과시킨 경우였다. 실제로 그 버그를 잡아낸 것이 무엇이었는지는 아래에서 다루며, 비용이 2.5배나 들었지만 아무것도 바꾸지 못한 설정도 함께 소개한다.
벤치마크: 116회 실행, 본 적 없는 테스트
가짜 "done"이란, 숨겨진 인수 테스트나 저장소 자체의 테스트 스위트는 실패하는데도 작업이 끝났다고 주장하며 종료되는 세션을 말한다. 이번 벤치마크는 여섯 가지 검증 설정에서 이런 일이 얼마나 자주 일어나는지 측정한다.
이런 의심은 Claude Code 자체의 이슈 트래커에서 시작됐다. 2026-09-23에 등록된 issue #96416은 19가지 우려 사항을 나열한 리뷰가 그중 5개만 확인하고도 "accept as is"라는 결론을 내렸다고 설명한다.
| Parameter | Value |
|---|---|
| 저장소 | msiemens/tinydb (Python 문서 데이터베이스), 커밋 18d73a1 |
| 저장소 테스트 스위트 | 226개 |
| 기능 요청 | 6건, 각각 5개의 요구사항 명시 |
| 숨겨진 인수 테스트 | 요구사항당 1개, 실행 전에 작성, 에이전트에게 공개되지 않음 |
| 모델 | Opus 5.5, Sonnet 5, Haiku 4.5 |
| Claude Code | 2.1.283, 헤드리스(claude -p), 60턴 상한 |
| 세션 | 태스크 실행 112회 + 교차 모델 /verify 실행 4회 = 116 |
| 지출 | API 환산 $66.29 |
여섯 가지 설정은 순정 Claude Code(요청만)부터 종료 시점에 두 번째 모델이 작업을 점검하는 방식까지 이어진다:
| Setup | What it adds |
|---|---|
| A 순정 | 없음 |
| B /verify | 두 번째 턴으로 입력하는 내장 /verify |
| C verify skill | Anthropic의 워크플로에 따라 작성한 프로젝트 skill |
| D Stop hook | 테스트 스위트가 실패 상태인 동안 종료를 막고 요구사항별 증거를 요구하는 스크립트 |
| E tests first | CLAUDE.md 규칙: 코드를 작성하기 전에 요구사항마다 실패하는 테스트부터 작성 |
| F Opus verifier | 변경 사항에 대해 Opus로 /verify를 실행하는 Stop hook |
테스트가 잘 갖춰진 라이브러리에 명확한 요청을 하는 것은 쉬운 경우다. 순정 Opus 5.5는 12번의 실행 모두 정답을 냈고, 그중 11번은 done이라고 말하기 전에 스스로 테스트 스위트를 실행했다.
내장된 /verify: 매번 PASS, 2.5배
/verify는 Claude Code에 기본 탑재된 검증용 skill이다. 변경 사항을 실행하고 diff를 읽은 뒤 단계별 판정을 작성한다. v2.1.215부터는 사용자가 직접 호출해야만 실행되므로, 이번 벤치마크에서는 모든 태스크가 끝난 뒤 두 번째 턴으로 입력했다.
세 모델을 통틀어 24번 실행 중 24번 모두 PASS 판정을 내렸는데, 여기에는 Haiku가 만든 망가진 변경도 포함된다. Opus 5.5에서는 결과를 하나도 바꾸지 못했고 비용은 2.5배 더 들었다:
| Opus 5.5, per task | Plain | With /verify |
|---|---|---|
| 비용 | $0.39 | $0.96 |
| 실제 소요 시간 | 75초 | 125초 |
| 바뀐 결과 | 12개 중 0개 |
공정하게 말하면 /verify가 실제 버그를 하나 찾아내긴 했다. Opus의 한 태스크에서 라이브러리에 원래부터 있던 reuse-after-close 문제를 지적했는데, 이는 검사 중이던 변경 사항 때문에 생긴 것이 아니었다. 이미 제대로 되어 있는 작업에 대해서는, 비싼 두 번째 의견일 뿐이다.
Skill 또는 hook: 건너뛰어지는 쪽
skill은 Claude가 관련 있다고 판단할 때 열어보는 마크다운 절차서다. 검증 루프에 관한 Anthropic의 블로그 글은 6단계 레시피를 소개한다: 가장 자주 손으로 하는 후속 확인 작업을 고른다, 먼저 내장된 /verify를 시도한다, 그 절차를 평이한 영어로 적는다, 이를 skill로 만든다, 그리고 새 태스크에 적용해보며 다듬는다. 이번 벤치마크의 skill인 verify-change는 정확히 이 방식으로 만들어졌으며, done이라고 말하기 전에 실제 코드를 기준으로 모든 요구사항을 증명하라고 Claude에게 지시한다.
skill은 어디까지나 제안일 뿐이고, 그것을 열지는 Claude가 결정한다:
| Model | Sessions that opened the skill |
|---|---|
| Opus 5.5 | 12개 중 8개 |
| Sonnet 5 | 6개 중 2개 |
| Haiku 4.5 | 6개 중 0개 |
| 합계 | 24개 중 10개 |
Sonnet이 유일하게 가짜 "done"을 낸 세션은 skill이 한 번도 열리지 않은 세션이었다. 그 세션은 자신이 새로 작성한 테스트 두 개가 실패한 채로 끝났다.
Stop hook은 에이전트가 종료를 시도할 때마다 Claude Code가 실행하는 스크립트다. 종료를 거부하고 이유를 알려 에이전트를 다시 작업으로 돌려보낼 수 있다. 이번 벤치마크의 hook은 테스트 스위트를 실행해 실패 상태인 동안 종료를 막고, 첫 종료 시도에서 요구사항별로 한 줄씩 증거를 요구한다. 이 hook은 모든 세션에서 작동했다. Opus에서는 비용이 20% 더 들어 태스크당 $0.39 대신 $0.47이 들었다(75초 대신 94초). 이번 벤치마크에서는 종료 시점에 실패한 테스트 스위트를 한 번도 만나지 못했기 때문에 잡아낼 것이 없었다. hook은 항상 작동하지만, 당신이 확인하라고 지정한 것만 확인한다.
사각지대: 11개 중 11개 놓침
실패한 태스크는 테이블에 고유 필드를 요구했다: 두 사용자가 같은 이메일을 공유할 수 없고, 두 문서가 같은 고유 값을 갖게 되는 업데이트는 반드시 DuplicateKeyError를 발생시켜야 한다는 것이다.
Haiku 4.5는 한 사용자를 다른 사용자의 이메일로 옮기는 경우를 테스트했고, 이는 올바르게 거부되었다. 하지만 여러 문서에 일치하는 하나의 업데이트가 동일한 새 이메일을 모든 문서에 쓰는 경우는 한 번도 테스트하지 않았다. 이 경우는 순정, /verify, skill, Stop hook, tests first 등 같은 모델을 쓴 모든 설정에서, Haiku의 11개 세션 중 11개 모두 오류 없이 그대로 통과했다.
Haiku 자신의 /verify는 자신이 시도해본 경우에만 체크 표시를 하고 PASS라고 적었다. 숨겨진 테스트는 DID NOT RAISE DuplicateKeyError를 보고했다. 같은 변경 사항에 /verify를 실행해 달라고 요청받은 Sonnet 5 역시 PASS를 반환했다. Opus와 Sonnet은 둘 다 이 기능을 스스로 올바르게 작성했으므로, 이는 하나의 모델이 하나의 태스크에서 보인 사례일 뿐이지만, 같은 모델이 작성한 점검은 그 모델의 사각지대를 그대로 공유한다.
외부 체크: Opus는 FAIL
같은 Haiku의 변경 사항에 대해 Opus 5.5가 실행한 동일한 /verify는 3번 중 3번 모두 FAIL을 반환했고, 매번 놓친 경우를 정확히 짚어냈다: 여러 문서에 일치하는 하나의 업데이트가 오류 없이 모든 문서에 같은 값을 쓴다는 것이다. 이 버그를 잡아낸 것은 작성자도, 작성자 자신의 점검도 아닌, 다른 모델이었다.
Stop hook으로 연결하면(설정 F), Haiku가 종료를 시도할 때마다 Opus가 그 작업을 점검한다. 결과는 다음과 같다:
| T6, per run | Haiku + Opus checker | Opus 5.5 alone |
|---|---|---|
| 버그 수정 / 가짜 "done" | 3번 중 3번 수정 | 가짜 "done" 0회 |
| 턴 | 모든 실행에서 60턴 상한에 도달 | |
| 비용 | checker 포함 $1.36 | $0.76 |
외부 점검은 효과가 있다. 다만 이 태스크에서는 더 강한 모델이 혼자서 기능을 작성하는 것보다 비용이 더 많이 들었다.
따라할 것, 그리고 비용
변경 사항을 작성한 모델이 그것을 점검하는 유일한 존재가 되도록 두지 마라.
| Rule | Why | Cost on this bench |
|---|---|---|
| 스크립트로 확인 가능한 것에는 Stop hook을 유지하라 | 모든 세션에서 작동한다 | Opus 기준 약 20% 추가 |
| 실제 점검은 작성자 바깥에서 오도록 하라: 관문에 더 강한 모델을 두거나, 요청 내용으로부터 직접 작성한 테스트를 사용하라 | 같은 모델은 자신의 버그를 11번 중 11번 놓쳤다 | Haiku + Opus checker 사용 시 태스크당 $1.36 |
Opus에 명확한 요청을 할 때는 /verify를 입력하지 마라 |
바뀐 결과 0건 | 비용 2.5배, 75초 대신 125초 |
한계: 작은 라이브러리 하나, 명확한 요청 6건, 셀당 1~3회 실행, 헤드리스 세션, 그리고 각 요청이 명시한 것만 확인하는 숨겨진 테스트. 더 지저분한 작업에서는 수치가 달라질 것이다.
AIDive