AI 코딩 도구를 쓰다 보면 같은 문제가 반복된다. 규칙 문서에 '이렇게 해라'라고 아무리 적어 둬도 모델은 슬쩍 피해 가고, 대화가 길어지면 그 규칙은 조용히 사라진다. 그래서 AI 코딩 도구가 믿을 만한지는 '모델이 얼마나 똑똑한가'로 정해지지 않는다. 진짜로 그걸 굴리는 건 모델을 감싸 통제하는 장치, 곧 하네스(harness)다. 하네스의 힘은 모델의 지능이 아니라 그 주변을 채운 결정론적 인프라에서 나온다. 결정론적 인프라란 입력이 같으면 결과도 늘 같은, AI 추론이 아닌 코드 장치를 말한다. 클로드 코드(Claude Code, 이 AI 코딩 도구)의 소스를 뜯어보면 그런 코드가 98.4%다. 규칙 문서(rule)·기능(skill)·작업 흐름(workflow)·기억(memory)·여러 AI를 지휘하는 조율기(orchestrator)는 전부 그 위에 얹힌다. 그 맨 아래 토대가 바로 hook pipeline(도구를 실행할 때마다 끼어드는 감시 스크립트들의 사슬)이다. 지시문이나 규칙은 문서에 적힌 '이렇게 해줘'라는 부탁(이 글의 라벨로는 SOFT)이다. 그래서 모델이 입력만 살짝 바꾸면 피해 간다. 대화가 길어져 자동 요약될 때(이른바 compaction)에는 조용히 사라진다. 반대로 hook이 돌려주는 종료 신호 exit code(작업을 통과시킬지 막을지 알리는 숫자, 이 글의 라벨로는 HARD)는 모델이 손댈 수 없는 바깥에서 작동한다. 그래서 아무도 무시하지 못하게 길을 막는다. 나는 13개의 순간(이벤트)마다 127개의 감시 스크립트를 걸어, 286개 규칙 중 핵심을 exit 2(차단 신호)로 강제한다.
근거 — github.com/jung-wan-kim/cc-sync · 내 개인 설정 폴더(홈 폴더 아래 .claude 폴더)를 그대로 공개한 저장소다. 설정 파일과 hook 스크립트, 규칙 문서가 담겨 있다
결론부터 말하면, AI 에이전트(스스로 도구를 골라 쓰며 일하는 AI)가 믿을 만해지는 힘은 대부분 '정해진 대로만 도는 코드'에서 나온다. 클로드 코드의 소스를 뜯어보면 에이전트 신뢰성의 98.4%가 결정론적 인프라(입력이 같으면 결과도 같은 코드 장치를 말한다. 권한을 막는 검문소, 대화 맥락 관리, 오류 복구 같은 것들이다)에서 나온다. AI 추론이 기여하는 몫은 1.6%뿐이다. 하네스를 건물처럼 층으로 그려 보면 맨 위가 모델의 추론이다. 그 아래가 여러 AI를 지휘하는 조율기(orchestrator)·기능(skill)·규칙(rule)·기억(memory) 같은 '운영 지식'이다. 그리고 이 모든 층이 실제로 지켜지도록 강제하는 맨 아래 토대가 바로 hook pipeline(도구를 실행할 때마다 끼어드는 감시 스크립트들)이다. 규칙(rule)이 '무엇을 하라'고 적어둔 종이라면, hook은 '안 하면 아예 못 지나간다'고 길을 막는 문지기다. 이 토대가 없으면 위층은 지켜도 그만 안 지켜도 그만인 부탁일 뿐이다.
SOFT(부탁 층인 규칙 문서)는 그렇다면 왜 토대가 못 되고, 강제 층인 hook만 토대가 될까? 똑같은 내용의 규칙이라도, 그것을 어디에 두느냐가 강제력을 가른다. 문서에 적으면 부탁이 되고, 감시 스크립트에 넣으면 문지기가 된다. 아래 두 카드가 그 둘을 나란히 놓은 것이다.
SOFT — prose"이렇게 해주세요" — 지켜주길 바라는 부탁
CLAUDE.md(프로젝트 지시문 파일)이나 규칙 파일 같은 설정 문서에 그냥 글로 적혀 있다compaction(대화가 길어질 때 일어나는 자동 요약)이 한 번 지나가면 조용히 사라진다HARD — hook exit code"조건 안 맞으면 → 차단" — 구조로 막는 강제
SOFT(부탁 층)은 모델이 읽는 영역 안에 있어서, 대화가 요약될 때 사라지거나 입력만 바꿔도 우회당한다. 같은 규칙이 반복해서 무시되는 게 감지되면, 그 규칙을 hook(별도 프로그램)으로 옮겨 종료 신호로 막는 문지기로 '승격'시킨다. 그러면 더는 무시할 수 없다.AI가 한 번 일하는 흐름(세션이라 부른다)은 아무 검문 없이 흘러가지 않는다. 세션 시작 → 사용자 입력 → 도구 실행(여러 번) → 종료로 이어지는 모든 길목마다 hook(감시 스크립트)이 끼어든다. 나는 이 13개의 길목마다 clawd-hook.js라는 공통 관측 스크립트(무슨 일이 일어났는지 기록하는 역할)를 깔고, 그 위에 길목별로 문지기와 정보 주입기를 얹었다. 역할은 네 가지다 — 차단(막기, 종료 신호 2) · 정보 주입(끼워 넣기, 맥락 보강) · 검사(확인·기록) · 관측(지켜보기).
ensure-default-modelauto-apply-permissionsprompt-enhancerself-improve-checkmid-loop-reminderpremature-remindergit-push-adversarialno-env-commitno-localstorageworkflow-cost-gateformatter-checksql-injection-checkdependency-auditwork-recheck-ledgerwork-recheck-gatepremature-detectormid-loop-detectorself-improve-triggerclawd-hookfact-extractsubagent-snapshotsubagent-qa-directiveexit 2(차단 신호)를 돌려주는 hook은 그 도구 호출 자체를 물리적으로 막아 버린다. 아래 문지기들은 '다 끝냈다'는 모델의 자기 보고를 믿지 않는다. 대신 실제 증거(증거 파일·지문 해시·합격 조건 통과 기록)를 요구한다. 증거가 없으면 코드 올리기(push)도, 저장(commit)도, 작업 흐름(workflow) 실행도, 세션 종료도 통과되지 않는다.
qa-gate-before-push.shPreToolUse[Bash]코드를 고쳤는데 'QA(품질 검사)를 통과했다'는 증거 파일 .qa-cycle-passed(합격 표시·지문·1시간 이내 기록)이 없으면 코드 올리기(push) 차단. 지문(그때의 코드 상태를 요약한 해시값)이 지금 코드와 안 맞아도 막는다.
git-push-adversarial-review-gate.shPreToolUse[Bash]CRITICAL(치명적 문제, 곧 리뷰가 매기는 가장 심각한 등급)이 다른 AI(Codex)의 트집 잡기식 교차 검토 결과에 하나라도 남아 있으면 코드 올리기 차단. 한 모델이 스스로 검사할 때 놓치는 맹점을 다른 모델로 메운다.
workflow-cost-gate.shPreToolUse[Workflow]여러 AI를 한꺼번에 돌리는 작업 흐름(workflow)을 부를 때, 정해진 6가지 방식 중 하나를 밝히지 않으면 차단. '검토자 5명 소집' 같은 값비싼 낭비를 구조로 막는다.
work-recheck-gate.shStop · exit 2 ×4의미 있는 작업을 해놓고 '합격 조건을 실제로 통과했다'는 증거로 다시 확인하지 않으면 세션 종료 차단. 실제론 안 됐는데 '다 됐다'고 하는 거짓 완료를 끝나기 직전에 붙잡는다.
no-env-commit.shPreToolUse[Bash].env(비밀 값을 모아 두는 환경 설정 파일)이나 API 키, 비밀번호 같은 민감 정보를 저장(commit)하려 하면 차단. 암호처럼 인코딩해 숨긴 비밀 값까지 뒤져서 잡는다.
no-localstorage-universal.shPreToolUse[Edit|Write]웹 파일에서 브라우저 저장소 localStorage를 쓰는 것을 차단 (로그인 토큰 저장만 예외). 사용자 데이터는 브라우저 대신 서버에 둬야 하기 때문이다.
scaffold-violation-check.shPreToolUse · exit 2 ×2프로젝트마다 정해둔 뼈대 규칙(scaffold rule) 문서에서 NEVER DO('절대 하지 마라')로 표시된 항목을 어기면 즉시 차단한다. 그 프로젝트의 관례를 구조로 강제하는 것이다.
task-quality-gate.shproject · acceptance화면이든 아니든 무언가 고치고 '합격'이라고 판정했는데(판정 기록에는 verdict=PASS라고 적힌다), 그 옆에 acceptance_verified[](합격 조건을 하나씩 실제로 통과했다는 증거 목록)가 없거나 안 채워졌으면 차단. 시험을 위한 시험으로 눈속임하는 걸 막는다.
이 페이지를 조사하다가, 아무것도 바꾸지 않는 단순 읽기 명령을 하나 실행했는데 그게 문지기 스크립트 qa-gate-before-push.sh에게 HARD BLOCK(강제 차단) 당했다. 내 명령 안에 grep '…|git push|…'처럼 "git push"라는 글자가 들어 있었기 때문이다. 문지기는 명령 글자 속에서 그 조각을 발견하자마자, 안전을 위해 무조건 막았다.
이게 바로 hook 강제의 본질이다 — 문지기는 내 의도를 헤아려 주지 않는다. 정해진 조건(글자나 증거)이 안 맞으면 그냥 막는다. 그 바람에 멀쩡한 명령까지 가끔 잘못 막히는 대가가 따르지만, '무시할 수 있는 규칙'보다 '가끔 지나치게 막는 규칙'이 시스템 신뢰에는 훨씬 이득이다.
hooks/workflow-cost-gate.sh — 작업 흐름을 강제하는 검문 스크립트
PreToolUse[Workflow](작업 흐름을 실행하기 직전에 끼어든다). 문서로만 적혀 있던 두 가지 부탁
(필요할 때만 켜기 / 사용량 측정)을 HARD exit code(무시할 수 없는 종료 신호)로 못박아 닫는다.
exit 0 = 통과 (방식을 밝힘)
exit 2 = 차단 (방식을 안 밝힘)
우회 스위치(이 검문을 일부러 끄는 환경 변수): WORKFLOW_COST_GATE=0맨 위층인 동적 작업 흐름(dynamic workflow, 상황에 따라 여러 AI를 즉석에서 짜서 돌리는 방식)조차 이 토대 위에서만 안전하다. 이 방식은 강력하지만 값비싸다. 하위 AI 수십 개를 한꺼번에 띄우기 때문이다. 그래서 workflow-cost-gate.sh(작업 흐름 비용 검문 스크립트)는 모든 작업 흐름 호출이 정해진 6가지 방식 중 하나를 meta.description(작업 흐름의 설명 항목) 또는 // pattern: 주석에 분명히 밝히도록 요구한다. 안 밝히면 exit 2(차단)이다. 통과한 호출은 workflow.jsonl(호출 기록 파일)에 기록으로 남는다. 즉 위층의 값비싼 작업 흐름이 증거를 만들고, 맨 아래 hook이 그 증거를 검사한다. 토대와 위층이 서로 맞물려 도는 순환 구조다.
workflow-cost-gate(작업 흐름 비용 검문)은 작업 흐름을 호출하면 6가지 정해진 방식(분류 후 처리 / 펼쳐서 합치기 / 반박 검증 / 뽑아서 거르기 / 토너먼트 / 될 때까지 반복) 중 하나를 밝혔는지 확인한다. 안 밝히면 차단 신호(exit 2)로 막고, 밝히면 실행한 뒤 .claude/logs/workflow.jsonl(호출 기록 파일)에 남긴다. '값비싼 건 함부로 쓰지 마라'는 원칙을 문지기의 종료 신호로 못박은 것이다.classify-and-act
fan-out-and-synthesize
adversarial verification
generate-and-filter
tournament
loop-until-done
값비싼 위층(천장)인 작업 흐름이 증거를 만들고, 늘 켜져 있는 바닥인 hook이 그 증거를 검사한다. 예를 들어 team-deliver.js 작업 흐름이 항목마다 '합격 조건을 통과했다'는 기록 acceptance_verified[] 를 .qa-evidence.json 파일에 남기면, task-quality-gate.sh (강제 문지기)가 그 증거가 채워졌는지 종료 신호로 확인한다. 위층이 만들고 바닥이 잠그는 — 서로 맞물려 도는 강제의 순환이다.
workflow-cost-gate(작업 흐름 비용 검문)가 강제로 못박는 '부탁'들의 출처다. 6가지 방식과 3가지 실패 유형, 바닥과 천장의 이중 방어를 설명한다.
hook·규칙·기능·기억이 한데 엮인 하네스 전체 구조. hook은 그중 강제를 맡는 층이다.
System내 개인 설정 폴더(홈 폴더 아래 .claude 폴더)를 공개한 저장소가 어떻게 자라 왔는지 적은 글이다. 127개 hook과 286개 규칙이 사는 곳의 변천사다.
Source