에이전트 신뢰 구축 — 검증 경로부터 자동 머지까지

신뢰는 태도가 아니라 인프라의 결과물이다. “믿어보자”는 결심으로는 아무것도 안 바뀌고, 검증 경로가 생기면 신뢰는 따라온다. 그래서 순서를 바꾸면 안 된다 — R5 이전에 R9(병렬화)로 건너뛰면 토큰만 태운다.

# 원칙 위반 신호
P1 에이전트가 스스로 실행해서 확인할 수 없는 작업은 위임하지 않는다 스크린샷을 복붙하고 있다
P2 구현자와 검증자는 다른 컨텍스트여야 한다 “수정 완료했습니다”를 구현자가 말한다
P3 소프트 규칙(문서·프롬프트)은 신뢰의 근거가 아니다 CLAUDE.md 에 적었는데 안 지킨다
P4 가장 짧은 경로가 정답 경로가 되도록 설계한다 에이전트가 매번 우회로를 찾는다
P5 사람이 리뷰로 강제하는 불변식은 전부 코드 스멜이다 같은 지적을 두 번 했다

R00. 적용하면 안 되는 곳 — 먼저 거른다

자동 검증이 도달 불가능한 도메인(실제 예약·결제·외부 파트너 연동 — 여기서 검증 에이전트는 “코드를 읽고 추론”으로 퇴화한다), 도메인 규칙이 코드 밖에 있는 영역(규제 요건 — 검증할 대상이 코드베이스 안에 없다), R06 의 1~3층을 바꿀 권한이 없는 코드베이스.

해당하면 자동화 대상을 좁히는 게 답이지 방법론을 약화시키는 게 아니다. 내부 도구에서 끝까지 밀고 위험 영역은 손대지 않는다.


R01. 지금 어느 구간인지 확정

구간 증상 다음
0 불신 출력을 매번 전부 읽는다. 병렬 0~1 R02
1 관찰 툴 콜을 읽으며 실패 패턴 수집 중. 병렬 1~3 R04
2 위임 검증 경로가 있고 판정을 에이전트가 낸다. 병렬 3~8 R06
3 자동화 하드 제약이 CI/hook 에 있다. 병렬 8+ R09
4 무감독 자동 머지 후 사후 리뷰 R10

판정 질문 하나“지금 에이전트가 만든 PR 을 읽지 않고 머지한다면, 문제가 생겼을 때 며칠 뒤에 알게 되는가?” “모른다”면 구간 0이다.


R02. 검증 스킬 부트스트랩 — 사람이 검증자 자리에서 내려온다

재료 — 앱을 헤드리스로 기동하는 명령, 실행 중 앱을 조작할 채널(CDP·Playwright MCP), 관찰 채널(콘솔·네트워크·DOM 스냅샷).

  1. 먼저 사람이 손으로 한 번 한다. dev 서버 기동 → 특정 화면 도달 → 관찰. 여기서 막히면 에이전트도 막힌다.
  2. 그 과정을 boot.sh·snapshot.sh 로 스크립트화하고 .claude/skills/verify-app/ 으로 감싼다.
  3. 출력 계약을 고정한다. 산문 판정을 받으면 사람이 다시 해석해야 하고 거기서 신뢰가 샌다.
{ "verdict": "pass | fail | cannot_verify",
  "steps_taken": ["실제 실행한 명령"],
  "evidence": ["관찰한 원문 출력"],
  "unverified": ["실행으로 확인 못 한 항목"] }
  • cannot_verify 를 1급 값으로 둔다. 없으면 에이전트가 pass/fail 중 하나를 억지로 고르고 그 순간 신뢰가 무너진다.
  • evidence 에 코드 인용이 들어가면 검증이 아니다. 코드를 읽고 추론한 건 관찰이 아니다. SKILL.md 에 명시한다.

확인 — 같은 버그를 3번 던졌을 때 steps_taken 이 일관되면 통과.

함정 — 검증 스킬과 구현 에이전트를 동시에 만들지 말 것. 구현자가 있으면 검증 없이도 진행되므로 검증 스킬을 다듬을 동기가 사라진다. 트레이스 수집도 처음부터 넣지 않는다 — 기동 + 도달 + 콘솔로 시작.


R03. feature map

검증 스킬만 있으면 에이전트는 앱을 실행할 수는 있지만 그 앱이 뭔지 모른다. “사이드바가 버벅인다”에서 사이드바까지 가는 방법을 모른다.

화면마다 기록할 것 — 사용자가 부르는 이름(컴포넌트명 아님), 도달 경로(URL + 클릭 순서), 선택자(없으면 “없음”이라고 쓴다), 담당 디렉터리, 진입 조건(인증·권한·데이터 상태).

라우팅 정의와 최상위 레이아웃부터 읽는다. 컴포넌트를 무작위로 열지 않는다. 전부 하지 않는다 — 문의가 많은 화면 10개만.

이 레시피의 진짜 산출물은 본문이 아니라 “선택자 없음 목록”이다. 검증 자동화가 어디서 막히는지가 거기 그대로 나오고, 그 목록이 곧 다음 스프린트의 작업이다.


R04. 실패 모드를 스킬로 승격

툴 콜과 thinking 블록을 펼쳐서 읽는다. 최종 출력만 보면 아무것도 안 보인다.

실패 유형 관찰 신호 대응
미탐색 파일을 읽지도 않고 원인 단언 스킬: 추측 전 검색 강제
조기 확신 첫 가설을 검증 없이 채택 스킬: 가설 2개 + 배제 근거
도달 실패 UI 에서 기능까지 못 감 R03 보강
도구 부재 확인할 방법 자체가 없음 R02 확장
범위 이탈 시키지 않은 리팩터링 R06 하드 제약

같은 유형이 3회 이상 나올 때만 스킬로 만든다. 1회성 실패에 스킬을 만들면 스킬이 쓰레기가 된다. 스킬 하나에 실패 유형 하나. “하지 마라”보다 “대신 이걸 해라”가 강하다 — 금지만 적으면 다른 방식으로 같은 실수를 한다. description 에 “언제 쓰지 말아야 하는지” 를 한 줄 넣으면 라우팅 정확도가 눈에 띄게 오른다.


R05. eval 하네스

스킬을 고쳤을 때 좋아졌는지 확인. 이게 없으면 스킬은 미신이 된다.

  1. 코디네이터가 루브릭을 먼저 만든다. 기대 동작이 아니라 채점 항목으로.
  2. 서브에이전트를 각각 독립 디렉터리에서 실행.
  3. 다른 모델의 judge 로 채점. 같은 모델이면 자기 출력에 후한 점수를 준다.
  4. 점수를 기록하고 스킬을 고친 뒤 재실행. 힐 클라이밍.

함정에이전트는 평가받는 걸 감지하면 행동이 바뀐다. 디렉터리명·파일명·프롬프트 어디에도 eval·test·평가 를 넣지 말고, 워킹 디렉터리를 평범한 프로젝트 복제본처럼 보이게 한다. 힐 클라이밍은 무제한 토큰에서만 성립하므로 반복 상한을 3회로 두고, 못 가면 스킬 설계 자체를 의심한다.


R06. 강제 계층 배치

순위 수단 성질
1 코드베이스 구조(디렉터리 규약, feature 코로케이션) 에이전트는 기존 패턴을 복사한다
2 타입 시스템(strict, 브랜디드 타입) 하드
3 CI / 린트 / 의존성 검사 하드
4 hooks (PreToolUse 차단) 하드 (로컬)
5 rules / skills / CLAUDE.md 소프트
6 코드 리뷰 최악

5·6만 갖고 있다면 코드베이스가 무너지는 건 시간 문제다.

  • PreToolUse (exit 2 → 차단): 차단 사유가 stderr 로 전달되어 자가 수정된다
  • PostToolUse: 변경 파일에 한정. 전체 타입체크는 루프를 끊는다
  • SubagentStop: 출력 계약 위반 반려

하드로 박을 후보 — 신규 파일 useEffect 금지(기존 파일은 허용해 점진 이행), 컴포넌트에서 fetch 직접 호출 금지, feature 간 cross-import 금지, any/as 신규 추가 금지.

주석에 대해서는 전면 금지보다 “왜”가 없는 주석 금지가 현실적이다 — 에이전트가 쓰는 주석의 대부분은 코드가 아니라 대화 이력을 서술한다(일회성 지적을 전역 규칙으로 오해한 결과다).


R07. 리뷰 코멘트를 하드 제약으로

트리거는 같은 지적을 두 번 하는 순간이다. 세 번째를 기다리지 않는다.

PR 코멘트 → CLAUDE.md 규칙(소프트, 임시) → 린트/hook(하드, 목표)
         → 타입으로 표현 불가능하게(최선) → 구조적으로 제거(최고)

판단 순서: ① 애초에 불가능하게 만들 수 있나(API 설계 변경) ② 타입으로 막을 수 있나 ③ 린트/hook 으로 막을 수 있나 ④ 셋 다 안 되면 — 정말 안 되는가, 아니면 귀찮은가? 4번에서 “귀찮다”가 답이면 아직 안 된 게 아니다.


R08. 그린필드 가드레일

신규가 더 위험하다. 브라운필드엔 이미 컨벤션과 가드레일이 있어 오히려 좋은 환경이고, 가드레일 없이 태스크를 주면 에이전트는 가장 편한 방법으로 푼다. 시간이 지나면 지름길에 최적화된, 사람이 이해 못 하는 코드베이스가 남는다.

코드 한 줄 쓰기 전에 — feature 코로케이션(작업의 80%가 한 디렉터리에서 끝나야 한다), 의존성 경계 + CI 검사, 명사 정의(에이전트는 이름이 있는 것을 복사한다), strict 최대치(나중에 켜는 건 사실상 불가능), 최단 경로 = 정답 경로.

확인 — 새 기능 하나를 시켜보고 몇 개 디렉터리를 건드렸는지 센다. 3개를 넘으면 구조가 잘못됐다.


R09. 병렬화 기준 — 개수가 아니라 충돌 가능성

서로 다른 feature 디렉터리·서로 다른 앱이면 동시 가능. 공용 타입·디자인 토큰·API 클라이언트는 직렬 강제, 마이그레이션·스키마 변경은 단독 실행. 공용 레이어는 병렬 이득이 거의 없고 머지 충돌 비용만 는다.

동시 개수 ≤ (1시간에 리뷰 가능한 PR 수) × (자동 검증 통과율)

통과율이 50%면 개수를 늘려도 사람 병목이 그대로다. 개수를 늘리기 전에 통과율을 올린다.


R10. 자동 머지 진입 조건

전제부터 — 자동 머지를 하는 대상은 본인이 재설계한 코드베이스다. 같은 회사의 기존 코드베이스에는 하지 않고 회귀가 계속 난다. 자동 머지는 에이전트 신뢰의 결과가 아니라 아키텍처 저작권자의 특권이다.

전부 yes 여야 한다 — 아키텍처를 내가 설계했거나 완전히 이해한다 / 위반 시 CI 가 빨개지는 하드 제약이 최소 5개 / cannot_verify 비율 20% 미만 / 롤백 5분 내 / 장애를 24시간 안에 자동 인지 / PR 이 원자적이다.

위험 = 폭발 반경 × 발견 지연 × 되돌리기 비용. 내부 운영 도구는 가능, 공개 웹(SEO)은 신중(인덱싱 회복이 느리다), 결제·예약·인증 플로우는 하지 않는다 — 실패한 사용자는 롤백으로 돌아오지 않는다. 같은 사람이 영역마다 다른 신뢰 수준으로 일해도 문제없다.


#584raw