하네스 엔지니어링 (1) 개념과 가드레일: 지시가 아니라 구조로 통제하기
하네스 엔지니어링 시리즈 1편 개념과 가드레일 (현재 글) · 2편 데이터 거버넌스와 피드백 루프 · 3편 역할별 레시피와 안티패턴 선행 글: Claude Code 에이전트 팀 입문 · 레거시 프로젝트에 에이전트 팀 투입하기
CLAUDE.md에 “테스트 없이 커밋하지 마세요”라고 써두었는데도 에이전트가 커밋합니다. 프롬프트에 “.env는 절대 읽지 마세요”라고 적었는데 어느 날 로그에 API 키가 찍혀 있습니다. 문장을 더 강하게 고쳐도(절대, 반드시, 대문자 경고) 재발률은 크게 줄지 않습니다.
원인은 문장 실력이 아닙니다. 지켜야 할 규칙을 지켜지지 않을 수 있는 형태로 두었기 때문입니다. 이 시리즈는 그 형태를 바꾸는 방법을 다룹니다.
1. 하네스 엔지니어링이란
하네스 엔지니어링(Harness Engineering) 은 AI 에이전트가 안전하고 안정적으로 일할 수 있도록 모델 바깥의 작업 환경과 제어 구조를 설계하는 기술입니다.
하네스(harness) 는 원래 말이나 낙하산 사용자에게 채우는 안전 장구를 뜻합니다. 말의 능력을 키우는 장비가 아니라, 말이 엉뚱한 방향으로 뛰지 못하게 하고 사람이 방향을 통제할 수 있게 만드는 장비입니다. AI에 붙는 하네스도 정확히 같은 역할입니다. 모델을 똑똑하게 만드는 게 아니라, 모델이 할 수 있는 일의 범위와 결과를 확인하는 절차를 설계합니다.
1-1. 세 계층의 구분
AI를 다루는 기술은 세 계층으로 나뉩니다.
| 계층 | 질문 | 다루는 것 |
|---|---|---|
| 프롬프트 엔지니어링 | 무엇을 물어볼까 | 지시문의 구조, 예시, 출력 형식 |
| 컨텍스트 엔지니어링 | 무엇을 보여줄까 | 컨텍스트 창에 어떤 정보를 넣고 뺄지 |
| 하네스 엔지니어링 | 어떤 환경에서 일하게 할까 | 도구, 권한, 검증 절차, 운영 체계 |
중요한 것은 이 세 계층이 대체재가 아니라 적층 구조라는 점입니다. 하네스가 좋아도 프롬프트가 엉망이면 결과가 나쁘고, 프롬프트가 완벽해도 하네스가 없으면 사고가 납니다.
실무에서 자주 벌어지는 오해는 하네스 문제를 프롬프트 문제로 착각하는 것입니다. 앞서 든 .env 사례를 보겠습니다.
<!-- CLAUDE.md -->
## 필수 준수 규칙
- .env 로 시작하는 파일은 어떤 경우에도 절대 조회하지 말 것.
이 문장은 문제가 없습니다. 명확하고 구체적입니다. 그런데도 뚫립니다. 왜냐하면 CLAUDE.md는 모델이 읽는 참고 자료일 뿐이고, 컨텍스트가 길어지거나 급한 디버깅 상황이 되면 모델의 주의가 다른 데로 갈 수 있기 때문입니다. 문장을 아무리 고쳐도 “읽을 수는 있지만 읽지 않기로 약속한 상태”라는 본질은 바뀌지 않습니다.
같은 요구사항을 하네스로 옮기면 이렇게 됩니다.
{
"permissions": {
"deny": ["Read(.env*)", "Read(**/.env*)"]
}
}
이제 모델이 .env를 읽으려 하면 도구 호출 자체가 거부됩니다. 모델의 주의력, 컨텍스트 길이, 그날의 운과 무관합니다. 프롬프트를 고쳐서 확률을 낮추는 대신, 구조를 바꿔서 확률을 0으로 만든 것입니다.
판단 기준은 하나입니다.
그 규칙이 한 번이라도 어겨지면 안 되는 것이라면, 프롬프트가 아니라 하네스에 있어야 합니다.
1-2. 지시는 권고, 하네스는 강제
앞의 대비를 일반화하면 이렇습니다.
지시 (프롬프트 · CLAUDE.md) |
하네스 (권한 · 훅 · 도구 제약) | |
|---|---|---|
| 성격 | advisory (권고) | deterministic (결정적) |
| 준수 여부 | 모델의 판단에 달림 | 코드가 판정 |
| 실패 시 | 조용히 어겨짐 | 도구 호출이 차단됨 |
| 컨텍스트 길이 영향 | 받음 (길어지면 희석) | 받지 않음 |
| 설정 비용 | 거의 없음 | 있음 (설정 파일·스크립트) |
여기서 자주 놓치는 지점이 있습니다. 에이전트가 여러 개면 이 격차가 개수에 비례해 벌어집니다.
에이전트 하나를 쓸 때는 지시만으로도 어느 정도 굴러갑니다. 준수율이 95%라고 해도, 어긴 5%를 사람이 화면에서 보고 그 자리에서 교정하면 되기 때문입니다. 그런데 팀원 5명을 병렬로 돌리면 상황이 달라집니다.
에이전트 1개, 준수율 95%
→ 사람이 지켜보고 있음 → 어겨도 즉시 교정 가능
에이전트 5개, 각 준수율 95%
→ 전원이 규칙을 지킬 확률 = 0.95^5 ≈ 77%
→ 사람은 5개 화면을 동시에 볼 수 없음
→ 어긴 것을 사후에 발견 → 이미 커밋됨
숫자 자체보다 중요한 건 감시 능력이 에이전트 수에 비례해 늘지 않는다는 점입니다. 에이전트는 늘리면 되지만 사람의 주의력은 늘지 않습니다. 이 비대칭이 하네스를 선택이 아니라 필수로 만듭니다.
에이전트 팀 입문에서 “팀원이 많다고 좋은 게 아니다”라고 했던 이유가 여기 있습니다. 병렬화의 이득은 팀원 수에 비례해 늘지만, 하네스 없이 병렬화하면 사고 확률도 같이 비례해 늘어납니다.
1-3. 하네스 3대 구성요소
하네스는 세 축으로 구성됩니다. 이 표가 시리즈 전체의 기준점입니다.
| 구성요소 | 통제 대상 | 핵심 질문 | Claude Code 수단 | 다루는 편 |
|---|---|---|---|---|
| 가드레일 (Guardrails) |
할 수 있는 일 | 무엇을 못 하게 막을까 | tools, disallowedTools, permissions, permissionMode, PreToolUse 훅, maxTurns, isolation: worktree |
1편 |
| 데이터 거버넌스 (Data Governance) |
볼 수 있는 정보 | 무엇을 보여주고 무엇을 숨길까 | CLAUDE.md, 스킬(Skills), 스폰 프롬프트, MCP 서버, .worktreeinclude, memory |
2편 |
| 피드백 루프 (Feedback Loops) |
완료의 판정 | 잘했는지 누가 어떻게 확인할까 | Stop / TaskCompleted / TeammateIdle 훅, 검증 서브에이전트, 태스크 리스트, /context |
2편 |
세 축은 각각 다른 실패를 막습니다.
- 가드레일이 없으면 → 하지 말아야 할 일을 합니다 (프로덕션 DB 삭제, 비밀키 유출)
- 데이터 거버넌스가 없으면 → 잘못된 근거로 일합니다 (없는 API 호출, 팀 컨벤션 위반)
- 피드백 루프가 없으면 → 안 된 일을 됐다고 보고합니다 (테스트 없이 “완료했습니다”)
세 축 모두 필요하지만, 시작은 가드레일입니다. 나머지 둘은 품질 문제고, 가드레일은 사고 문제입니다.
2. 가드레일 — 할 수 있는 일을 좁힌다
2-1. 3단 방어선
가드레일은 시점에 따라 세 겹으로 나뉩니다. 겹마다 막을 수 있는 실패의 종류가 다르므로, 하나로 다 처리하려 하면 실패합니다.
① 사전 차단 ② 실시간 검사 ③ 사후 검증
(에이전트 정의) (훅) (리뷰 에이전트)
───────────── ───────────── ─────────────
도구를 안 준다 → 호출을 판정한다 → 결과를 검사한다
tools PreToolUse 검증 서브에이전트
permissionMode permissions.deny TaskCompleted 훅
maxTurns
| 방어선 | 막는 실패 | 못 막는 실패 |
|---|---|---|
| ① 사전 차단 | 애초에 권한 밖의 행동 (읽기 전용 에이전트가 파일을 씀) | 권한 안에서 벌어지는 잘못된 행동 (허용된 경로의 엉뚱한 파일 수정) |
| ② 실시간 검사 | 조건부 위반 (rm -rf, 특정 경로 쓰기, 프로덕션 접속) |
개별 호출은 정상인데 결과가 틀린 경우 |
| ③ 사후 검증 | 논리적 오류, 요구사항 미충족, 테스트 누락 | 이미 실행된 파괴적 작업 (복구 불가) |
핵심은 ①에서 막을 수 있는 것을 ③으로 미루지 않는 것입니다. 프로덕션 DB 삭제는 리뷰 에이전트가 발견해도 늦습니다. 반대로 “요구사항을 충족했는가”는 ①로 막을 수 없으므로 ③이 필요합니다.
비용 순서도 이와 같습니다. ①은 설정 파일 몇 줄, ②는 스크립트 하나, ③은 추가 에이전트 실행(= 토큰)입니다. 싼 방어선에서 최대한 막고, 남은 것만 비싼 방어선으로 넘깁니다.
2-2. 능력 최소화 — 도구를 빼앗는다
가장 저렴하고 확실한 가드레일은 도구를 주지 않는 것입니다. 서브에이전트 정의 파일(.claude/agents/*.md)의 프론트매터에서 설정합니다.
두 가지 방식이 있습니다.
---
name: explorer
description: 코드베이스를 조사하고 구조를 문서로 보고
tools: Read, Grep, Glob, Bash
---
tools는 화이트리스트입니다. 나열한 것만 쓸 수 있고, 나머지는 전부 없습니다. 위 정의의 에이전트는 Write, Edit가 없으므로 파일을 수정할 방법이 아예 없습니다.
---
name: careful-implementer
description: 기능을 구현하되 위험한 도구는 제외
disallowedTools: Write, mcp__github
---
disallowedTools는 블랙리스트입니다. 기본으로 상속받는 도구 풀에서 나열한 것만 뺍니다. 위 에이전트는 Bash, Edit, 다른 MCP 도구를 그대로 유지합니다.
둘을 같이 쓰면 disallowedTools가 먼저 적용되고, 남은 풀에서 tools가 해석됩니다. 양쪽에 다 있는 도구는 제거됩니다.
tools (화이트리스트) |
disallowedTools (블랙리스트) |
|
|---|---|---|
| 기본 상태 | 아무것도 없음 → 나열한 것만 추가 | 전부 있음 → 나열한 것만 제거 |
| 새 도구가 추가되면 | 자동으로 차단됨 (안전) | 자동으로 허용됨 (위험) |
| 적합한 용도 | 읽기 전용·역할이 명확한 에이전트 | 대부분 다 필요한데 몇 개만 막을 때 |
보안이 목적이면 화이트리스트를 쓰세요. MCP 서버를 추가했을 때 블랙리스트는 자동으로 새 도구를 허용하지만, 화이트리스트는 자동으로 차단합니다.
읽기 전용 에이전트의 정석 조합
조사·리뷰용 에이전트는 이 조합이 기본입니다.
---
name: reviewer
description: 변경된 코드를 검토하고 문제점만 보고. 코드를 수정하지 않음
tools: Read, Grep, Glob, Bash
permissionMode: plan
model: opus
---
변경된 코드를 검토하고 발견 사항만 보고하세요. 코드를 직접 수정하지 마세요.
각 필드가 서로 다른 구멍을 막습니다.
tools에Write·Edit가 없음 → 파일 수정 도구가 존재하지 않음permissionMode: plan→ 플랜 모드(읽기 전용 탐색).Bash로 우회 수정하는 경로까지 막힘- 본문의 “수정하지 마세요” → advisory 한 겹 추가 (있어도 손해는 없음)
Bash를 남겨두는 이유는 git diff, npm test 같은 조사 명령이 필요해서입니다. 하지만 Bash가 있으면 echo ... > file.ts 로 파일을 쓸 수 있습니다. 그래서 permissionMode: plan이 필요합니다. 도구 목록만으로는 읽기 전용이 완성되지 않습니다.
permissionMode 6종
권한 모드는 프롬프트를 어떻게 처리할지 결정합니다.
| 모드 | 동작 | 쓰는 곳 |
|---|---|---|
default |
표준 권한 확인 (프롬프트 띄움). manual은 별칭 |
기본값 |
plan |
플랜 모드, 읽기 전용 탐색 | 조사자·검증자 |
acceptEdits |
작업 디렉터리 내 파일 편집 자동 승인 | 신뢰하는 구현 작업 |
dontAsk |
권한 프롬프트를 자동 거부. 명시적으로 허용된 도구만 동작 | 무인 실행, 사람이 못 볼 때 |
auto |
백그라운드 분류기가 명령을 검토해 판정 | 프롬프트를 줄이고 싶을 때 |
bypassPermissions |
권한 확인 전부 생략 | ⚠️ 권장하지 않음 |
dontAsk가 특히 유용합니다. 무인으로 돌릴 때 bypassPermissions를 쓰면 아무거나 다 하지만, dontAsk는 미리 허용해둔 것만 하고 나머지는 멈춥니다. 무인 실행의 기본값은 bypassPermissions가 아니라 dontAsk여야 합니다.
부모 → 자식 상속 규칙
서브에이전트는 메인 세션의 권한 컨텍스트를 물려받고 모드를 덮어쓸 수 있지만, 부모가 우선하는 경우가 있습니다.
부모가 bypassPermissions / acceptEdits
→ 자식이 낮출 수 없음 (부모 모드가 우선)
부모가 auto 모드
→ 자식은 auto를 상속하고, 자식의 permissionMode 는 무시됨
실무 함의가 큽니다. --dangerously-skip-permissions로 세션을 띄우면, 자식 에이전트에 permissionMode: plan을 걸어도 무의미합니다. 읽기 전용으로 만들었다고 믿었던 리뷰 에이전트가 실제로는 파일을 쓸 수 있는 상태가 됩니다.
에이전트 팀도 같습니다. 팀원은 리드의 권한 설정으로 시작하며, 스폰 시점에 팀원별 권한을 따로 줄 수 없습니다. 리드가 위험한 모드로 떠 있으면 팀 전체가 그렇습니다.
원칙: 가드레일은 가장 바깥 세션에서 가장 좁게 시작해야 합니다. 부모가 넓으면 자식을 좁혀도 소용없습니다.
권한을 좁히면 품질이 올라간다
가드레일을 “안전을 위해 성능을 희생하는 장치”로 이해하기 쉽지만, 실제로는 품질이 같이 올라가는 경우가 많습니다.
이유는 단순합니다. 할 수 있는 일이 적으면 엉뚱한 일도 못 합니다.
[ 도구를 다 준 조사 에이전트 ]
"이 인증 모듈이 왜 느린지 조사해줘"
→ 조사 중 개선 여지를 발견
→ 고치기 시작
→ 고치다가 테스트가 깨짐
→ 테스트를 고침
→ 조사 결과 보고가 부실해짐 (컨텍스트를 수정 작업에 다 씀)
[ 읽기 전용 조사 에이전트 ]
"이 인증 모듈이 왜 느린지 조사해줘"
→ 고칠 방법이 없으므로 조사만 함
→ 컨텍스트 전부를 조사에 씀
→ 원인 분석이 깊어짐
에이전트는 여지가 있으면 일을 벌입니다. 도구를 빼앗는 것은 제약이 아니라 집중의 강제입니다. 조사자에게 Write를 주지 않는 이유는 못 믿어서가 아니라, 조사를 잘하게 만들기 위해서입니다.
2-3. 실행 시점 차단 — 훅
도구를 빼앗는 방식(2-2)은 전부 아니면 전무입니다. 하지만 실무의 규칙은 대개 조건부입니다.
Write는 되지만migrations/에는 쓰면 안 된다Bash는 되지만rm -rf는 안 된다psql은 되지만 프로덕션 호스트에는 접속하면 안 된다
이런 규칙은 훅(Hooks) 으로 처리합니다. 훅은 에이전트의 생애주기 특정 시점에 실행되는 내 스크립트입니다. 모델이 아니라 코드가 판정하므로 결정적입니다.
PreToolUse — 도구가 실행되기 전에 판정
가장 많이 쓰는 훅입니다. settings.json에 등록합니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-paths.sh",
"statusMessage": "보호 경로 확인 중..."
}
]
}
]
}
}
matcher는 도구 이름을 필터합니다("Bash", "Write|Edit", "mcp__.*" 같은 정규식). 스크립트는 stdin으로 JSON을 받습니다.
#!/bin/bash
# .claude/hooks/protect-paths.sh
# migrations/ 와 schema.prisma 에 대한 쓰기를 차단
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
case "$FILE_PATH" in
*migrations/*|*schema.prisma)
echo "보호 경로입니다: $FILE_PATH — 스키마 변경은 사람이 직접 처리합니다." >&2
exit 2
;;
esac
exit 0
실행 권한을 줘야 합니다.
chmod +x .claude/hooks/protect-paths.sh
이제 에이전트가 migrations/ 아래 파일을 수정하려 하면 호출이 차단되고, stderr에 쓴 메시지가 에이전트에게 전달됩니다. 에이전트는 이유를 알고 다른 방법을 찾거나 사람에게 요청합니다.
종료 코드 2의 의미
훅의 종료 코드가 동작을 결정합니다.
| 종료 코드 | 의미 |
|---|---|
0 |
통과. 정상 진행 |
2 |
차단 + 피드백 전달. stderr 내용이 에이전트에게 전달됨 |
| 그 외 | 비차단 에러. 대부분의 이벤트에서 작업은 그대로 진행됨 |
핵심은 2가 “차단”만이 아니라 “차단 + 대화”라는 점입니다. 그냥 막으면 에이전트는 이유를 모르고 같은 시도를 반복합니다. stderr에 이유를 쓰면 에이전트가 그 정보를 받아 방향을 바꿉니다.
이벤트별로 종료 코드 2의 효과가 다릅니다. 자주 쓰는 것만 정리하면 이렇습니다.
| 훅 이벤트 | 시점 | 종료 코드 2의 효과 |
|---|---|---|
PreToolUse |
도구 실행 전 | 도구 호출 차단 |
PostToolUse |
도구 실행 후 | 차단 불가 (이미 실행됨). stderr만 전달 |
Stop |
에이전트가 턴을 마치려 할 때 | 종료를 막고 계속 일하게 함 |
SubagentStop |
서브에이전트가 끝나려 할 때 | 종료를 막음 |
TaskCompleted |
태스크를 완료 처리할 때 | 완료 처리 차단 |
TaskCreated |
태스크가 생성될 때 | 생성 롤백 |
TeammateIdle |
팀원이 유휴로 전환하려 할 때 | 계속 일하게 함 |
PermissionRequest |
권한 확인이 필요할 때 | 권한 거부 |
앞의 넷은 가드레일이지만, Stop·TaskCompleted·TeammateIdle은 피드백 루프에 해당합니다(2편에서 다룹니다). 같은 메커니즘이 두 축에 걸쳐 쓰인다는 점만 기억하면 됩니다.
maxTurns — 폭주 차단
훅으로 개별 호출을 막을 수는 있어도, 같은 시도를 무한 반복하는 것은 막지 못합니다. 테스트가 통과하지 않아서 고치고 다시 돌리고 또 고치는 루프에 빠지면 토큰만 소진됩니다.
---
name: migrator
description: 기계적인 리팩토링을 여러 파일에 적용
maxTurns: 30
---
maxTurns는 에이전트의 최대 턴 수를 제한합니다. 넘으면 멈춥니다. 정확한 값은 작업 성격에 따라 다르지만, 끝나지 않는 것보다 미완으로 멈추는 게 낫다는 판단이 기준입니다.
훅과 CLAUDE.md 중 어디에 둘까
이 시리즈의 가장 중요한 판단 기준입니다.
CLAUDE.md |
훅 | |
|---|---|---|
| 성격 | advisory (권고) | deterministic (결정적) |
| 적합한 규칙 | 스타일, 관례, 선호 (“가능하면 함수형으로”) | 위반 시 사고가 나는 규칙 |
| 위반 시 | 조용히 지나감 | 차단 + 로그 |
| 비용 | 컨텍스트를 차지 | 컨텍스트를 차지하지 않음 |
부수적인 이점이 하나 있습니다. 훅은 컨텍스트를 소비하지 않습니다. CLAUDE.md에 규칙을 20줄 추가하면 매 세션 그만큼의 컨텍스트를 먹지만, 훅으로 옮기면 0입니다. 규칙이 많은 팀일수록 이 차이가 커집니다.
판단 기준: “이 규칙이 어겨졌을 때, 나중에 발견해도 괜찮은가?” 괜찮다면
CLAUDE.md. 안 괜찮다면 훅.
2-4. 작업 공간 격리 — worktree
여러 에이전트가 동시에 파일을 고치면 서로의 변경을 덮어씁니다. 에이전트 팀 입문에서 “파일 소유권을 겹치지 않게 나눠라”라고 했던 문제입니다. 프롬프트로 나누는 대신 구조로 분리할 수 있습니다.
---
name: refactorer
description: 기계적인 리팩토링을 여러 파일에 걸쳐 적용
isolation: worktree
maxTurns: 40
---
요청된 리팩토링을 해당 파일 전부에 적용하고, 테스트를 실행해 결과를 보고하세요.
isolation: worktree를 넣으면 이 서브에이전트는 저장소의 독립된 사본(git worktree) 에서 작업합니다. 내 체크아웃의 파일은 건드리지 않습니다. 변경이 없으면 worktree는 자동 정리되고, 변경이 있으면 남습니다.
기본적으로 기본 브랜치(보통 main)에서 분기합니다. 진행 중인 작업 위에서 작업하게 하려면 설정을 바꿉니다.
{
"worktree": {
"baseRef": "head"
}
}
격리되는 것과 격리되지 않는 것
여기서 흔한 오해가 생깁니다. worktree는 파일만 격리합니다.
| 대상 | 격리 여부 |
|---|---|
| 작업 파일, 브랜치 | ○ 격리됨 |
.git 디렉터리 |
✗ 공유 (git commit이 메인 저장소에 씀) |
| 데이터베이스 | ✗ 공유 — 마이그레이션 실행 시 서로 충돌 |
| 포트 (dev 서버, Redis) | ✗ 공유 — 동시에 띄우면 충돌 |
| 외부 API (결제, 메일 발송) | ✗ 공유 — 실제로 호출됨 |
| 권한 승인 기록 | ✗ 공유 (메인 체크아웃의 settings.local.json에 저장) |
gitignore된 파일 (.env, node_modules) |
✗ 복사되지 않음 — 별도 설정 필요 |
마지막 항목이 실무에서 가장 많이 걸립니다. worktree는 새 체크아웃이므로 .env도 node_modules도 없습니다. 그래서 에이전트가 테스트를 돌리려다 실패합니다. 필요한 파일만 자동 복사하려면 프로젝트 루트에 .worktreeinclude를 둡니다.
.env
.env.local
config/secrets.json
.gitignore 문법을 씁니다. 패턴에 맞고 동시에 gitignore된 파일만 복사되므로, 추적 중인 파일이 중복되지는 않습니다.
여기서 데이터 거버넌스와 만납니다. .worktreeinclude에 무엇을 넣을지는 “이 에이전트에게 어떤 비밀을 넘길 것인가” 라는 질문이기도 합니다(2편에서 다룹니다).
팀원에는 적용되지 않는다
이 시리즈에서 반드시 기억해야 할 비대칭입니다.
| 서브에이전트 | 팀원(teammate) | |
|---|---|---|
isolation: worktree |
○ 가능 | ✗ 미적용 |
| 파일 격리 | 자동 | 프롬프트로 소유권을 나눠야 함 |
팀원은 리드와 같은 작업 디렉터리를 공유합니다. 에이전트 팀으로 병렬 구현을 할 때 파일 충돌을 구조적으로 막는 수단이 없습니다. 레거시 프로젝트에 에이전트 팀 투입하기에서 리팩토링 단계를 “★★ 관리 필요”로 분류한 이유가 여기 있습니다.
정리하면 판단이 이렇게 갈립니다.
- 병렬로 파일을 고쳐야 한다 → 서브에이전트 +
isolation: worktree - 병렬로 논의·조사해야 한다 → 에이전트 팀 (읽기 전용이므로 충돌 없음)
- 팀으로 병렬 구현해야 한다 → 프롬프트로 파일 소유권을 엄격히 분리 + 사람이 감시
격리가 과잉인 경우
worktree는 무료가 아닙니다. 생성마다 디스크와 시간이 들고, 의존성 설치가 필요하면 그 비용도 매번 듭니다. 다음 경우에는 쓰지 않는 게 낫습니다.
- 읽기 전용 에이전트 — 격리할 변경이 없습니다
- 한 번에 한 에이전트만 도는 경우 — 충돌 상대가 없습니다
- 에이전트가 DB나 포트를 써야 하는 경우 — 어차피 격리되지 않으므로 파일만 나눠봐야 충돌합니다
판단 기준: 두 에이전트 이상이 동시에 파일을 쓸 때만 worktree를 씁니다.
핵심요약
- 하네스 엔지니어링은 프롬프트(무엇을 물어볼까)·컨텍스트(무엇을 보여줄까) 위에 쌓이는 계층으로, 어떤 환경에서 일하게 할까를 설계한다. 프롬프트로 안 풀리는 문제의 대부분은 실제로 하네스 문제다.
- 지시는 권고(advisory), 하네스는 강제(deterministic). 한 번이라도 어겨지면 안 되는 규칙은
CLAUDE.md가 아니라 권한 설정과 훅에 있어야 한다. 에이전트가 늘면 이 격차의 비용이 개수에 비례해 커진다. - 하네스 3대 구성요소는 가드레일(할 수 있는 일) · 데이터 거버넌스(볼 수 있는 정보) · 피드백 루프(완료의 판정). 가드레일만 사고 문제고 나머지는 품질 문제이므로, 가드레일부터 세운다.
- 가드레일 3단 방어선: 사전 차단(
tools·permissionMode) → 실시간 검사(PreToolUse훅) → 사후 검증(리뷰 에이전트). 싼 방어선에서 최대한 막고 남은 것만 넘긴다. - 능력 최소화는 가장 저렴한 가드레일이다. 보안 목적이면
tools화이트리스트를 쓴다. 읽기 전용 에이전트는tools: Read, Grep, Glob, Bash+permissionMode: plan조합이 정석 —Bash만 남겨두면 우회 수정이 가능하므로 두 필드가 다 필요하다. - 권한을 좁히면 품질이 올라간다. 할 수 있는 일이 적으면 엉뚱한 일도 못 하고, 컨텍스트를 본래 임무에 다 쓴다.
- 부모 권한이 우선한다. 부모가
bypassPermissions·acceptEdits면 자식이 낮출 수 없고,auto면 자식의permissionMode가 무시된다. 팀원은 리드의 권한으로 시작한다. 가드레일은 가장 바깥 세션에서 가장 좁게 시작해야 한다. - 훅은 조건부 규칙을 결정적으로 강제한다. 종료 코드 2는 “차단 + 피드백 전달”이므로 stderr에 이유를 써야 에이전트가 방향을 바꾼다. 훅은 컨텍스트를 소비하지 않는다는 이점도 있다.
isolation: worktree는 파일만 격리한다. DB·포트·외부 API는 공유되고,.env같은 gitignore된 파일은 복사되지 않으므로.worktreeinclude가 필요하다. 팀원에는 적용되지 않는다 — 서브에이전트만 격리 가능하다.
2편 예고
가드레일로 “못 하게” 만들었으면, 다음은 “제대로 하게” 만드는 일입니다. 2편에서는 남은 두 축을 다룹니다.
- 데이터 거버넌스: 지식 공급의 3층 구조(
CLAUDE.md/ 스킬 / 스폰 프롬프트), “이건CLAUDE.md인가 스킬인가” 판단 기준, 컨텍스트 예산 관리,memory스코프로 지식을 축적하는 법 - 피드백 루프: “완료”의 정의를 누가 쥐는가(강제력 4단계), 채점자를 분리하는 구조, 같은 실수가 반복될 때 프롬프트가 아니라 하네스를 고치는 에스컬레이션 규칙