AI 하네스
Claude Code를 무인으로 부리려고 짠 협업 하네스.
무엇인가
혼자 여러 프로젝트를 돌리려면, AI를 일관되게·병렬로·무인으로 부릴 체계가 필요했다. 그래서 Claude Code 위에 직접 설계한 것이 이 협업 하네스다. 크게 다섯 가지를 얹었다. ① 규칙 자동주입(Skills) ② 전문 작업 자율 위임(역할별 Agents) ③ 야간 무인 자동화 ④ 세션을 넘어 지속되는 파일 메모리 ⑤ 도구 그라운딩(MCP).
Skills·Agents·MCP는 Claude Code의 기능이다. 내 몫은 그 위에 무엇을 어떻게 얹었는가에 있다. 에이전트 구성, 규칙 분리, 자동화 파이프라인, 안전 스코핑. 자동화의 경계는 계속 밀었다. 처음엔 초안·수집까지만 자동이고 판단·발행은 사람이 쥐었는데, 개발로그는 결국 발행까지 무인으로 넘겼다. 위키도 한동안은 낡았다고 드러내기만 했는데, 지금은 밀린 만큼을 매일 조금씩 자동으로 갚는다.
왜
AI 협업에는 반복되는 고질이 있다. 매번 같은 규칙을 설명해야 하고, 대화가 길어지면 규칙이 희석되고, 그래서 결과가 일관되지 않고, 작업은 순차로만 흐른다. 이걸 구조로 풀었다: 공통 규칙은 Skills로 자동 주입하고, 전문 작업은 Agents에 자율 위임하고, CLAUDE.md는 경량 라우터로.
하네스 3계층
핵심 원칙은 중복 제거다. 에이전트 A도 B도 알아야 할 것은 Skills에, 전문가에게 맡기고 결과만 받고 싶은 것은 Agent에 둔다.
| 계층 | 무엇 | 역할 |
|---|---|---|
| Skills | 규칙 자동주입 | 모든 대화·에이전트에 자동 적용. "any 금지"를 한 번 쓰면 어느 에이전트가 짜든 지켜짐 → 일관성 |
| Agents | 자율 위임 | 별도 컨텍스트·병렬 실행·규칙 100% 유지. "전문가를 새로 부르는 것" |
| CLAUDE.md | 경량 라우터 | 한 페이지. 에이전트 목록 + 워크플로우 안내만, 상세는 Skills/Agents에 위임 |
Skills는 처음엔 코딩 규칙과 프로젝트 운영 규칙 둘이었는데 다섯이 됐다. 판단 기준을 세 개 더 뗐다. 시각 설계를 무슨 수치로 판단하는지(design-craft), 시각 효과를 CSS·Canvas·WebGL 중 무엇으로 구현하는지(visual-tech), 저장소에 이미 있는 검사·생성 스크립트가 무엇인지(repo-guards). 마지막 하나는 "새로 만들기 전에 있는 걸 먼저 본다"를 강제하려고 만들었다. 차트 라이브러리가 있는 줄 모르고 SVG를 347줄 손으로 짠 적이 있어서다.
그리고 라우터 자체를 3층으로 갈랐다. 저장소 하나에 성격도 스택도 다른 프로젝트가 스무 개 넘게 있는데, 라우터 한 장이 그걸 다 이고 있으니 스택을 오판했다. 지금은 루트는 공통 규칙만, 스택·빌드 명령·디자인은 projects/<이름>/CLAUDE.md, 직무 기준은 에이전트 정의로 나뉜다. 세션은 저장소 루트에서 시작해서 하위 CLAUDE.md가 자동으로 안 읽히므로, "무엇부터 읽는가"라는 절차를 루트 맨 앞에 박아뒀다.
라우터 안의 프로젝트 목록도 한 번 정리했다. 같은 목록이 두 곳에 있으면서 서로 자기가 정본이라고 하고, 정작 둘 다 실제와 어긋나 있었다. 지금은 역할을 갈랐다. 라우터의 표는 무엇이 어디 있는지만 말하고, 상태와 현황은 별도 문서 하나가 정본이다. 겸사겸사 함정도 적어뒀다. 저장소 이력으로 추적하지 않는 디렉터리가 몇 개 있는데, 거기서 커밋 0은 "안 했다"가 아니라 "여기서는 안 센다"는 뜻이다.
라우터에서 규칙을 덜어내기도 했다. 7월 말에 "메인이 일하는 방식" 규칙 여섯 개를 적어뒀는데, 여드레 뒤에 세어 보니 그중 여섯 개를 하루에 다 어겼다. 같은 진단을 두 번 하고 두 번 다 문서로 덮은 다음 두 번 다 고쳤다고 말한 상태였다. 문서를 늘리는 것으로는 안 바뀐다는 뜻이라, 그날 여섯을 넷으로, 넷을 셋으로, 셋을 둘로, 둘을 0으로 네 번에 걸쳐 걷어냈다. 지금 그 절에 남은 건 "시키면 하고, 끝나면 무엇을 어떻게 해서 완료했다고 보고한다" 한 문장과 왜 이렇게 짧은지에 대한 설명뿐이다.
대신 라우터 맨 앞에 절차가 아니라 목적을 뒀다. 쉽고 · 빠르고 · 예쁘게. 고를 자리를 없애고, 왕복을 없애고, 그러고도 봐줄 만해야 한다는 세 줄이다. 여기엔 앞의 실패가 그대로 들어 있다. 빠르려고 단계를 건너뛰면 며칠 뒤에야 드러날 결함이 생기니, 빠른 것은 단계를 빼는 것이 아니라 되돌아오지 않는 것이라고 적어뒀다.
개발 사이클 오케스트레이션
개발 단계마다 역할별 전문 에이전트 7개를 매핑하고, 모델을 티어링했다(추론=opus, 기계적 작업=sonnet).
| 에이전트 | 모델 | 단계 |
|---|---|---|
senior-clean-architect | opus | 구현 · 스택은 프로젝트 CLAUDE.md를 읽어 판단 |
code-reviewer | opus | 리뷰 · 심각도 4단(차단·중요·Nit·칭찬) |
test-runner | sonnet | 검증 · 빌드·타입·린트 |
commit-manager | sonnet | 커밋 · diff 분석·자동 커밋 |
librarian | opus | 도서관 · 프로젝트 가로질러 탐색·회고 |
blogger | opus | 글쓰기 · 페이퍼·이슈·devlog에서 블로그 초안 |
designer | opus | 시각 · 정보 설계부터 위계·인코딩까지 |
한동안 여덟 개였는데 하나를 걷어냈다. 기획 단계의 requirement-planner는 Claude Code에 내장된 Plan 에이전트와 하는 일이 겹쳤다. 내가 만든 것이라고 남겨두면 선택 비용만 는다. 계획 수립은 내장 Plan에 넘겼다.
커밋 단계는 반대로 좁혔다. 원래는 모든 커밋이 commit-manager를 거쳤는데, 커밋 한 번에 5분이 들었다. 커밋 자체는 10초고 나머지는 에이전트를 부르고 돌아오는 왕복이었다. 게다가 에이전트가 백그라운드로 도는 사이에 같은 커밋이 한 번 더 만들어지는 사고까지 났다. 느린 것이 안전하지도 않았던 셈이다. 지금은 평범한 커밋을 직접 하고, 에이전트는 예민할 때만 부른다. 스테이징된 삭제, gitignore 변경, subtree 미러, push 갈라짐 같은 경우다. 원래 이 규칙이 막으려던 두 가지 중 담는 범위 실수는 "git add -A 금지, 담은 뒤 목록 확인" 한 줄이 막고, 파괴적 명령은 이미 훅이 막는다.
에이전트 정의에는 규칙만이 아니라 돌릴 명령도 박아둔다. 사서 에이전트가 ls로 앞 열두 줄만 훑어보고 "테스트가 거의 없다"고 보고한 적이 있다. 잘라낸 출력에 안 보인다는 건 없다는 근거가 아닌데 그렇게 읽은 것이다. 그래서 활동량·테스트 유무·CI·배포 대상처럼 사실로 말하게 될 항목마다 그걸 확인하는 명령을 표로 정의에 넣었다. 그 표에는 "커밋 수를 사람이 한 일로 읽지 마라"도 같이 들어 있다. 이 사이트는 야간 크론이 매일 커밋하기 때문에, 활동량을 말하려면 기계가 찍은 커밋을 먼저 갈라내야 한다.
여기서 하나 더 알게 된 게 있다. 내가 얹은 규칙 위에, 내가 끌 수 없는 층이 하나 더 있다. 도구 번들이 세션마다 "요청받지 않으면 서브에이전트를 부르지 마라"를 자동으로 붙이는데, 그게 이 저장소의 에이전트 워크플로우를 조용히 무력화했다. 설정으로 끄는 방법이 없으니 문서 쪽에서 풀었다. 라우터의 에이전트 절 맨 앞에 "이 표와 워크플로우는 상시 지시다"라고 못 박아, 그 문구가 말하는 '사용자의 요청'이 곧 이 절이라고 적어뒀다. 하네스를 남의 기본값 위에 짓는다면, 그 기본값과 부딪히는 지점을 찾아 명시적으로 덮어써야 한다.
명령을 일일이 치지 않아도, 요청 하나에 알맞은 에이전트가 자동으로 이어 붙는다. 화면 작업만은 순서를 못 박았다. designer가 정보 설계와 시각 방향을 먼저 잡고, 구현이 그 뒤에 오고, 다시 designer가 결과를 검수한다.

이 위키들을 채운 코드 전수조사도 이 하네스로 돌렸다. 15개+ 서브에이전트를 병렬로 띄워 여러 프로젝트를 동시에 조사했다. 하네스가 실제로 작동한다는 증거다.
자동화한 것
개발 기록·회고·문서 관리를 커맨드 8종 + 야간 무인 실행으로 파이프라인화했다.
/devlog는 그날 커밋·파일 수정시각·이슈 원장을 긁어 개발일지 초안을 자동 생성한다/issue로 이슈를 원장에 기록(open)하고, 해결을 반영(done)하고, 목록을 조회(list)한다/blog-draft·/publish로 이슈·배움을 블로그 글로 빚는다. 그리고 이 개발로그는 결국 발행까지 완전 무인으로 갔다(→ 반자동에서 완전 무인으로)/til로 그날 배운 것을 일지에 즉석 기록한다/trend로 매일 아침 여러 포럼(HN·arXiv 등)에서 기술 동향을 수집해 뉴스카드로 브리핑한다/ideas로 그 브리핑에 달린 "이거 써먹을 만함"을 나중에 프로젝트별로 다시 꺼낸다. 수집은/trend, 재사용은/ideas로 갈랐다/refresh로 코드에 밀린 위키를 커밋 근거로 다시 조사해 갱신한다. 지금 이 문서도 그 대상이었다

야간 무인, 그리고 안전 스코핑
launchd에 매일 도는 크론 셋을 걸었다. 밤 23:00 개발일지(/devlog), 아침 09:00 동향 브리핑(/trend), 10:00 RAG 평가. 대표는 야간 /devlog로, 헤드리스로 돌아 그날 일지 초안을 만들고 알림을 띄운다. 여기서 핵심 설계는 안전 스코핑이다. 무인으로 도는 만큼, Bash 전체가 아니라 읽기성 명령만 허용한다(git·find·grep·ls + 파일 읽기/쓰기). 임의 셸 실행은 차단해 무인 실행의 blast radius를 최소화했다. 발행처럼 blast radius가 큰 단계는 처음엔 기본 OFF로 두고 수동 루프로 검증했다. 그 검증이 끝난 뒤, 발행까지 무인으로 넘겼다(다음 절).
다만 이 스코핑이 주석에만 있고 명령에는 없던 자리가 하나 있었다. 평가 크론은 파일 두 개만 스테이징하도록 짜여 있고 "다른 세션의 미커밋 작업은 안 건드린다"고 주석까지 붙어 있는데, 정작 커밋 명령에는 경로 한정이 빠져 있었다. 그러면 다른 세션이 이미 올려둔 것까지 같이 커밋된다. 이 저장소는 여러 세션이 git 인덱스를 공유하니 결국 일어났다. 8월 26일 아침 관제판 데이터 갱신 커밋에 엉뚱한 게임 코드 백여 줄이 실려 나갔다. 내용이 날아간 건 아니고 커밋 경계가 흐려진 것뿐이지만, 같은 일을 하는 동향 크론 쪽은 그 자리에 경로 한정이 제대로 붙어 있다. 의도를 두 곳에 다르게 구현하면 안전한 쪽은 주석에만 남는다.
여기서 한 가지를 더 배웠다. 지키게 하고 싶은 것은 부탁이 아니라 훅으로 막는다. 문서에 "파괴적 git 금지"라고 아무리 써둬도 그건 읽히기를 기대하는 규칙이다. 지금은 도구 실행 직전에 훅이 reset --hard·checkout .·clean -fd를 차단한다. 이 저장소는 세션 여럿이 동시에 쓰기 때문에, 한 세션의 되돌리기가 다른 세션의 커밋 안 된 작업을 지운다. 경로를 한정해도 마찬가지라 경로 한정도 안전장치로 치지 않는다.
훅 배선 자체도 저장소에 넣었다. 그전엔 에이전트 정의와 스킬만 git으로 추적하고 설정 파일은 빼뒀는데, 그러면 훅이 사라져도 오류 하나 없이 조용히 멈춘다. 막는 장치가 없어진 걸 아무도 모르는 상태가 제일 나쁘다. 지금은 설정과 에이전트 기억까지 추적하고, 기계마다 다른 로컬 설정만 뺀다.
훅이 늘기만 하는 것도 아니다. 하루 사이에 훅 셋을 얹었다. 할 일이 전부 완료로 바뀌면 요청과 대조하게 하는 것, 위임 프롬프트가 비대해지는 걸 막는 것, 구현을 위임하기 전에 계획을 세우게 하는 것. 마지막 하나는 새벽 다섯 시에 들어와 두 시간을 못 살고 지워졌다. 다음 날 아침에 셋을 한 커밋으로 전부 걷어냈기 때문이다. 그날 남긴 건 파괴적 git을 막는 훅 하나뿐이다.
걷어낸 이유는 계산이 맞지 않아서다. 훅 셋과 측정 스크립트 하나를 만든 그날, 정작 쓸 화면은 하나도 안 나왔다. 기계로 막을 것은 되돌릴 수 없는 것뿐이다. 다른 세션의 커밋 안 된 작업이 날아가는 건 되돌릴 수 없으니 훅으로 막고, 나머지는 틀렸을 때 그 자리에서 고치면 된다.
반자동에서 완전 무인으로
처음엔 개발로그가 반자동이었다. 밤마다 일지 초안은 자동으로 쌓였지만, 그걸 블로그 글로 다듬어 발행하는 건 사람 몫이었다. 읽어보고 /publish로 토글하는 식이었다. 안전한 대신 매번 손이 갔다. 한 단계 줄여보려 후보 방식(밤마다 여러 편 뽑아두고 아침에 하나 고르면 발행, 안 고르면 제일 좋은 걸 자동 발행)도 만들어봤는데, 정작 검토를 어디서 하느냐가 걸렸다. 이 사이트는 정적 공개 사이트라 초안을 띄우면 남들 눈에 그대로 보인다. 비공개로 보려면 로컬 서버를 띄우거나 레포를 열어야 했고, 관문을 줄이려던 게 관문을 늘리고 있었다.
그래서 컨셉으로 돌아갔다. 이건 '무인 블로그'다. 발행까지 넘겼다. 지금은 매일 밤 클라우드 크론(맥이 꺼져 있어도 돈다)이 그날 커밋을 읽고, 글감이 될 게 있으면 제일 좋은 한 편을 써서 바로 발행한다. 없으면 아무것도 안 쓴다.

사전 검토를 없앤 대신 안전장치를 다른 데 뒀다. 초안 후 스스로 AI 냄새를 5개 이상 찾아 실제 커밋·결정으로 바꾸는 자기점검, 근거 없는 주장 금지, 그리고 빌드가 통과할 때만 발행. 문법 깨진 글이나 도구 출력이 샌 글은 아예 못 나간다. 통제는 사후다. 별로면 읽고 고치거나 지운다. 덜 다듬어진 글이 잠깐 올라갈 리스크는, '만드는 과정을 남긴다'는 이 블로그의 전제가 받아준다.
완전 무인엔 걸림돌이 하나 더 있었다. 그림이다. 스크린샷은 사람이 찍어 붙여야 하니, 그게 끼는 순간 무인이 아니다. 그래서 글에 들어가는 도식은 스크린샷 대신 AI가 그릴 수 있는 다이어그램 컴포넌트(흐름·비교·패널)로 바꿨다. 실제 구조에서 나온 정직한 도식만 그리고, 없는 UI를 그럴싸하게 지어내지 않는다. 크론이 사람 손 없이 그림까지 갖춘 글을 낼 수 있게 하려는 것이다.
무인은 실패까지 설계해야 한다
무인 자동화의 진짜 어려움은 성공이 아니라 실패 쪽에 있다. 사람이 안 보고 있으니, 죽어도 죽은 줄 모른다.
처음엔 거짓말을 했다. 크론이 종료 코드를 보지 않고 무조건 성공을 찍고 있었다. 그래서 사흘 내리 동향 큐레이션이 사용량 한도에 걸려 죽었는데도, 현황판은 사흘 내내 "실행 ok"라고 말했다. 자동화가 도는 줄 알았는데 실은 성공 도장만 찍히고 있었던 것이다. 관측 장치가 거짓을 말하면 없느니만 못하다.
지금은 세 단계로 다룬다.

여기서 핵심은 세 번째다. 재시도할 값이 있는 실패와 없는 실패를 구분하는 것. 연결이 끊긴 건 다시 부르면 대개 되지만, 한도에 걸린 건 곧바로 다시 불러도 같은 이유로 죽는다. 무작정 재시도하는 건 복구가 아니라 소음이다.
그리고 자동 복구는 숨기지 않는다. 재시도로 살아난 날은 현황판에도 "재시도 후 복구"라고 적는다. 조용히 살아나면 이 체계가 얼마나 자주 넘어지는지 아무도 모르게 된다.
이 설계가 실제로 값을 한 날이 8월에 다섯 번 있다. 일지 초안 단계가 두 번, 동향 큐레이션 단계가 세 번 네트워크로 죽었다. 다섯 번 다 그날의 나머지 단계는 끝까지 돌았고, 현황판에는 죽은 단계만 실패로 남았다. 단계를 갈라 기록해 두면 한 단계의 죽음이 그 밤 전체를 죽이지 않고, 무엇이 죽었는지도 그대로 보인다. 동향 쪽은 다음에 성공한 회차가 밀린 날짜를 보충으로 메워, 빠진 날이 그대로 구멍으로 남지 않았다.
이 기록이 실제로 일한 밤이 있다. 새벽에 시작한 회차에서 일지 초안 단계만 네트워크로 죽고, 뒤이은 드리프트 감지와 원장 갱신은 멀쩡히 끝났다. 현황판에는 그 한 칸만 "실패 · 네트워크"로 남고 나머지는 정상으로 남았다. 단계를 뭉뚱그려 한 줄로 찍었다면 그 밤은 통째로 성공이거나 통째로 실패였을 것이다. 다음 밤에 그 칸은 정상으로 돌아왔다.
문서가 낡으면 스스로 알려준다, 로튼 도큐먼트
이 위키의 각 페이지는 특정 시점의 코드를 조사해 쓴 "스냅샷"이다. 그런데 코드는 계속 움직인다. 그래서 문서마다 신선도 배지를 붙였다. 로튼토마토를 오마주해 로튼 도큐먼트라고 부른다. 문서를 마지막으로 고친 뒤 그 프로젝트에 쌓인 git 커밋 수(drift)를 세서 100 · 20 / (20 + drift)로 점수를 낸다. drift가 0이면 100%, 20커밋 쌓이면 50%로 떨어진다. 70% 이상은 FRESH, 40%대는 STALE, 그 아래는 ROTTEN이고, ROTTEN이면 인스펙터의 문서 아이콘이 찢어진 문서로 바뀐다.
계산은 빌드가 아니라 스크립트로 따로 돌려 freshness.json에 커밋한다. 배포 빌드는 git을 얕게 클론해 전체 히스토리가 없어서 drift를 셀 수 없기 때문이다. 여기엔 규칙이 하나 박혀 있다. 하네스가 감시하는 건 각 프로젝트의 코드지, 이 사이트가 발행한 글·위키가 아니다. 그래서 블로그를 아무리 발행해도 하네스 문서 자신은 안 썩는다. 실제로 이 배지는 이 문서 자신을 여러 번 잡았다.
이 문서에는 사각지대가 하나 더 있었다. 감시하는 코드 경로가 사이트 쪽뿐이어서, 정작 하네스를 뜯어고쳐도 하네스 문서는 안 썩었다. 에이전트를 여덟에서 일곱으로 정리하고 스킬을 셋이나 새로 만든 날에도 이 문서의 drift는 0이었다. 기계를 바꿨는데 그 기계를 설명하는 글만 멀쩡한 척을 하고 있던 셈이다. 라우터와 에이전트 정의, 스킬, 훅 스크립트를 감시 경로에 넣자 drift가 9에서 17로 뛰었고, 그제서야 이 문서가 야간 갱신 대상으로 잡혔다.
그런데 이 배지가 한동안 거짓말을 하고 있었다. 기준점을 "위키 파일을 마지막으로 고친 커밋"으로 잡았던 게 문제였다. 그러면 본문을 검증하지 않고 오타 하나만 고쳐도 배지가 100%로 되돌아간다. 실제로 위키 제목에서 긴 줄표를 걷어낸 커밋 하나가 전 문서의 배지를 한꺼번에 FRESH로 되돌렸다. 검증하지 않았는데 신선하다고 말하는 배지는 앞 절의 성공 도장과 똑같은 종류의 거짓말이다.
기준을 사실원장의 조사 지점으로 옮겨서 고쳤다. 위키의 근거가 되는 비공개 사실원장이 따로 있고, 거기 "여기까지 코드를 확인했다"는 커밋 해시가 박혀 있다. 배지는 그 지점 이후 쌓인 커밋을 센다. 그러자 전부 FRESH이던 표가 실제 상태를 드러냈다. 이 문서가 54커밋 밀린 27% ROTTEN으로 나온 것도 그때다.
같은 김에 계산기를 하나로 합쳤다. 그전엔 배지를 내는 스크립트와 썩음을 알리는 스크립트가 각자 git을 세고 각자 매핑 표를 들고 있어서, 같은 질문에 서로 다른 답을 내놓고 있었다. 지금은 계산이 한 곳, 문서와 코드의 대응이 한 곳이고 나머지는 그 결과를 읽기만 한다.
밀린 만큼이 아니라 정해진 만큼만
낡은 걸 감지한 다음이 문제다. 원래는 "가만히 있다가 많이 밀리면 한 번에 갚는" 구조였다. 그러면 밀릴수록 한 번의 일이 커지고, 커지니까 "너무 크면 건너뛴다"는 상한이 필요해지고, 결국 가장 썩은 문서가 자동에서 정확히 제외된다. 감지의 목적을 배반하는 구조였다.
작업 단위를 문서 전체에서 커밋 묶음으로 바꿨다. 원장에 박힌 커밋 해시를 오프셋 삼아, 매일 밤 그 지점 이후 정해진 개수만큼만 소화하고 오프셋을 전진시킨다. 컨슈머 오프셋과 같은 발상이다.

이 한 번의 변경으로 세 가지가 같이 풀렸다. 한 번에 처리하는 양이 고정이라 크기 상한이 필요 없어졌고, 매일 조금씩 소화하니 빚이 쌓이지 않고, 무엇보다 진도를 따로 기록할 것이 없어졌다. 오프셋이 곧 어디까지 읽었는지라서, "여기까지 했다"는 도장을 따로 찍을 필요가 없다. 기록할 것이 없으면 거짓말을 할 수도 없다.
한 가지는 나중에 고쳤다. 처음엔 오프셋이 전진했으면 성공, 그대로면 실패로 판정했는데 그러다 정상 실행을 실패로 찍었다. 정상인데 오프셋이 안 움직이는 경로가 둘 있었기 때문이다. 바꿀 게 없는 밤과, 원장은 이미 최신이고 위키만 뒤처져서 위키만 맞추는 밤이다. 그래서 성공과 실패는 종료 코드로만 가르고, "무슨 라운드였나"는 오프셋이 움직였는지로 따로 분류해 현황판에 적는다. 진도를 재는 자와 생사를 재는 자를 겹쳐 쓰면 안 된다는 이야기다.
이 설계가 실제로 값을 한 밤이 8월 말에 있었다. 원장 갱신이 응답이 끊긴 채 중간에 죽었고, 죽기 직전까지 고친 내용은 커밋으로 남았다. 그런데 그 문서의 오프셋은 한 칸도 움직이지 않았다. 다음 밤이 같은 지점에서 이어받아 마저 처리했고, 그 이틀 뒤에는 위키만 뒤처진 문서를 골라 위키만 맞추는 라운드가 실제로 돌았다. 죽어도 진도를 잃지 않는다는 게 말이 아니라 커밋으로 확인된 셈이다.
이 구조가 실제로 도는 흔적은 커밋에 남는다. 8월 들어 사람이 아무것도 시키지 않은 채로 사실원장과 위키가 스물여덟 번(8월 1일부터 29일까지) 고쳐졌다. 셀 측정기 문서, RAG 문서, noon-desktop 문서로 시작해 지금은 이 문서 차례까지 왔다. 밀린 순서대로 하나씩, 정해진 분량만큼만 갚아 나가는 셈이다.
한 가지는 정확하게 적어둔다. 커밋 날짜만 보면 19일이 비어 있고 20일에 두 번이 몰려 있다. 빠뜨린 게 아니라, 19일 밤 회차가 자정을 넘겨 시작해 새벽 한 시 반에 커밋됐기 때문이다. 스무 날에 스무 번이지만 "하루도 빠짐없이"는 아니라서, 날수 대신 회차로 센다. 이후로도 밤마다 이어져 29일까지 스물여덟 번이 됐다.
재미있는 건 이 문서를 마지막으로 갱신하게 만든 커밋들의 정체다. 지난 스무하루 동안 하네스 감시 경로에 쌓인 커밋은 서른여섯 개였는데, 그중 사람이 만진 것은 하나도 없었다. 문서를 고친 열일곱 개와 관제판 데이터를 갱신한 열아홉 개, 전부 이 자동화 자신이 남긴 것이다. 자기가 남긴 커밋 때문에 자기가 낡았다고 판정돼서 자기를 다시 조사하는 셈인데, 이건 결함이라기보다 이 구조의 정직한 부작용이다. 그래서 에이전트 정의에도 "커밋 수를 사람이 한 일로 읽지 마라"를 박아둔 것이다.
앞 절의 거짓 성공이 왜 생겼는지도 여기서 분명해진다. 날짜는 "어디까지 했는지"를 표현하지 못한다. 그래서 별도의 성공 도장이 필요했고, 그 도장이 거짓말을 했다. 커밋 해시는 그 자체가 진도라서 도장이 필요 없다. 관측 장치를 고치는 것보다 관측할 필요가 없는 구조로 바꾸는 쪽이 낫다.
세션을 넘는 파일 메모리
세션이 끝나도 유지되는 파일 기반 장기 기억을 뒀다. 사용자 배경, 행동 규칙(피드백), 프로젝트 결정을 개별 파일로 저장하고 인덱스로 매 세션 로드한다. 그래서 "디버깅은 로그 먼저", "레거시 이식 규칙" 같은 교훈이 세션을 넘어 축적되고, 같은 실수를 반복하지 않는다.
최근엔 층을 하나 더 뒀다. 에이전트마다 자기 몫의 기억 폴더를 갖는다. designer는 이 사이트의 디자인 규격과 관제판 규칙, 헤드리스 크롬에서 반복해 걸린 함정을 자기 폴더에 쌓았고, 구현 담당은 "편집이 실제로 반영됐는지 확인할 것" 같은 자기 버릇을 적어뒀다. 사람이 주는 규칙과 에이전트가 스스로 겪은 것을 갈라 둔 셈이다. 처음엔 이 둘뿐이었는데 지금은 넷이다. 리뷰 담당은 "리뷰만 하고 고치지는 말 것"을, 사서는 서가 지도와 함께 확인 없이 보고했던 그날의 기록을 자기 폴더에 들고 있다. 나중에 붙은 두 폴더가 둘 다 같은 종류의 사고에서 나온 셈이다. 이 폴더도 저장소에 커밋하기 때문에, 기계를 갈아도 쌓은 게 남는다.
도구 그라운딩 (MCP)
프롬프트만으로는 부족한 작업엔 전용 도구를 붙인다. 라이브 문서(context7), 브라우저 자동화(playwright), 그리고 자작 MCP(그어봄)까지 에이전트 도구로 편입했다. "프롬프트만"이 아니라 "프롬프트 + 전용 도구"로 그라운딩한다. noon-desktop의 전문가 모드와 같은 철학이다.
또한 루트뿐 아니라 각 프로젝트에도 그 프로젝트 전용 CLAUDE.md를 둔다. 스택, 빌드 명령, 이미 있는 자산, 다시 가져오면 안 되는 방향까지 그 프로젝트 안에 적어두는 식이다. 같은 하네스 패턴이 스튜디오 전체로 스케일된다.
설계 원칙
규칙은 Skills 한 곳, 워크플로우는 Agents. 여러 문서에 같은 금지사항을 쓰지 않는다. 그래야 일관성이 깨지지 않는다.
너무 적으면 범용화로 전문성이 떨어지고, 너무 많으면 선택 비용·중복·유지보수가 는다. "혹시 필요할까봐" 미리 만들지 않고, 반복되는 패턴이 보일 때만 추가한다. 빼는 쪽도 같다. 내장 기능과 겹치던 기획 에이전트는 지웠다. 내가 만들었다는 이유로 남겨두면 선택지만 늘어난다.
읽기성 명령만, 임의 셸 실행 금지, push는 스코프 한정. 무인으로 도는 것일수록 blast radius를 먼저 좁힌다. 발행처럼 되돌리기 어려운 단계는 처음엔 꺼두고 검증부터 했다.
수동 커밋 라우터(v1) → Skills 자동주입 + Agents 자율 스폰(v2). 일하는 방식 자체를 관찰하고, 반복 고질이 보이면 구조를 다시 짠다.
정적 공개 사이트에선 비공개 검토가 관문만 늘렸다. '무인 블로그'라는 컨셉대로 사전 검토를 빼고, 대신 자기 AI-검출·빌드 게이트·근거만으로 막았다. 통제는 사후(읽고 고치기)로 옮겼다. 덜 다듬어진 글이 잠깐 올라갈 리스크는 '과정을 남긴다'는 이 블로그의 전제가 받는다.
처음엔 위키 본문을 일부러 자동화에서 뺐다. 위키 한 페이지는 여러 프로젝트를 가로질러 추려낸 판단이라, AI가 통째로 다시 쓰면 그 판단이 뭉개진다고 봤기 때문이다. 그래서 한동안은 로튼 도큐먼트로 "낡았다"는 신호만 냈다.
지금은 갱신까지 무인으로 넘겼고, 대신 경계를 다른 곳에 그었다. 통째로 다시 쓰지 않고 어긋난 곳만 고친다. 파일과 줄 번호나 커밋 해시로 근거를 못 대는 사실은 아예 손대지 않는다. 애매하면 원문을 그대로 둔다. 틀리게 고치는 것보다 낡은 채로 두는 쪽이 낫기 때문이다. 안전장치는 실행 쪽에도 하나 더 있다. 원장 자동 갱신은 스크립트 기본값이 꺼짐이고, 크론 등록에서 환경변수로만 켠다. 손으로 그냥 돌리면 낡음을 알리는 데서 멈춘다.
무인으로 도는 것에 성공 도장을 미리 찍어두면, 죽어도 죽은 줄 모른다. 실제로 사흘 내내 실패한 자동화가 현황판에서는 정상으로 보였다.
지금은 종료 코드와 산출물을 같이 보고, 실패면 종류까지 갈라 적는다. 되살릴 값이 있는 실패만 다시 부르고, 그래도 안 되면 다음 날 보충하고, 그마저 실패하면 실패로 노출한다. 관측 장치가 낙관적이면 그 장치는 없느니만 못하다.
규칙을 늘려서 고쳐 본 적이 여러 번인데 안 됐다. 여섯 개를 적어두고 하루에 여섯 개를 다 어긴 날, 규칙을 다시 쓰는 대신 전부 지웠다. 지키지 않는 규칙을 남겨두면 그 규칙을 관리하는 데 시간이 가고, 문서만 보면 잘 돌아가는 것처럼 보인다. 같은 날 화면 측정 스크립트도 걷어냈다. 자기가 잰 값을 판정 근거로 쓰려면 그 측정부터 믿을 수 있어야 하는데 그렇지 않았다. 하네스는 늘기만 하는 것이 아니라 줄기도 한다.
회고
이 하네스의 증거는 이 포트폴리오 자체다. 5개+ 프로젝트를 1인이 끌고 갔고, 개발일지·이슈·블로그가 매일 밤 무인으로 쌓이며, 이 위키들을 채운 전수조사(15+ 병렬 서브에이전트)까지 전부 이 하네스 위에서 돌아갔다. 8월 들어서는 이 문서들 자신도 스물여덟 번 사람 손 없이 갱신됐다.
그래서 나는 "AI를 쓰는 사람"이 아니라 "AI를 부리는 체계를 설계·운영하는 사람"이다. 선은 분명하다. Claude Code 위에 얹은 하네스 설계가 내 몫이고, 무인으로 넘긴 것은 수집·초안·발행·문서 갱신까지다. 무엇을 만들지, 무엇을 자동화할지, 그 자동화를 켤지 끌지, 그리고 나온 결과를 어떻게 볼지는 여전히 사람이 쥔다. 그 경계를 아는 것까지가 이 방법론의 일부다.