Footnotes

  1. Feedback Ladder: ⛰ 산(Mountain) / 차단 및 즉각적인 조치 필요, 🧗‍♀️ 볼더(Boulder) / 블로킹, ⚪️ 자갈(Pebble) / 비차단하지만 향후 조치가 필요함, ⏳ 모래(Sand) / 논블로킹이지만 향후 고려 필요, 🌫 먼지(Dust) / 비차단, “받거나 놔두세요” ↩

  2. 다른 사람들의 상황이 자신에게 적용되는지 자문해볼 필요가 있다. ↩

#221

에이전트 역할 분리 — 입력을 좁혀 성공 기준을 가른다

언제: 실패를 기계적으로 열거할 수 있고, 그걸 반복해서 고쳐야 할 때.

같은 모델이라도 입력이 다르면 무엇을 성공으로 보는지가 갈린다. 구현자는 원본과 자기 추론을 받아 머지되기를 원하고, 리뷰어는 diff 만 받고 이 코드는 틀렸다고 가정하라는 지시를 받아 버그를 찾기를 원한다. 그래서 입력 자체를 잘라야 한다. 64개 병렬은 결과지 원인이 아니다 — 일을 성립시킨 건 컨텍스트 비대칭, 기계적으로 생성되는 큐, 언어를 바꿔도 통하는 합격 판정 기준(테스트 오라클) 셋이다. 판정 기준이 없으면 여기서 멈춘다: Bun 의 Rust 포팅에는 4,174개 파일 6만여 개 expect() 의 TypeScript 테스트가 있었고 스킵·삭제가 0개였다. 없으면 리뷰어를 둘 붙여도 검증되지 않은 코드를 빠르게 생산할 뿐이라, 실패를 열거 가능한 상태(타입 에러 목록·스냅샷·시각 회귀)를 먼저 만든다.

한꺼번에 세우지 않는다. 1 adversarial-reviewer 하나만 만들어 memory: project 로 몇 주 돌린다 — 도구는 Read·Grep·Glob 뿐, diff 하나를 받아 틀린 이유만 찾고 최소 3개의 실패 시나리오를 낸다. 2 쌓인 지적 패턴이 거절 규칙 목록이 된다: 한 문단짜리 주석으로 우회를 정당화하면 코드가 틀린 것, 스텁·TODO·조건부 비활성화로 컴파일만 통과시킨 건 미완성, 테스트를 고쳐 통과시킨 변경은 무조건 거절. 3 그 뒤에 porter(구현, isolation: worktree)와 fixer(지적만 반영, 새 기능 금지)를 붙인다. porter 의 변경은 .work/<id>.diff 로 나가고 리뷰어는 diff 경로와 원본 경로만 받는다. 큐는 세션 밖에서 돈다 — 실패 목록을 파일로 만들고 claude -p 를 반복한다.

확인: 리뷰어가 tsc·eslint가 통과시키는 지점을 잡고 있는가. 그게 이 역할의 존재 가치 전부다 — effect 의존성의 의미 변화, 이벤트 핸들러의 eager/lazy, ??와 ||의 단축평가 차이, 리렌더 타이밍, 정리 함수 누락. 린터가 잡을 걸 잡고 있으면 프롬프트가 헐거운 것이다.

컨텍스트 비대칭은 저절로 유지되지 않는다 — 위임 프롬프트를 부모가 쓰므로 그냥 두면 구현 근거를 요약해 리뷰어에게 넘긴다. .work/<id>.diff 를 경유시키고 리뷰어에게는 diff 경로와 원본 경로만 준다는 걸 CLAUDE.md 규약으로 박는다. 역할 분리를 프롬프트로 하면 무너지니 리뷰어 tools 에서 Edit·Write 를 빼 권한으로 막는다. 같은 모델 둘은 실패 상관관계가 높아 리뷰어를 둘 둘 거면 하나는 model 을 다르게 준다. -p(비대화형)에서는 fork mode 가 꺼져 subagent 가 포그라운드로 도는 경우가 있어 병렬은 셸에서 잡는다. 병목은 모델이 아니라 디스크 IOPS 였다 — 느린 grep 하나가 디스크를 몇 분씩 얼렸다.

#579

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

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

R00·R01. 거르고 구간 확정

먼저 R00 으로 거른다: 자동 검증이 닿지 않는 도메인(실제 예약·결제·외부 파트너), 규칙이 코드 밖에 있는 영역(규제), 강제 계층 13층을 바꿀 권한이 없는 코드베이스는 자동화 대상에서 뺀다 — 방법론을 약화하지 않고 대상을 좁힌다. 남은 곳에서 구간을 오른다. 0 불신(출력을 매번 다 읽음, 병렬 01) → R02 검증 스킬과 R03 feature map. 1 관찰(툴 콜을 읽으며 실패 수집, 병렬 13) → R04 실패 모드 승격과 R05 eval. 2 위임(검증 경로가 있고 판정을 에이전트가 냄, 병렬 38) → R06 강제 계층, R07 리뷰를 하드 제약으로, R08 그린필드 가드레일. 3 자동화(하드 제약이 CI·hook 에, 병렬 8+) → R09 병렬화 기준. 4 무감독(자동 머지 후 사후 리뷰) → R10 자동 머지 진입 조건. 신뢰는 인프라의 결과라 순서를 건너뛰면 토큰만 태운다. 판정 질문: 지금 PR 을 읽지 않고 머지하면 문제를 며칠 뒤에 아는가 — 모르면 구간 0.

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

R02. 검증 스킬 부트스트랩

먼저 사람이 손으로 한 번 한다: dev 서버 기동 → 특정 화면 도달 → 관찰. 여기서 막히면 에이전트도 막힌다. 재료는 헤드리스 기동 명령, 조작 채널(CDP·Playwright MCP), 관찰 채널(콘솔·네트워크·DOM 스냅샷). 그 과정을 boot.sh·snapshot.sh 로 스크립트화해 .claude/skills/verify-app/ 으로 감싸고, 출력 계약을 고정한다. cannot_verify 를 1급 값으로 둔다 — 없으면 pass/fail 을 억지로 고른다. evidence 에 코드 인용이 들어가면 검증이 아니다. 구현 에이전트를 같이 만들지 않는다 — 검증 스킬을 다듬을 동기가 사라진다. 트레이스 수집도 처음엔 넣지 않는다.

{
  "verdict": "pass | fail | cannot_verify",
  "steps_taken": ["실제 실행한 명령"],
  "evidence": ["관찰한 원문 출력"],
  "unverified": ["실행으로 확인 못 한 항목"]
}

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

R03. feature map

검증 스킬만 있으면 에이전트는 앱을 실행할 수는 있지만 그 앱이 뭔지 모른다. 라우팅 정의와 최상위 레이아웃부터 읽고, 전부가 아니라 문의가 많은 화면 10개만 기록한다. 화면마다 사용자가 부르는 이름(컴포넌트명 아님), 도달 경로(URL + 클릭 순서), 선택자(없으면 없음), 담당 디렉터리, 진입 조건(인증·권한·데이터 상태). 진짜 산출물은 선택자 없음 목록이다 — 검증 자동화가 어디서 막히는지가 거기 나오고, 그게 다음 스프린트의 작업이다.

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

툴 콜과 thinking 블록을 펼쳐 읽고 실패를 다섯 유형으로 나눈다. 미탐색(읽지도 않고 원인 단언) → 스킬: 추측 전 검색 강제. 조기 확신(첫 가설을 검증 없이 채택) → 스킬: 가설 2개 + 배제 근거. 도달 실패(UI 에서 기능까지 못 감) → R03 보강. 도구 부재 → R02 확장. 범위 이탈(시키지 않은 리팩터링) → R06 하드 제약. 같은 유형이 3회 이상 나올 때만 스킬로 만든다. 스킬 하나에 유형 하나, 하지 마라보다 대신 이걸 해라, description 에 언제 쓰지 말아야 하는지 한 줄.

R05. eval 하네스

코디네이터가 기대 동작이 아니라 채점 항목으로 루브릭을 먼저 만든다. 서브에이전트를 각각 독립 디렉터리에서 실행하고, 다른 모델의 judge 로 채점한다 — 같은 모델은 자기 출력에 후하다. 점수를 기록하고 스킬을 고쳐 다시 돈다. 반복은 3회까지, 못 가면 스킬 설계를 의심한다. 에이전트는 평가받는 걸 감지하면 행동이 바뀌므로 디렉터리명·파일명·프롬프트 어디에도 eval·test·평가 를 넣지 않고 평범한 프로젝트 복제본처럼 보이게 한다.

R06. 강제 계층 배치

강제 수단을 강한 순서로 놓으면 1 코드베이스 구조(디렉터리 규약, feature 코로케이션 — 에이전트는 기존 패턴을 복사한다), 2 타입 시스템(strict, 브랜디드 타입), 3 CI·린트·의존성 검사, 4 hooks(PreToolUse 차단, 로컬), 여기까지 하드. 5 rules·skills·CLAUDE.md 는 소프트, 6 코드 리뷰는 최악. 5·6 만 있으면 무너지는 건 시간 문제다. hooks: PreToolUse 는 exit 2 로 차단하고 사유가 stderr 로 가 자가 수정된다, PostToolUse 는 변경 파일에 한정한다(전체 타입체크는 루프를 끊는다), SubagentStop 은 출력 계약 위반을 반려한다. 하드 후보: 신규 파일 useEffect 금지, 컴포넌트의 fetch 직접 호출 금지, feature 간 cross-import 금지, any·as 신규 추가 금지, 왜가 없는 주석 금지.

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

같은 지적을 두 번 하는 순간이 트리거다. PR 코멘트에서 시작해 CLAUDE.md 규칙(소프트, 임시) → 린트·hook(하드, 목표) → 타입으로 표현 불가능하게(최선) → 구조적으로 제거(최고) 로 올린다. 판단은 위에서부터: 애초에 불가능하게 만들 수 있나(API 설계) → 타입으로 막을 수 있나 → 린트·hook 으로 막을 수 있나 → 셋 다 안 되면 정말 안 되는지, 귀찮은지 묻는다. 귀찮다면 아직 안 된 게 아니다.

R08. 그린필드 가드레일

브라운필드에는 이미 컨벤션과 가드레일이 있어 에이전트가 그걸 따른다. 그린필드에서 가드레일 없이 태스크를 주면 에이전트는 가장 편한 방법으로 풀고, 시간이 지나면 지름길에 최적화된 사람이 이해 못 하는 코드베이스가 남는다. 코드 한 줄 쓰기 전에: feature 코로케이션(작업의 80% 가 한 디렉터리에서), 의존성 경계 + CI 검사, 명사 정의(에이전트는 이름이 있는 것을 복사한다), strict 최대치(나중에 켜는 건 사실상 불가능), 최단 경로 = 정답 경로. 새 기능 하나를 시켜 건드린 디렉터리가 3개를 넘으면 구조가 잘못됐다.

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

R09. 병렬화 기준

서로 다른 feature 디렉터리나 다른 앱이면 동시에 돌린다. 공용 타입·디자인 토큰·API 클라이언트는 직렬로 강제한다 — 병렬 이득이 거의 없고 머지 충돌 비용만 는다. 마이그레이션·스키마 변경은 단독 실행. 동시 개수는 1시간에 리뷰 가능한 PR 수 곱하기 자동 검증 통과율을 넘지 않는다. 통과율이 50% 면 개수를 늘려도 사람 병목이 그대로라, 개수를 늘리기 전에 통과율을 올린다.

R10. 자동 머지 진입 조건

전제: 자동 머지 대상은 본인이 재설계한 코드베이스고, 같은 회사의 기존 코드베이스에는 하지 않는다(회귀가 계속 난다) — 자동 머지는 에이전트 신뢰의 결과가 아니라 아키텍처 저작권자의 특권이다. 여섯 가지가 전부 예여야 한다: 아키텍처를 설계했거나 완전히 이해한다, 위반 시 CI 가 빨개지는 하드 제약이 5개 이상, cannot_verify 비율 20% 미만, 롤백 5분 안, 장애를 24시간 안에 자동 인지, PR 이 원자적. 위험은 폭발 반경 × 발견 지연 × 되돌리기 비용이다. 내부 운영 도구는 가능, 공개 웹(SEO)은 인덱싱 회복이 느려 신중, 결제·예약·인증은 하지 않는다 — 실패한 사용자는 롤백으로 돌아오지 않는다. 영역마다 신뢰 수준이 달라도 된다.

#584