---
type: note
kind: Recipe
title: 에이전트 신뢰 구축 — 검증 경로부터 자동 머지까지
description: 사람이 검증자 자리에서 내려오는 순서. 진단 → 검증 스킬 → 스킬 승격 → 하드 제약 → 병렬화 → 자동 머지.
tags: ['claude-code', 'agent', 'verification', 'hooks', 'code-review']
status: release
ctime: 2026-09-01
mtime: 2026-09-02
generated: { by: claude/opus-5, at: 2026-09-01T00:00:00Z }
sources:
  - id: lauren-tan-trust
    resource: https://www.youtube.com/watch?v=Cmoh-yR-usA
    title: "Lauren Tan (Cursor) 세션 — 에이전트 신뢰 구축"
---

**신뢰는 태도가 아니라 인프라의 결과물이다.** "믿어보자"는 결심으로는 아무것도 안 바뀌고, 검증 경로가 생기면 신뢰는 따라온다. 그래서 **순서를 바꾸면 안 된다** — 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. **출력 계약을 고정한다.** 산문 판정을 받으면 사람이 다시 해석해야 하고 거기서 신뢰가 샌다.

```json
{ "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)은 신중(인덱싱 회복이 느리다), **결제·예약·인증 플로우는 하지 않는다** — 실패한 사용자는 롤백으로 돌아오지 않는다. 같은 사람이 영역마다 다른 신뢰 수준으로 일해도 문제없다.

---
