Harness · 관측 복구 · 2026-08-01

훅 605개가 돌고 있었는데
볼 방법이 없었다

문제는 이것이었다. 내 Claude Code 하네스(harness, AI가 다 했다고 거짓말 못 하게 증거를 강제하는 감시 장치)에는 훅(hook, 도구가 실행되기 전후에 자동으로 끼어들어 위험한 명령을 막거나 규칙 위반을 잡는 작은 검사 스크립트)이 수백 개 걸려 있다. 그런데 에이전트(agent, 역할을 맡은 보조 AI)와 스킬(skill, 정해진 절차를 담은 작업 설명서) 안에서도 그 훅이 실제로 도는지를 알 방법이 없었다. 조사해 보니 답은 "돈다"였다. 다만 조사 도중 더 큰 구멍이 드러났다. 프로젝트 스코프(scope, 훅이 등록된 범위를 뜻한다. 프로젝트 폴더 안의 설정 파일에 걸린 훅이 프로젝트 스코프다)의 훅 605개는 실행돼도 기록이 전혀 남지 않았다. 그래서 기록을 남기는 장치를 복구했다. 그러고 나서야 서브에이전트(subagent, 메인 대화가 일을 나눠 맡기려고 띄우는 보조 AI 세션) 안에서 훅이 실행된 횟수가 처음으로 보였다. 누적 11,502건이었다.

한 문장으로 줄이면, 돌고 있었다. 다만 볼 수 없었다

원래 질문은 "에이전트·스킬 스코프 훅 파이프라인이 존재하고 실제로 도는가"였다. 파이프라인(pipeline)이란 훅이 등록되고, 호출되고, 결과가 기록되기까지 이어지는 한 줄의 흐름을 말한다. 답은 둘로 갈린다. 첫째, 전역 훅은 서브에이전트 안에서 확실히 돈다. 발화(훅이 실제로 한 번 실행되는 것)가 누적 11,502건이고, 25종의 에이전트에서 돌았으며, 실제로 작업을 막은 차단이 18건이다. 둘째, 에이전트·스킬마다 다른 훅을 거는 "차등" 기능은 채택이 0이다. 플랫폼(Claude Code 자체)은 이미 세 가지 방식으로 지원하는데, 내 하네스는 그중 하나도 쓰지 않았다. 그리고 이 사실을 확인하려다 프로젝트 스코프 605개 훅이 통째로 측정 밖이라는 별개의 문제가 드러났다.

11,502서브에이전트 내 발화(누적)
25에이전트 종류
18에이전트 내 실제 차단
0 / 89 · 0 / 555agent · skill 차등 채택
605project 훅
0% → 100%관측 커버리지
11교차 리뷰 적발 결함
8h 03m소요
1단계 · 타당성

분석이 맞는지부터 다시 쟀다

먼저 분석 문서를 읽지 않은 blind(블라인드, 남의 결론을 모르는 채로 보는 방식) 감사 에이전트 2기를 띄워 디스크의 원본을 독립적으로 다시 세게 했다. 코드에 대한 주장 24항목은 전부 맞았다. 수치는 2건이 정정됐고, 가정 하나는 반증됐다.

2단계 · 복구

래퍼를 씌워 발화를 기록

래퍼(wrapper, 훅을 감싸서 대신 실행하고 그 실행 기록을 남기는 껍데기 스크립트)를 씌웠다. 정본 파일 4개를 고치고 19개 프로젝트에 롤아웃(rollout, 고친 것을 실제 프로젝트마다 배포하는 일)했다. 이제 훅이 한 번 발화할 때마다 (태그, 스코프, 훅명, exit) 네 값이 한 줄로 남는다. exit는 종료 코드로, 0이면 통과이고 2면 차단이다.

3단계 · 검증

내 수정에서 결함 11건이 나왔다

Claude로 돌린 QA(품질 검증)는 4라운드 내내 전건 통과(PASS) 판정이었다. 같은 코드를 다른 모델인 Codex에 넘기자 결함 11건이 나왔다. 등급별로는 치명 CRITICAL 3건, 높음 HIGH 4건, 중간 MEDIUM 4건이다.

에이전트·스킬 스코프는 존재하는가, 도는가

두 질문을 분리해야 답이 정확해진다. ①전역 훅이 에이전트 안에서도 도는가②에이전트·스킬마다 다른 훅을 걸 수 있는가는 다른 얘기다. 전역 훅이란 내 계정 전체 설정 파일에 걸려 모든 대화에 똑같이 적용되는 훅이다. ①의 답은 "돈다"이고, ②의 답은 "플랫폼은 되는데 우리가 안 썼다"이다. 그리고 ①을 증명할 수 있게 된 것은 이번 작업 이후다. 그전까지 텔레메트리(telemetry, 훅이 언제 어디서 돌았는지를 자동으로 남기는 기록 장치)는 훅 이름만 남기고 어느 에이전트 안에서 돌았는지를 기록하지 않았다. 도는 걸 알 수 없으니 "없는 것"처럼 보였던 셈이다.

두 갈래 — 전역 훅의 에이전트 내 작동 vs 에이전트별 차등 ① 전역 훅이 에이전트 안에서 도는가 → 돈다 (실측 확인) settings.json 전역 훅 메인 대화 · 서브에이전트 동일 적용 서브에이전트 25종 내부 발화 11,502건 exhaustive-auditor · web-qa-tester · viz-recheck … 실제 차단 18건 (exit 2) 동적명령 7 · scaffold 위반 6 · 세션탈취 4 · 스킬스캔 1 10개 에이전트에 이름까지 귀속됨 SubagentStop 62건 기록 종료 이벤트도 발화 — 파이프라인 실재 “없다”가 아니라 “안 보였다” agent_type 도입(7/31 14:01Z)~8/1 04:27Z 누적 ② 에이전트·스킬마다 다른 훅 → 플랫폼은 지원, 채택 0 agent frontmatter hooks 0 / 89 에이전트 정의 파일에 훅 선언 — 사용 0 skill frontmatter hooks 0 / 555 1회용(once) 훅도 여기서만 동작 — 사용 0 Subagent 이벤트 agent-type matcher matcher 전부 빈 값 등록은 됐으나 전 에이전트 일괄 적용 결과 — 모든 에이전트가 같은 훅만 받는다 고위험 에이전트에만 더 조이는 것이 불가능 파손이 아니라 미채택 — 기회 손실 차등은 못 하지만 사각은 아니다 전역 훅이 이미 모든 에이전트를 덮고 있다
왼쪽은 이번에 실측으로 증명된 것이고, 오른쪽은 플랫폼 기능이 있는데 쓰지 않은 것이다. 두 질문을 섞으면 "파이프라인이 없다"는 잘못된 결론이 나온다.
차등 방식플랫폼 지원하네스 채택실작동 증거
전역 훅 (계정 설정 파일 settings.json) 지원. 메인·서브에이전트에 동일하게 적용 전면 사용 발화 11,502건 · 차단 18건(동적 명령 7 · scaffold(프로젝트 뼈대 규칙) 위반 6 · 세션 탈취 4 · 스킬 스캔 1). 차단은 10개 에이전트에 이름까지 귀속됐다
agent frontmatter hooks: (에이전트 정의 파일 머리말에 훅을 직접 선언하는 방식) 지원 (프로젝트 에이전트는 신뢰 승인 관문 동반) 0 / 89 격리 실험으로 동작 확인, 운영 채택 없음
skill frontmatter hooks: (스킬 정의 파일 머리말에 훅을 선언하는 방식) 지원. 한 번 실행되면 자동으로 풀리는 once: true 옵션은 여기서만 동작한다 0 / 555 격리 실험으로 동작 확인, 운영 채택 없음
Subagent 이벤트의 agent-type matcher (서브에이전트가 시작·종료할 때 어느 에이전트 종류에 훅을 적용할지 고르는 필터) 지원 등록됨, 단 matcher 전부 빈 값 SubagentStop(서브에이전트 종료 이벤트) 62건 발화. 다만 전 에이전트에 일괄 적용
왜 "없다"고 보였나

텔레메트리가 훅 이름만 기록하고 어느 에이전트 안이었는지는 남기지 않았다. 그래서 "에이전트 안에서 훅이 돈다"는 사실이 측정에 잡히지 않았고, 갭(gap, 있어야 할 것과 실제 사이의 빈틈)이 갭으로 보이지도 않았다. 이번에 기록 한 줄마다 에이전트 종류를 담는 agent_type 필드와 에이전트 개체 번호를 담는 agent_id 필드를 넣고 나서야 위 수치가 처음 나왔다. 측정을 만들자 질문이 답을 얻은 셈이다. 훅은 이미 돌고 있으므로 남은 과제는 고위험 에이전트에만 더 조이는 차등을 켜는 것이다.

같은 훅, 다른 가시성

같은 훅인데 보이는 쪽과 안 보이는 쪽이 갈려 있었다. 훅은 두 범위에 등록된다. user 스코프는 내 계정 전체에 적용되는 설정 파일(~/.claude/settings.json)이고, project 스코프는 프로젝트 폴더 안의 설정 파일이다. user 스코프 훅 중 37개는 텔레메트리 래퍼 hook-run.sh(훅을 대신 실행하고 그 발화를 기록 파일에 한 줄 남기는 스크립트)를 경유해 기록을 남긴다. 그런데 프로젝트 스코프 훅은 605개 전부가 래퍼를 거치지 않았다. 설치기(프로젝트마다 훅을 등록해 주는 스크립트)가 처음부터 래퍼 없이 등록했기 때문이다. 프로젝트 설정 파일에서 래퍼 이름을 세는 명령 grep -c 'hook-run.sh'의 결과가 0이었다.

마이그레이션 전 — 훅 실행 경로 두 갈래 Claude Code 플랫폼 도구 호출 시 훅 이벤트 발생 user 스코프 · 54개 hook-run.sh (래퍼) · 37개 경유 실제 훅 실행 (exit 0 / 2) 기록 남음 → dead-hook 판정 가능 project 스코프 · 605개 래퍼 없음 · 0개 경유 실제 훅 실행 (정상 차단함) 기록 없음 → 죽어도 모름 실행은 양쪽 다 정상 — 갈라지는 것은 “그 실행을 아는가”뿐 하네스 강제 표면 659개 중 621개(94.2%)가 측정 밖이었다
발화 여부와 관측 여부는 다른 축이다. 오른쪽 경로의 훅도 차단할 것은 차단했고 게이트(gate, 조건을 못 채우면 작업을 막는 관문) 역할도 지켰지만, 그 사실을 확인할 수단이 없었다.
분석을 믿기 전에 다시 쟀다

구현에 들어가기 전에 분석부터 다시 쟀다. 요청은 "타당성을 입증하고 개선안을 짜서 진행하라"였다. 그래서 구현 전에 분석 문서를 읽지 않은 감사 에이전트 2기를 띄워 디스크에서 직접 세게 했다. 한쪽은 수치를 전수 조사했다. 19개 프로젝트의 설정 파일 settings.json을 하나하나 파싱해 훅 개수를 셌다. 다른 쪽은 코드에 대한 주장 24항목을 대조했다. 설치기의 어느 줄이 문제인지, 리포터(보고서를 만드는 스크립트)의 분모에 무엇이 들어가는지, 매처가 어떻게 배선돼 있는지 같은 것들이다. 결론은 분석이 타당했다였다. 다만 그대로는 아니었다.

검증 항목분석 문서재실측판정
project 훅 래퍼 경유00 / 605확인 핵심 주장
중복 등록 분포33건 / 5개 프로젝트15+15+1+1+1 완전 일치확인
user 스코프 구성54 = 53 command + 1 http정확히 일치확인
설치기 코드 주장설치기 224번째 줄(L224)에 래퍼 없음 외 24항목전건 일치확인
영향 프로젝트 수18개19개 (+1)정정 심층 중첩 누락
영향 훅 수582개605개 (+23)정정
"설치 프로젝트는 전부 git 저장소"손실 커버리지 03개는 자체 .git 폴더(git 저장소임을 나타내는 폴더)가 없음반증 설계 보정
반증이 만든 실제 결함

래퍼는 기록마다 프로젝트를 구별하는 태그를 붙이는데, 그 태그는 현재 폴더에서 위로 올라가며 .git 폴더를 찾아 만든다. 자체 .git이 없는 3개 프로젝트는 이 상위 탐색이 전부 홈 디렉토리에서 멈춰 같은 태그를 받았다. 태그가 겹치면 한 프로젝트의 발화가 다른 프로젝트의 죽은 동명 훅을 살아있게 보이게 만든다. 아무 경고도 없는 오판이다. 정지 조건을 프로젝트 설정 파일 .claude/settings.json 마커 우선으로 바꿔 해소했다. 정상 프로젝트의 태그는 그대로 유지됐다.

4파일을 함께 고쳐야 했다

"설치기 한 줄만 고치면 된다"는 초안은 적대 리뷰(adversarial review, 코드를 일부러 깨뜨리려는 관점으로 검토하는 리뷰)에서 무너졌다. 구조 문제가 세 가지였다. 래퍼가 자기 자신을 등록했다. 설치기가 append-only(기존 등록을 지우지 않고 덧붙이기만 하는 방식)라 같은 훅이 두 번 실행됐다. 리포터 분모에 project 스코프가 없어 다 고쳐도 리포트에 안 잡히는 구조였다. 그래서 배치·조립·마이그레이션(migration, 기존 설정을 새 형식으로 옮기는 작업)·집계 네 지점을 한 단위로 바꿨다.

마이그레이션 후 — 배치 · 조립 · 치환 · 집계 ① 배치 hook-templates/_lib/ glob 밖 — 자기등록 구조적 차단 ② 조립 + 치환 install-project-hooks.sh 정규화 후 제거 → 래퍼 1건 삽입 (exact-once) 결과 settings.json × 19 중복 33 → 0 · 절대경로 0 ③ 래퍼 — hook-run.sh (투명 통과) stdin 그대로 전달 · exit code 그대로 전파 · stdout/stderr 무오염 project_tag (salt+sha256) scope (user/project) agent_type / agent_id hook-triggers.jsonl 한 줄 = 한 발화 · 전수 유효 JSON ④ 집계 — dead-hook-report.sh 동일성 키 = (scope, project_tag, 훅명) · 판정불가는 보류 버킷으로 분리 네트워크 호출 0 · 자유 입력 문자열 미재현
래퍼는 투명해야 한다. 게이트가 차단하던 것은 래퍼를 씌운 뒤에도 그대로 차단해야 한다. 그리고 그 사실이 이제 기록으로 남는다.
초안 (리뷰 전)

설치기 두 줄

명령 문자열에 래퍼를 앞에 붙인다

그대로 실행했다면 훅이 이중 실행되거나, 래퍼가 자기 자신을 감싸거나, 다 고쳐도 리포트에 안 잡혔을 것이다.

확정 (결함 37건 수렴 후)

4파일 + 신규 디렉토리

배치 · 조립 · 치환 · 집계를 한 단위로

래퍼를 glob(별표 같은 이름 패턴으로 파일을 한꺼번에 고르는 방식) 밖에 두어 설치기가 래퍼 자신을 훅으로 줍지 못하게 했다. 등록은 append(덧붙이기) 대신 기존 항목을 지우고 다시 넣는 치환으로 바꿨다. 리포터 분모와 동일성 키도 함께 바꿨다.

내 QA는 전건 통과였다, 그게 문제였다

Claude로 돌린 QA는 4라운드 내내 전건 통과(PASS)였다. 검사 내용은 결정론 테스트 케이스(TC, 같은 입력이면 항상 같은 결과가 나와야 하는 검사) 13건과 E2E 저니(설치부터 차단 기록까지 실제 흐름을 처음부터 끝까지 따라가는 검사) 6건이었다. 같은 코드를 Codex에 적대 리뷰시키자 CRITICAL 3건, HIGH 4건, MEDIUM 4건, 합쳐 11건이 나왔다. 더 눈여겨볼 것은 그중 2건이 "수정이 만든 새 구멍"이었다는 점이다.

교차 리뷰 5라운드 — 위쪽은 Claude 판정, 아래쪽은 Codex 판정 13/13 · 6/6 PASS 10/10 PASS 6/6 PASS FIFO 재현 검증 blind 10/10 + 결정론 재현 R1 R2 R3 R4 R5 CRITICAL 2 게이트 우회 env 하드링크 truncate + HIGH 2 HIGH 1 · MED 2 설정 동시성 락 부재 제어문자 · 개행 경로 CRITICAL 1 R2 수정이 만든 락 파일 truncation + HIGH 1 (과잉주장) NO_CRITICAL + MED 1 락 자리 FIFO → 설치 무한 대기 CRITICAL·HIGH 0 FIFO 수정 재리뷰 MEDIUM 잔여: 회귀테스트 부재 같은 코드를 두 모델이 봤는데 판정이 갈렸다 Claude QA는 계약·회귀·저니를 검증했고, Codex는 “이 코드가 어떻게 악용되는가”를 물었다 R3의 CRITICAL은 R2에서 내가 추가한 락 코드에서 나왔다 — 완화책이 새 공격면이 된 전형
위쪽 초록은 Claude QA 판정이고, 아래쪽은 같은 라운드의 Codex 판정이다. 라운드는 R1부터 R5까지 다섯 번이다(R은 리뷰 라운드 번호). 1~4라운드 동안 위아래 판정이 어긋나고, 5라운드(FIFO, 즉 이름 붙은 파이프 파일 문제의 수정을 다시 리뷰한 회차)에서 처음 함께 clean(결함 없음)이 된다.
교차 리뷰가 잡은 것

표의 라운드 표기는 R1부터 R5까지 리뷰 라운드 번호이고, 그 옆은 결함 등급이다. 치명(CRITICAL)은 강제 계층이 통째로 꺼지거나 남의 파일이 파괴되는 급이다. 높음(HIGH)은 판정을 속이거나 기록을 잃게 하는 급이다. 중간(MEDIUM)은 특정 조건에서만 새는 급이다.

라운드결함무엇이 문제였나수정
R1 CRITICAL 진단 모드가 게이트를 통째로 우회 태그 조회용 환경변수가 켜져 있으면 모든 훅이 태그만 찍고 exit 0(통과)으로 끝난다. 에러도 로그도 없이 강제 계층이 멈춘다 인자 개수로 교차검증 (인자가 없을 때만 진단 모드)
R1 CRITICAL 설치기의 복사 명령 cp가 링크 대상을 파괴 하드링크(한 파일 내용에 이름을 하나 더 붙인 것. 한쪽을 덮어쓰면 다른 이름의 파일도 함께 바뀐다)로 훅 자리를 피해자 파일에 미리 연결해 두면, 설치 한 번에 그 파일이 0바이트가 된다 프로젝트 안의 모든 쓰기를 temp 파일에 쓴 뒤 rename으로 끼우는 원자 교체(중간 상태가 남지 않는 교체)로 변경
R1 HIGH 훅 이름을 통한 JSON 인젝션(주입) 기록 파일은 한 줄이 JSON(프로그램이 읽는 데이터 형식) 한 건인데, 훅 파일명에 줄바꿈을 심으면 위조 텔레메트리 레코드를 끼워 넣어 판정을 속일 수 있다 필드 새니타이즈 (위험 문자 제거)
R1 HIGH 공백 포함 경로가 쪼개짐 프로젝트 경로에 공백이 있으면 인자가 분할돼 그 프로젝트가 리포트에서 조용히 누락 마커 목록을 배열로 다뤄 공백이 있어도 한 덩어리로 유지
R2 HIGH 설정 파일 동시 갱신 소실 원자적 rename은 크래시(중간에 죽음) 안전성일 뿐이다. 두 설치기가 동시에 돌면 나중 쓰기가 앞선 등록을 조용히 삼킨다 읽기부터 교체까지 전 구간에 배타 락(한 번에 한 프로세스만 들어가는 잠금). 락을 못 잡으면 진행하지 않는 fail-closed 방식
R2 MEDIUM 제어문자 잔존 따옴표·개행 등 4종만 지우면 탭 문자가 남는다. 그 줄은 파싱에 실패하고 레코드가 소리 없이 사라진다 제어문자 클래스 전체 제거
R2 MEDIUM 개행 포함 경로 경로에 줄바꿈이 있으면 탐색 단계에서 쪼개진다. 공백 수정으로도 닫히지 않은 잔여 경로다 NUL(문자 코드 0. 파일 이름에 절대 들어갈 수 없는 문자) 구분 탐색
R3 CRITICAL 락 파일이 새 공격면이 됨 2라운드에서 추가한 락 파일을 쓰기 모드 open("w")로 열었다. 그러면 락을 얻기도 전에 파일이 이미 truncate(0바이트로 비워짐)된다. 락 자리에 심링크(다른 파일을 가리키는 바로가기)를 심으면 무기가 된다 쓰기 플래그 없는 open + 심링크/하드링크/일반파일 3검사
R3 HIGH 보호 범위 과잉 주장 락은 협조하는 설치기끼리만 직렬화하는데 주석은 "수동 편집까지 보호"라고 적혀 있었다 주장 범위를 코드 주석에서 축소
R4 MEDIUM 락 자리에 FIFO를 두면 설치가 무한 대기 FIFO는 이름 붙은 파이프라는 특수 파일로, 열면 상대가 쓸 때까지 기다린다. 락 자리에 이걸 심어두면 열기 단계에서 타임아웃이 걸리기도 전에 영원히 멈춘다 기다리지 않는 논블록 열기 + 일반 파일 검사 (구버전은 hang, 즉 멈춤. 신버전은 즉시 거부를 실측)
R5 MEDIUM FIFO 거부에 회귀 테스트(고친 것이 다시 깨지지 않는지 매번 확인하는 테스트)가 없다 수정은 수동 재현으로 확인했지만 영속 테스트가 없어 다음 변경에서 조용히 깨질 수 있다 미조치. 후속 과제로 기록 (5라운드는 CRITICAL·HIGH 0건)
반복된 패턴

1라운드의 하드링크 방어를 넣자 2라운드에서 동시성 축(두 프로세스가 동시에 쓰는 경우)이 비어 있었다. 그걸 락으로 막자 3라운드에서 락 자신이 뚫렸다. 그걸 막자 4라운드에서 FIFO가 남았다. 완화책을 넣을 때마다 그 완화책이 지나가지 않는 경로가 남는다. 원칙을 적어두는 것으로는 부족하고, 분기마다 확인해야 한다.

19개 프로젝트에 실제로 착지했는가

"고쳤다"와 "적용됐다"는 다르다. 설치는 19개 프로젝트 중 19개에 끝났다. 로컬 커밋(commit, 변경을 저장소 이력에 기록하는 단위)은 4차례(마이그레이션 · 태그 수정 · 마커 갱신 · 보안 수정)에 걸쳐 16개 저장소에 반영했다. 각 커밋은 커밋에 들어간 파일 목록을 보여 주는 명령 git show --name-only로 의도한 .claude 경로만 들어갔는지 확인했다. 홈 디렉토리 자체가 저장소인 3개 프로젝트는 무관한 변경 230만 줄이 널려 있어 이 확인이 특히 중요했다.

착지
19설치 완료
16커밋 반영

나머지 3개는 저장소가 .claude/(또는 .claude/hooks/)를 gitignore(git이 추적하지 않을 경로 목록)로 제외한 곳이다. 파일은 적용됐고, 커밋할 것이 없어 no-op(아무 일도 하지 않음)으로 끝난 것이 정상이다.

정합
19/19고유 태그
0중복 등록

래퍼의 지문(파일 내용의 해시값. 한 글자만 달라도 값이 바뀐다)은 정본·템플릿·설치본·미러 네 곳 전부 동일하다. 설정 파일 19개는 전부 유효한 JSON이고, 절대 경로는 0건이다.

무해성
1.0게이트 강제율
0user 스코프 회귀

차단하던 훅은 래퍼를 거쳐도 그대로 차단한다(종료 코드 exit 2). 관측을 얻으면서 강제력을 잃지 않았다.

최종 재검증

구현자의 설명을 받지 않은 blind 검증자가 기준 10개를 디스크에서 직접 실측해 전건 통과했다. 실제 차단 저니(위반 입력을 넣으면 exit 2로 막히고 태그 레코드가 남는지), FIFO와 하드링크 공격 재현, 지문 4자 일치, 커밋된 내용 기준 확인까지 포함한다. 판정 근거는 실제로 돌렸고 기록이 남았다는 사실이다. "돌 것이다"라는 예상은 근거로 치지 않았다.

닫지 않은 것과, 남은 교훈
의도적으로 남긴 것

범위 밖 5+16건

숨기지 않고 보류 버킷으로 노출

템플릿 관리 밖의 커스텀 훅 5건과 user 스코프에서 아직 래퍼를 안 씌운 훅 16건은 이번 범위가 아니다. 리포터가 이것들을 판정 불가로 분리 집계해 "측정 못 한 것"이 "문제 없음"으로 둔갑하지 않게 했다.

후속 과제, 원래 질문의 나머지 절반

차등을 실제로 켜는 일

측정은 열렸고, 채택은 아직 0

이번에 연 것은 관측이다. 차등은 아직이다. 그리고 FIFO 거부에 회귀 테스트가 없다(5라운드 MEDIUM, 미조치). 다음 순서는 두 가지다. ① 기록의 agent_type 값을 보고 에이전트별로 검사를 분배하는 훅 하나를 만들어, 89개 에이전트 파일을 손대지 않고 per-agent(에이전트마다 다른) 검사를 거는 것. ② 그다음 고위험 에이전트(배포·사용자 대리)에만 frontmatter 훅을 직접 부여하는 것이다. 매처 사각(훅이 못 보는 도구 노출 8종)과 로그 로테이션(기록 파일이 커지면 잘라서 보관하는 기능) 미작동도 조사만 해둔 상태다.

측정이 없으면 이후 모든 변경의 효과를 검증할 수 없다. 그래서 관측 복구를 가장 먼저 했다. 기능을 하나 더 얹는 것보다, 이미 있는 것들이 살아 있는지 아는 쪽이 먼저였다.
1
지표를 믿기 전에 그 지표가 무엇을 세는지 본다
dead 훅(등록만 되고 한 번도 안 도는 훅) 0건은 “다 살아있다”가 아니라 “user 스코프만 셌다”였다
2
범위 측정은 탐색 조건이 결과를 만든다
문서가 한 번 교정했던 “깊이 과소평가”가 재실측에서 또 나왔다 (18 → 19)
3
고친 직후가 가장 위험하다
2라운드의 락 수정이 3라운드의 CRITICAL을 만들었다. 완화책도 리뷰 대상이다
4
같은 모델의 QA는 같은 맹점을 공유한다
전건 PASS 4회 옆에서 다른 모델이 결함 11건을 집었다

이 작업의 발단이 된 분석은 이 글이다. 에이전트 스코프 훅은 이미 있었다