하네스(Harness)는 작성만으로 끝나지 않는다: 3개월 운영 중 동적으로 망가진 5가지 순간
요약
Claude Code를 활용한 하네스(Harness) 운영 중 모델 업데이트와 에이전트 구조로 인해 발생하는 동적인 문제들을 다룹니다. 모델 버전 업그레이드에 따른 규칙 준수 실패, Hook 실행 경로의 불일치, Subagent의 컨텍스트 유실 등 실제 운영 과정에서 겪은 5가지 실패 사례와 교훈을 공유합니다.
핵심 포인트
- 모델 업데이트(예: Opus 4.7) 시 기존에 설정한 CLAUDE.md의 규칙이 무시될 수 있음
- Hook 설정 시 Tool 호출 직접 경로뿐만 아니라 Subagent를 경유하는 시나리오도 반드시 테스트해야 함
- Subagent는 설계상 부모의 컨텍스트를 거의 전달받지 못하므로 독립된 컨텍스트 윈도우를 가짐을 인지해야 함
작성한 순간에는 완벽하다고 생각했던 CLAUDE.md가 3주 후에 전부 stale 상태가 된 이야기
제가 처음으로 자신의 하네스(Harness)를 "이건 깔끔하게 잘 만들었다"라고 생각했던 것은 2026년 2월 말쯤이었습니다.
CLAUDE.md에 프로젝트 규약을 작성하고, .claude/hooks에 pre-commit과 post-tool-use를 심어두었으며, subagent를 3개 준비하고, permission allowlist도 확실하게 좁혀두었습니다. git push를 한 뒤 자신의 리포지토리를 보며 만족스럽게 커피를 내렸던 기억이 납니다.
3주 후, Opus 4.7이 출시되었습니다.
그로부터 2개월 동안, 저의 하네스는 조용하지만 확실하게 망가져 갔습니다. 작동하지 않게 된 것은 아닙니다. 오히려 표면적으로는 작동하고 있습니다. 하지만 작성했을 때 의도했던 동작은 이제 절반 정도밖에 남아 있지 않습니다.
이전에 Zenn에 작성했던 「하네스 엔지니어링, 모두가 다른 말을 하고 있다」에서는 업계 각사의 정의 차이를 정리했습니다. 이번에는 그 속편이라고 할까, 좀 더 현실적인 이야기입니다. "정의가 통일된 후, 실제로 운영해 보니 어떤 일이 일어나는가".
지난번에는 설계론이었습니다. 이번에는 운영론입니다. 3개월 동안 저의 하네스를 5번 망가뜨린, 그 순간들에 대해 이야기하겠습니다.
순간 1: 모델 업데이트로 인해 CLAUDE.md가 준수되지 않게 됨
처음으로 망가진 것은 CLAUDE.md였습니다.
Opus 4.6 시절에 "커밋 메시지는 일본어로 작성할 것", "입니다/합니다(ですます調) 체로 통일할 것"이라고 적어두었던 규칙이, 4.7로 업그레이드한 다음 날 아침, 왠지 모르게 영어 커밋 메시지가 흘러나오기 시작했습니다. 처음에는 기분 탓인가 생각했습니다.
3일 후, 다른 프로젝트에서도 동일한 증상이 나타나면서 마침내 깨달았습니다. CLAUDE.md의 규칙이 모델 업데이트로 인해 조용히 준수되지 않게 된다는 사실을 말입니다.
이는 저만의 체감이 아니었습니다. GitHub의 anthropics/claude-code issues #58369에 「Categorized regression analysis: Opus 4.7」이라는 스레드가 생성되어 있었고
이것은 사실입니다. 단, 조건이 있습니다. Hook이 확실히 발화(fire)하는 경로에 두었을 경우에 한합니다. subagent를 통해 tool이 호출된 경우, 저의 linter hook은 「거의 매번」 실패했습니다. 수도꼭지 앞의 센서가 사실은 다른 수도꼭지에만 반응하고 있었던 셈입니다.
배운 점은, Hook을 심었다면 subagent를 경유하는 시나리오도 테스트해야 한다는 것입니다. tool 호출을 직접 실행하는 경로와 Task tool을 통해 subagent로 던지는 경로는 별개의 시나리오로 취급해야 합니다.
순간 3: Subagent 위임 시 context가 유실됨
세 번째는 Subagent입니다.
저는 리서치용, 코드 리뷰용, 문서 생성용의 3가지 subagent를 운용하고 있었습니다. 각각 전용 skill 파일을 갖게 했고, 권한(permission)도 제한해 두었습니다.
어느 날, 리서치용 subagent에게 「지난주에 논의했던 context engineering 접근 방식을 바탕으로 최신 논문을 조사해줘」라고 부탁했더니, 자식 에이전트가 「지난주에 논의한 내용에 대한 정보가 없으므로, 일반적인 context engineering에 대해 해설하겠습니다」라고 답해왔습니다.
이때 깨달았습니다. Subagent는 부모의 context를 거의 전달받지 못한다는 사실을 말입니다.
「Claude Code Agent Teams, Subagents, and MCP: The 2026 Playbook」에 따르면, subagent는 부모와 독립된 context window를 가집니다. 이는 「verbose한 출력을 부모의 context로부터 격리하기 위해서」라는 설계 사상이며, 장점이기도 합니다. 하지만 운용하다 보면 또 다른 면이 나타납니다.
subagent에게 전달되는 것은 Task tool에 작성한 prompt뿐입니다. 부모가 대화 속에서 쌓아온 전제도, CLAUDE.md의 세세한 습관도 아무것도 계승되지 않습니다. skill 파일도 명시적으로 preload 하지 않는 한, 자식은 읽지 않습니다.
즉, 부모가 3시간 동안 공들여 다듬은 전제를, 자식은 1초도 모르는 상태에서 시작합니다.
이는 설계 당시에는 상상하지 못했습니다. subagent = 똑똑한 분신,이라는 이미지로 만들었습니다. 실제로는 subagent = 별개의 신입 엔지니어였습니다. 매번 온보딩(onboarding)을 다시 하지 않으면, 문맥 제로 상태에서 시작합니다.
대책은 간단했습니다. Task tool에 전달하는 prompt의 서두에 「전제로서, 이 프로젝트에서는 X를 채택하고 있으며, 지난주에 Y라는 논의가 있었다」를 반드시 적는 것입니다. 이것을 적지 않으면, 똑똑한 분신은 초면인 타인이 됩니다.
순간 4: 메모리 리셋이 예상치 못하게 발화
네 번째는 메모리 주변입니다. 이것이 가장 까다로웠습니다.
/clear 명령어로 세션을 리셋하는 운용은 일반적이라고 생각합니다. auto-compaction으로 context가 압축되는 것도 어느 정도 이해합니다. 문제는 그 두 가지가 의도치 않게 혼재된다는 점이었습니다.
구체적으로는 이랬습니다. 장시간 세션을 지속하면, Claude Code는 내부적으로 context를 compaction 합니다. 사용자에게 명시적인 알림이 뜨는 경우도 있고, 뜨지 않는 경우도 있습니다. compaction이 실행되면 그때까지의 대화 세부 사항이 유실됩니다.
제가 낭패를 본 것은 리팩터링(refactoring) 도중이었습니다.
「이 클래스의 설계 의도는 이렇고, 이런 경위로 명명은 이렇게 되어」라고 처음 30분 동안 설명했던 내용이, 3시간 후 최종 확인 시점에 홀연히 사라져 있었습니다. Claude는 「이 코드를 보니 설계 의도는 아마도 Y인 것 같습니다」라며 멋대로 추측하기 시작했고, 원래 의도와는 다른 리팩터링을 제안해 왔습니다.
「Claude Code Commands Cheat Sheet (2026)」를 읽어보면, /clear는 메모리를 리셋하고, /init은 CLAUDE.md로부터 재구축한다고 되어 있습니다. 하지만 auto-compaction은 그 중간에 위치하여 사용자가 완전히 제어할 수 없습니다.
이를 깨달은 후, 저는 중요한 전제를 CLAUDE.md와 memory 파일 양쪽 모두에 쓰도록 했습니다. 대화에서 한 번 전달한 정보는 3시간 후에는 존재하지 않는 것으로 취급합니다. 「메모리는 휘발한다」는 전제로 운용합니다.
제가 '신장의 야망'을 15년 동안 플레이하며 배운 것은 포석을 까는 것입니다. 3수 앞을 내다보려면, 3수 분량의 기록을 남겨두지 않으면 AI 에이전트는 즉시 잊어버립니다.
순간 5: 외부 MCP 업데이트로 인한 permission allowlist(허가 화이트리스트)의 허점 발생
다섯 번째, 이것이 가장 소름 끼쳤던 순간입니다.
저는 permission allowlist (허가 화이트리스트)를 상당히 엄격하게 제한하고 있었습니다. Bash(git commit:*)나 Edit와 같은 입도(granularity)로 개별 허가하였고, 알 수 없는 도구는 모두 프롬프트(prompt)가 뜨도록 운영했습니다.
3월의 어느 날, 외부 MCP 서버를 버전 업데이트했습니다. 버전 1.2에서 1.3으로의 마이너 업데이트였습니다. CHANGELOG (변경 이력)를 읽었지만, 특별한 파괴적 변경(breaking changes)은 없었습니다. 업데이트를 마치고 평소처럼 작업을 진행했습니다.
일주일 후, 문득 깨달았습니다. 새로 추가된 MCP tool (도구)이 프롬프트 없이 호출되고 있다는 사실을 말입니다.
무슨 일이 일어난 걸까요? MCP의 네이밍 패턴(naming pattern)은 mcp__<server-name>__<tool-name>입니다. 서버 이름이 같다면, 새로운 tool이 추가되어도 서버 단위의 allowlist에는 매칭됩니다. 저는 당초 mcp__myserver__*와 같이 와일드카드(wildcard)로 허가하고 있었습니다. 새로운 tool이 추가되면 그것도 자동으로 허가되도록 설계되어 있었던 것입니다.
Claude Code MCP permissions 2026: allowedTools vs bypassPermissions 문서에서는 다음과 같이 경고하고 있습니다.
허가는 가장 좁은 입도로 작성할 것. 서버에 10개의 tool이 있고 필요한 것이 2개라면, 그 2개만 작성한다.
저는 서버 측을 신뢰했기에 와일드카드로 끝냈습니다. 그것이 실수였습니다. MCP 서버는 반드시 자신의 관리하에 있다고 단정할 수 없습니다. 서드파티 (third-party) MCP라면, 메인테이너(maintainer)가 신기능을 추가할 때마다 저의 allowlist에는 조용히 구멍이 뚫려갑니다.
이를 깨달은 후, 저는 와일드카드를 전부 폐지했습니다. tool 단위로 명시적으로 열거합니다. MCP 서버가 업데이트되면 CHANGELOG를 읽고 새로운 tool이 추가되었는지 확인합니다. 지루한 작업이지만, 이 외에 안전한 방법은 없습니다.
permission allowlist는 작성한 순간이 가장 안전하며, 시간이 흐름에 따라 느슨해집니다. 이것 또한 하네스(Harness)가 동적으로 망가지는 전형적인 패턴이었습니다.
설계와 운영은 별개였다
다섯 가지 순간을 나열해 보면 공통된 구조가 있습니다.
| 망가지는 순간 | 트리거 (Trigger) | 정적 상태 |
|---|---|---|
| CLAUDE.md stale (오래됨) | 모델 업데이트 | 에러 발생 안 함 |
| ... |
모두에게 공통적인 것은 "조용히 망가진다"는 점입니다.
에러가 화려하게 터져준다면 차라리 알아챌 수 있습니다. 하지만 하네스가 망가지는 방식은 훨씬 더 미미합니다. 어제와 똑같이 작동하는 것처럼 보이지만, 설계 당시의 의도와는 이미 다른 일을 하고 있다. 이것이 가장 무섭습니다.
설계와 운영은 별개의 것이었습니다. 설계는 정적이며 작성하는 순간 완성됩니다. 운영은 동적이며 작성하는 순간부터 열화(degradation)가 시작됩니다.
하네스 서적(『하네스 엔지니어링 실전 가이드』)의 제12장에서 "피드백 루프 (feedback loop)"에 대해 썼습니다. 즉시(초), 태스크(분), 세션(시간), 전략(주/월)의 4개 계층입니다. 그것은 설계 단계의 이야기입니다. 3개월간 운영하며 알게 된 것은, 운영에는 별도의 계층이 필요하다는 사실입니다.
저의 현재 운영 루틴은 다음과 같이 바뀌었습니다.
- 매주: CLAUDE.md를 처음부터 다시 읽으며, stale (오래된) 규칙을 업데이트
- 모델 업데이트 시: 모든 subagent (하위 에이전트)의 prompt (프롬프트)를 재테스트
- MCP 업데이트 시: CHANGELOG를 읽고, allowlist를 재검토
- 중요 결정 시: 그 자리에서 즉시 memory (메모리) 파일에 기록
지루합니다. 하지만 지루한 작업을 체계화하지 않는 한 하네스는 망가집니다. 하네스를 작성하는 시간보다, 유지보수(maintenance)하는 시간이 지금은 더 깁니다.
요약
3개월 운영하며 깨달은 점:
- 하네스는 정적인 파일 집합이라고 생각하기 쉽지만, 실체는 동적으로 열화되는 인프라(infrastructure)이다.
- 모델 업데이트는 CLAUDE.md와 subagent prompt를 동시에 구식으로 만든다.
- Hooks (훅)의 발화 조건은 subagent를 경유하는지 여부에 따라 달라진다. 테스트는 2개 계통으로 진행해야 한다.
- Subagent는 부모 context (컨텍스트)를 상속하지 않는다. Task tool에 매번 온보딩 (onboarding) 내용을 작성해야 한다.
- Memory는 칠판이다. 중요한 사항은 즉시 파일로 다시 써넣어야 한다.
- Permission allowlist는 와일드카드를 피하고, tool 단위로 명시한다.
하네스(Harness)는 작성만으로 끝나는 것이 아니었습니다. 작성한 후부터가 시작입니다. CLAUDE.md를 정비한 순간보다, 3개월 후에 "여전히 의도한 대로 작동하고 있는가"를 점검하는 시간의 가치가 훨씬 더 높습니다.
설계자에서 운영자로. 이 전환을 해낸 사람만이 하네스 엔지니어링 (Harness Engineering)을 진정으로 능숙하게 다룰 수 있다고 생각합니다. 즐겁게 가봅시다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기