← 실전 가이드 도서관 · 에이전트·검증
Claude Code가 "완료했다"고 할 때, 실제 파일을 강제로 검사하는 3중 하네스
에이전트의 완료 문장을 믿는 대신 PreToolUse · PostToolUse · Stop 훅과 verify.py 스크립트로 파일 누락·덮어쓰기를 잡아내는 실전 세팅입니다.
"다 고쳤습니다!"라는 Claude Code의 대답을 믿고 폴더를 열어 봤더니 파일이 비어 있거나 기존 설정이 날아간 경험, 한 번쯤 있으실 겁니다.
말로 "실수하지 마"라고 타이르는 프롬프트 대신, 통과 증거가 없으면 에이전트가 멈추지 못하게 만드는 결정론적 검사 장치(하네스)를 10분 안에 내 폴더에 심어 봅니다.
이 가이드 안에 들어 있는 전체 순서
- 0 핵심 원리 요약 1분 요약
- 1 최소 완료 검사기(verify.py) 만들기 스크립트 복붙
- 2 .claude/settings.json 3중 훅 연결 설정 파일
- 3 억지 수정 방지 인계 프롬프트 프롬프트
잠긴 단계를 누르면 아래 0초 무료 해금 카드로 이동합니다.
말로 부탁하는 규칙(CLAUDE.md)과 강제하는 훅(Hooks)은 다릅니다
많은 분이 CLAUDE.md에 "절대 기존 파일을 지우지 마", "끝나면 꼭 테스트해"라고 적으면 해결될 거라 생각합니다. 하지만 컨텍스트가 길어지면 LLM은 지시문을 건너뛰고도 태연하게 완료 보고를 출력합니다.
-
1
1층 · PreToolUse 가드 (실행 전 차단)위험한 셸 명령(
rm -rf, 강제 push, 보호 파일 덮어쓰기 등 G1~G7 규칙)이 실행되기 직전에 종료 코드 2로 원천 차단합니다. -
2
2층 · PostToolUse 워처 (수정 직후 문법 검사)파일을 저장할 때마다 JSON·Python·마크다운 문법 검사를 자동 실행해 깨진 구문을 즉시 에이전트에게 되돌려 줍니다.
-
3
3층 · Stop 훅 + verify.py (완료 전 증거 대조)에이전트가 대화를 끝내려 할 때
verify.py가 필수 파일 존재·빈 칸 여부·링크 무결성을 검사하고, 실패하면 종료를 거부합니다.
| 구분 | CLAUDE.md 지시문만 있을 때 | 3중 하네스(Hooks + verify.py) 적용 시 |
|---|---|---|
| 작동 방식 | 모델의 확률적 주의를 기대함 | 운영체제 프로세스가 종료 코드로 강제함 |
| 거짓 완료 보고 | 파일이 안 만들어져도 "완료" 출력 가능 | verify.py 통과 전까지 Stop 자체가 반려됨 |
| 실측 관찰 (16회 묶음) | 긴 작업에서 검증 생략·덮어쓰기 발생 | 현행 14회 본 실험 + 추가 훅 실험 2회에서 위험 명령 차단 및 누락 재검출 확인 |
FREE INSTANT UNLOCK · 0초 즉시 해금
한 번만 입력하면 이 브라우저에서 12편 전체가 계속 열려 있습니다
복붙 프롬프트와 나머지 실전 단계를 바로 여세요
이메일 주소를 입력하시는 즉시 지금 읽고 계신 STEP 1~3 본문과 도서관의 모든 실전 가이드(12편)가 대기 시간 없이 바로 펼쳐집니다.
이미 구독 중인 이메일을 입력하셔도 즉시 잠금이 해제됩니다.
STEP 1. '완료' 문장 대신 파일 상태를 확인하는 verify.py 작성
먼저 프로젝트 루트의 scripts/verify.py에 내가 절대 놓치면 안 되는 완료 기준 3가지를 적습니다. 아래 예시는 필수 산출물 파일이 실제로 존재하는지, 비어 있지 않은지, 금지된 임시 표시(TODO, {{)가 남지 않았는지를 0.1초 만에 검사합니다.
#!/usr/bin/env python3
import sys, pathlib
REQUIRED = ['README.md', 'output/summary.json']
FORBIDDEN = ['TODO:', '{{PLACEHOLDER}}']
errors = []
for rel in REQUIRED:
p = pathlib.Path(rel)
if not p.exists():
errors.append(f'[누락] 필수 파일이 없습니다: {rel}')
elif p.stat().st_size < 20:
errors.append(f'[빈 파일] 내용이 너무 짧습니다({p.stat().st_size}B): {rel}')
else:
txt = p.read_text(encoding='utf-8', errors='ignore')
for bad in FORBIDDEN:
if bad in txt:
errors.append(f'[미완성 토큰] {rel} 안에 {bad!r} 가 남아 있습니다.')
if errors:
print('✖ VERIFY FAILED — 아직 완료 보고를 할 수 없습니다:', file=sys.stderr)
for e in errors:
print(' -', e, file=sys.stderr)
sys.exit(2)
print('✓ VERIFY PASSED — 모든 필수 산출물 확인 완료')
sys.exit(0)
STEP 2. Claude Code 설정에 PreToolUse · PostToolUse · Stop 연결하기
이제 프로젝트 폴더 안의 .claude/settings.json에 훅을 등록합니다. 종료 코드 2가 반환되면 Claude Code는 표준 에러(stderr) 메시지를 읽고 스스로 누락된 파일을 마저 고친 뒤 다시 종료를 시도합니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 scripts/guard_bash.py"
}
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "python3 scripts/verify.py"
}
]
}
]
}
}
mkdir -p harness-demo/scripts && cd harness-demo# 일부러 output/summary.json 을 안 만든 상태에서 검사기 실행python3 scripts/verify.py# 출력: ✖ VERIFY FAILED — [누락] 필수 파일이 없습니다: output/summary.json
STEP 3. 범위를 넘는 억지 수정을 막는 '정직한 인계' 지시문
Stop 훅만 걸어 두면 에이전트가 통과만 하려고 빈 더미 데이터를 채워 넣는 부작용이 생길 수 있습니다. 아래 프롬프트를 함께 주면 권한 밖의 오류는 억지로 덮지 않고 남은 오류 목록으로 정직하게 인계합니다.
자주 겪는 문제: Stop 훅이 무한 반복될 때는 어떻게 하나요?
검사 조건이 현재 작업과 무관한 옛 파일까지 요구하고 있을 가능성이 큽니다. verify.py 는 '항상 참이어야 하는 저장소 무결성(깨진 JSON 없음, 필수 스키마 일치)'만 검사하게 두고, 작업별 일회성 산출물은 별도 인자로 분리하면 무한 루프를 막을 수 있습니다.