문제는 이것이었다. 내 하네스에는 사용자 단위·프로젝트 단위의 자동 검사 파이프라인은 있는데, 에이전트 하나하나에 따로 거는 검사는 없었다. 그래서 "플랫폼이 지원하지 않는다"는 통념이 정말 맞는지 직접 실측했다. 통념은 절반만 맞았다. Claude Code 플랫폼은 이미 세 가지 방식으로 지원하고 있었다. 없는 것은 하네스가 그걸 쓰지 않은 것뿐이었다. 89개 에이전트 중 채택은 0개였다. 용어를 짚어 두면 이렇다. 하네스는 AI가 일을 다 했다고 거짓말하지 못하게 증거를 강제하는 감시 장치다. 파이프라인(pipeline)은 그 검사가 순서대로 이어지는 흐름이다. 에이전트(agent)는 역할을 나눠 맡는 AI 작업자다. 이 글에서는 에이전트마다 따로 거는 그 검사를 에이전트 스코프 훅이라 부른다. 이 페이지는 그 원인분석과 실측, 그리고 만들어 본 파이프라인을 정리한다.
먼저 용어 하나. 훅(hook)은 "특정 순간이 오면 자동으로 실행되는 작은 검사 스크립트"다. 예를 들어 파일을 저장할 때마다 문법을 검사하는 것, 서버에 코드를 올리기 직전 테스트가 통과했는지 확인하고 막아서는 관문이 훅이다. Claude Code에서 이 훅은 지금까지 "사용자 전체" 또는 "프로젝트 하나" 단위로만 거는 줄 알려져 있었다. 이렇게 설정이 적용되는 범위를 스코프(scope)라고 부른다. 그런데 "에이전트 하나하나마다 다른 훅을 건다"는 건 못 한다는 통념이 있었다. 이 글의 주제인 에이전트 스코프 훅이 바로 그것이다. 실측해 보니 플랫폼은 이미 세 가지 방법으로 이걸 지원하고 있었다. 진짜 문제는 우리 하네스가 89개 에이전트 중 단 하나도 그 기능을 쓰지 않았다는 것이었다.
원인은 두 겹이다. 하나는 기능이 늦게 생긴 플랫폼의 역사다. 다른 하나는 그 기능이 생긴 것을 알아채지 못한 우리 하네스의 사각지대다. 진짜 원인은 오른쪽, 하네스 쪽이다.
"원래 훅은 설정 파일만 소유했다"
"기능이 생긴 걸 관측할 표면이 없었다"
방안의 심장은 분배기(dispatcher)다. 분배기란 설정 파일에 걸어 둔 훅 하나가 "지금 어느 에이전트냐"를 읽어 알맞은 검사로 넘겨주는 장치다. 그래서 89개 에이전트 파일을 하나도 안 건드리고 중앙에서 에이전트별 정책을 관리할 수 있다. 훅에 들어오는 입력에는 "어느 에이전트냐"를 알려주는 에이전트 종류 필드가 담겨 있다. 분배기는 그 값만 본다. 아래는 실제로 돌려 본 결과다. 그림에서 분배기가 읽는 그 필드의 이름은 agent_type이다. 똑같은 date 명령이 메인 대화에선 통과하고, 특정 에이전트 안에서는 막힌다.
권장 순서는 C → B → A다. 관측(C)을 먼저 깔아야 다음 두 층의 효과가 수치로 측정된다. 분배기(B)가 중앙 규약을 세운 뒤, 개별 훅(A)은 꼭 필요한 고위험 에이전트에만 붙인다. 이 순서가 유지비가 가장 낮다.
텔레메트리(관측 로그)에 두 필드를 추가한다. 하나는 어느 에이전트 안에서 돌았는지 알려주는 에이전트 종류이고, 다른 하나는 그 실행 하나하나를 구분하는 에이전트 고유 번호다. 이게 없으면 아래 두 층을 깔아도 "훅이 에이전트 안에서 돈다"는 사실이 측정에 안 잡힌다. 몇 줄짜리 변경이다. 두 필드의 이름은 차례로 agent_type과 agent_id(실행마다 따로 붙는 번호)다.
설정 훅 하나가 훅 입력에 실리는 에이전트 종류 필드를 읽어 에이전트별 검사로 넘긴다. 89개 에이전트 파일을 안 건드리고 중앙에서 정책을 관리한다. "특정 에이전트 안에서만 위험 명령 차단" 같은 강제 게이트(gate — 조건을 못 채우면 실행 자체를 막는 관문)를 실증 완료했다. 분배기가 읽는 그 필드의 이름은 agent_type이다.
에이전트 정의 파일에 훅을 직접 붙인다. 사용자 스코프(내 컴퓨터 전체에 적용되는 범위)의 에이전트는 신뢰 승인 배관 없이 바로 동작한다(실측). 인프라를 되돌릴 수 없게 만지는 배포 담당 devops, 사용자 대신 결과를 검증하는 user-proxy 같은 고위험 에이전트에만 선별 적용한다.
| 지원 방식 | 플랫폼 상태 | 하네스 채택 | 실측 증거 |
|---|---|---|---|
| frontmatter 훅 | 지원됨 — 그 에이전트가 도는 동안만 유효 | 0 / 89 | debug 로그(개발자용 상세 실행 기록) Registered 1 frontmatter hook(s) |
agent_type 입력(에이전트 종류 필드) |
지원됨 — 서브에이전트(메인 대화가 일을 맡기려고 띄우는 하위 에이전트) 훅에만 존재 | 미사용 | 에이전트 3건 vs 메인 0건 (덤프) |
| SubagentStart 필터 | 지원됨 — 에이전트 이름으로 매칭 | 미사용 | matcher(이름 필터) 4/4 발화, 오발화 0 |
| 신뢰 승인 관문 | 프로젝트 에이전트만 필요 · 사용자 스코프는 불필요 | 즉시 채택 가능 | "not trusted"(신뢰 안 됨) 에러 재현 |
방안을 실제 코드로 만든 뒤, 다른 모델(Codex)에게 "이건 헛소리다"라고 가정하고 결함을 찾게 했다. 이런 검증을 적대 리뷰라고 부른다. 라운드마다 진짜 결함이 나왔다. 결함에는 등급을 매겼다. 핵심 로직의 심각 결함은 6라운드에서 사라졌다. 이후는 곁가지(로그 유틸리티)의 하드닝(hardening — 우회 경로를 하나씩 막아 단단하게 만드는 일)이 이어지다 13라운드에서 전부 통과(CLEAN)로 수렴했다. 아래 타임라인에서 위쪽 붉은 마커는 심각 등급, 아래 회색 마커는 높음 등급이다. 심각 등급은 반드시 고쳐야 배포할 수 있는 결함이다. 높음 등급은 심각 다음으로 위험한 결함이다. 리뷰 도구가 두 등급을 적는 원래 표기는 차례로 심각 등급(CRITICAL)과 높음 등급(HIGH)이다.
"헛소리라고 가정하라"는 적대 검증이 실제로 결함을 잡았다. 그중 하나는 내가 놓칠 뻔한 진짜 버그였다. 로그 유틸(기록을 파일에 쓰는 작은 도구)을 파이썬으로 바꾸면서 입력 통로가 막혀 데이터가 하나도 안 써졌다. "실제로 기록됐는지" 검증하는 습관 덕에 잡았다. 결론은 단순하다. 에이전트 스코프 훅은 이미 있었고, 없던 건 채택뿐이다. 관측(C) → 분배기(B) → 개별 훅(A) 순으로 3층을 세우면 89개 에이전트를 안 건드리고도 에이전트별 강제 게이트를 얻는다.