지식

[Claude Code] 어제의 삽질을 오늘 세션은 모른다 — 에이전트 하네스 my-agent-harness

혹시 이런 적 있으신가. 어제 30분을 갈아 넣어 겨우 고친 버그가 있다. 원인도 찾았고 해결책도 손에 넣었는데, 오늘 새 세션을 열면 에이전트는 그 얘기를 처음 듣는다. 같은 자리, 같은 30분. 세션은 끝나면 사라지니까.

이런 문제를 다루는 일에 이름을 붙이자면 하네스 엔지니어링(harness engineering)에이전트가 작업할 수 있는, 내가 원하는 방식으로 일하게 되는 환경을 만드는 일이다. 모델을 더 똑똑하게 만드는 게 아니라, 모델이 일하는 자리를 짓는다. "작업할 수 있는"만이라면 런타임을 까는 것으로 끝나지만, "내가 원하는 방식으로"가 붙는 순간 일이 된다 — 무엇을 기억하고, 무엇을 신뢰하고, 무엇이 기계로 강제되고, 작업이 어떤 절차로 굴러가는지를 내 기준으로 환경에 인코딩하는 것. 전부 모델 밖, repo 안의 문제다.

이 문제를 지난 몇 주 동안 실제 워크스페이스에서 굴리며 다듬은 결과물을 템플릿으로 추출해 공개했다: my-agent-harness. GitHub의 "Use this template"으로 찍어 쓰는 에이전트 하네스다. 이 글은 루프 엔지니어링까지의 하네스 기록이다 — 실제로 굴려본 범위가 거기까지라서다. 기억(위키)을 세우고, 그 위에 무인 구현 루프를 올리기까지 무엇이 들어 있고, 왜 그렇게 설계했고, 어떻게 쓰는지를 담았다. "최종판"이라고 부르기엔 아직 열린 질문이 남아 있는데, 그건 글 끝에 적었다.

한 문장 요약

세션이 남긴 것을 repo에 붙잡아 둔다. 그 위에 세 층이 쌓인다:

무엇이 달라지나
🧠 기억 (wiki) 어제 알아낸 걸 오늘 세션이 알고 시작한다. 지식은 wiki/에 쌓이고, 사람이 승인한 것만 신뢰되고, 낡으면 기계가 표시한다
🔁 무인 구현 루프 (loop) 스펙을 파일에 박아두면 루프가 사이클을 굴린다. 통과한 것만 커밋되니 자리를 비워도 된다
👥 역할 분담 (Advisor/Worker) 판단은 메인 세션이 하고, 손이 많이 가는 구현은 서브에이전트가 맡는다

용도는 정해두지 않았다. 서비스 개발이든 인프라 구축이든 공부 노트든, 시공 인터뷰가 상황을 물어보고 거기 맞게 짓는다.

빠른 시작

시작에 필요한 건 세 줄이다.

# GitHub에서 "Use this template" → 새 repo 생성 후:
git clone < repo URL> my-harness && cd my-harness

scripts/setup.sh    # githooks 활성화 + 도구·이름 검사 (몇 번 돌려도 안전)
claude              # repo 루트에서 실행 → trust 다이얼로그 수락

그리고 Claude Code 안에서 /wiki-start 한 번. 질문을 몇 개 던진다 — 무슨 일을 하는 repo인지, 혼자 쓰는지 팀이 쓰는지. 답을 듣고 위키의 방 구조(섹션·태그·규약)를 설계해 보여주고, 승인하면 시공한다. 필요한 것은 git, python3 + PyYAML, Claude Code CLI. gitleaks는 선택이지만 깔아두면 비밀이 커밋되기 전에 로컬에서 먼저 걸린다.

그 뒤로는 평소처럼 쓰면 된다. 코딩하고, 디버깅하고, 물어보고. 기록할 값어치가 있는 게 나오면 에이전트가 초안을 보여주며 먼저 제안하고, 승인하면 커밋된다. 세션을 닫기 전 /wiki-record만 습관으로 만들면 된다.

1층: 기억 — 위키

위키의 리듬은 이렇다.

시점 무슨 일이
최초 1회 /wiki-start — 인터뷰로 섹션·태그·규약을 설계하고 승인받아 시공
세션 중 기록 트리거 감지 → 본문을 보여주고 → 승인 → verified로 커밋
세션 끝 /wiki-record — 세션을 되짚어 남길 지식을 기록 (마지막 기회)
분기마다 /wiki-curate — 남은 draft와 만료 문서 처분
상시 pre-commit·CI가 형식·비밀·규격·만료를 기계적으로 검사

설계하며 가장 오래 붙잡고 있던 질문은 "위키는 왜 죽는가"였다. 답은 대체로 넷이다: 낡은 문서가 신뢰를 무너뜨리고, 잡음이 쌓여 검색이 안 되고, 자동화가 조용히 죽고, 비대해져서 아무도 안 읽는다. 그래서 규칙 세 개를 뼈대에 박았다.

  • verified만 믿는다 — draft는 누구나 쓸 수 있지만, 사람이 읽고 승인해야 verified가 된다. 승인 게이트의 목적은 타인 검토가 아니라 노이즈 필터라서, 혼자 쓰는 개인 모드에서도 게이트는 유지된다.
  • 낡음은 기계가 표시한다 — 유효기한이 지나면 lint가 만료 배너를 알아서 달고, 근거가 된 코드가 바뀌거나 사라지면 stale-check가 잡아낸다.
  • 트리거 없는 기록은 노이즈다 — 30분+ 삽질, 반복 질문, 사고, 되돌리기 어려운 결정. 그것만 기록한다. "그래도 유용할 듯"이 위키를 죽인다.

그리고 위키↔코드가 어긋나면 코드가 맞다. 위키는 원본의 지도이지 대체물이 아니고, 지도와 지형이 다르면 지형이 맞으니까.

한 가지 결정이 더 있다. 템플릿은 일부러 "시공 전" 상태로 출고된다 — 섹션도 태그도 비어 있다. 위키가 죽는 걸 막는 장치(신뢰 게이트, 만료, 노이즈 필터, 기계 강제)는 어떤 상황에서도 필요하니 뼈대에 고정했고, 섹션 구성·태그·연결 관례처럼 상황을 타는 것은 비워두고 인터뷰가 채운다. 시공 전에는 lint가 문서 기록 자체를 막는다. 방도 안 만들고 이삿짐부터 들이지는 않으니까.

2층: 무인 구현 루프 — loop

기억이 바닥이라면 loop는 그 위에 올라가는 층이다. 구현 작업을 무인 사이클로 굴린다.

/loop-plan ─ requirements·features·progress 시공


사용자만 풀 수 있는 blocker 소진


┌─ Worker: 구현 노동 ◄─────────────┐
│      │                          │
│      ▼                          │ 실패 → 수정 브리프로 재위임
│  Advisor: diff 확인·검증 재실행 ──┘
│      │ 통과
│      ▼
│  통과분만 커밋 · progress.md에 다음 사이클 인계
│      │
└──────┴─ features 전 항목 판정될 때까지 반복 → ✅ 정지·최종 보고

/loop-plan이 스펙 인터뷰로 요구사항을 확정해 docs/projects/<slug>/에 스펙(requirements.md)과 상태 파일 2종(features.json·progress.md)을 시공하고, 사이클을 실제로 돌리는 건 Claude Code 내장 /loop(주기 재실행)이다. 상태는 전부 파일에 있다 — 모델의 기억이 아니라 파일이 진실이다. 사이클이 몇 번을 돌든, 세션이 몇 번을 죽든, 다음 wakeup은 progress.md를 읽고 이어간다.

루프는 하나뿐이다. 작업이 크든 작든 같은 구조를 쓰고 분량만 거기 비례한다. "작은 작업용 간이 절차"를 따로 두는 순간 어느 쪽을 탈지 판정하는 비용이 생기고, 간이 절차는 반드시 부실해진다.

이 그림의 힘은 여기서 나온다 — 작성자와 채점자가 다르다. Worker는 코드를 쓰되 커밋하지 않고, Advisor는 "다 됐습니다"를 믿지 않고 diff와 검증을 직접 다시 돌려 통과분만 승인한다. 커밋 직전에는 구현 과정을 모르는 reviewer 서브에이전트가 새 눈으로 한 번 더 본다. 같은 컨텍스트가 만들고 승인하면 맹점도 같이 승인되니까.

3층: 역할 분담 — Advisor / Worker

메인 세션은 Advisor다. 요구사항 분석, 작업 분해, 브리프 작성, 결과 검증, 커밋 승인 — 판단에 집중한다. 구현 노동 전부는 Worker(서브에이전트, Advisor보다 한 티어 아래 모델)에게 위임한다. 서로 독립적인 작업은 병렬로.

운영해 보며 넣게 된 규칙 하나: 진행 중인 Worker에 개입하지 않는다. 개입은 증거 기반 4조건(블로커 보고 / 작업 소진 / 반복 실패+구체 교정 / 명백한 이탈)에서만 하고, 위임한 뒤에 완료 기준을 올리지 않는다. 감독자가 조바심으로 끼어들기 시작하면 위임의 이득이 사라진다.

문서는 권고, 기계는 강제

이 하네스에서 제일 좋아하는 설계 원칙이다. 에이전트에게 "하지 마라"라고 문서에 적는 것과, 애초에 할 수 없게 만드는 것은 다르다. 문서는 언젠가 컨텍스트에서 밀려나지만 훅은 밀려나지 않는다.

장치 하는 일
wiki-lint.py 위키 형식 검사 + 색인 재생성 + 만료 배너 삽입·제거
skill-lint.py 스킬 정본·래퍼 쌍과 서브에이전트 frontmatter 규격 검사
loop-lint.py loop 상태 파일 형식 검사 + 스펙 문구 휴리스틱
wiki-stale-check.py 근거 코드가 바뀌거나 사라진 걸 감지해 낡은 문서 표시
githooks pre-commit(gitleaks + 변경 영역별 lint) · commit-msg(위키 커밋 장부 형식)
CI 서버 측 최후 방어선 — lint 3종 + 비밀 전 이력 스캔

색인(wiki/index.md)도 장치의 일부다. 단순 목록이 아니라 운영 신호판이라서, 전체·방별 집계(검증됨/초안/만료)와 상한 초과 경고, 만료 표시, 신뢰 순 정렬이 lint를 돌릴 때마다 새로 그려진다. 색인을 사람이 손으로 관리하는 순간 색인부터 낡기 시작한다.

스킬 한 벌

외울 필요는 없다. 에이전트가 상황을 보고 먼저 제안하는 편이 더 잦다.

스킬 언제
/wiki-start 최초 1회 위키 시공 — 인터뷰로 섹션·태그·규약 설계
/wiki-record 세션에서 얻은 지식을 승인받아 verified로 기록
/wiki-curate 분기 큐레이션 — 남은 draft·만료·고아 문서 처분
/wiki-import 위키 밖 문서 뭉치(작업 로그·런북·노트)를 증류해 이식
/loop-plan loop로 굴릴 작업의 스펙과 상태 파일 시공
/handoff 다른 세션·다른 머신·다른 사람이 이어받을 인계문 작성

정본은 .claude/skills/에 있고, .agents/skills/에는 AGENTS.md 규약을 읽는 다른 런타임을 위해 정본을 가리키는 얇은 래퍼만 둔다. 복제하면 skill-lint가 잡는다 — 사본은 반드시 낡고, 두 벌이 어긋나면 어느 쪽도 못 믿게 되니까. 같은 이유로 AGENTS.md가 원본이고 CLAUDE.md는 symlink다.

기존 프로젝트에 얹기

새 repo가 아니라 기존 repo에 얹으려면 wiki/, docs/, scripts/, .claude/, .agents/, CI 워크플로우, AGENTS.md, .gitignore 항목을 복사하고 CLAUDE.md symlink를 다시 만들면 된다(ln -s AGENTS.md CLAUDE.md). 손볼 곳은 드물지만 세 군데 있다: 에이전트의 git push는 기본 차단(허용하려면 settings의 deny에서 제거), repo 안에 독립 하위 repo를 두는 구조면 stale-check의 SUBREPOS, 기본 브랜치가 main이 아니면 CI 설정.

한 가지 주의 — 위키 하나는 repo 하나를 기억한다. 프로젝트가 여럿이면 한 위키에 몰아넣지 말고 템플릿을 한 벌 더 찍는 게 낫다. 방이 프로젝트 이름으로 채워지는 순간 위키는 검색되지 않는 서랍이 된다.

지속 가능성의 7조건

몇 주 굴리며 내린 결론을 표로 남긴다. 에이전트용 지식 저장소가 살아남으려면 이게 다 필요했다.

조건 구현
신뢰 게이트 draft→verified, 사람 승인 필수
원본 우위 근거(sources) 필수, 불일치하면 원본이 이김
시간 방어 만료 배너, stale-check
노이즈 필터 기록·조회 트리거, 크기 상한
삭제 가능성 삭제 사유가 장부에 남아 재기록 차단
기계 강제 lint 3종·gitleaks·githooks·CI
거버넌스 책임자(DRI) + 분산 검증

거창해 보이지만 쓰는 입장에서는 명령 세 줄과 습관 하나(/wiki-record)가 전부다. 나머지는 에이전트가 이미 읽고 있고, 필요한 순간에 먼저 물어본다.

덧붙이면, 이 하네스에 컨텍스트 엔지니어링이나 프롬프트 엔지니어링 층은 따로 없다. 일부러 뺐다기보다 필요를 못 느꼈다 — 그 부분은 Claude Code가 워낙 잘해서다. 다만 정직하게 말하면 위키 자체가 컨텍스트 엔지니어링이긴 하다. 무엇을 기록하고(노이즈 필터), 무엇을 신뢰하고(verified), 무엇을 만료시킬지(시간 방어)를 고르는 일은 결국 다음 세션의 컨텍스트에 무엇을 넣을지를 설계하는 일이니까. 문장 단위의 프롬프트 다듬기는 런타임에 맡기고, 이 하네스는 컨텍스트에 들어갈 지식의 재고 관리를 맡은 셈이다. 그래서 한 줄로 자리매김하면 — my-agent-harness는 컨텍스트 엔지니어링(위키)과 루프 엔지니어링(loop)을 사용한 하네스다.

남은 질문 — 위키는 얼마나 커져도 되는가

솔직히 말하면 이 하네스는 아직 "최종"이 아니다. 답을 못 낸 질문이 두 개 있다.

위키가 무한정 커지는 게 좋은 현상인가? 지금의 설계는 노이즈 필터(기록 트리거)와 크기 상한으로 성장을 억제하는 쪽에 서 있다. 하지만 트리거를 정직하게 통과한 지식만 쌓여도 위키는 계속 자란다 — 삽질과 사고와 결정은 프로젝트가 사는 한 계속 생기니까. 그 성장은 기억의 축적인가, 아니면 결국 검색 비용과 컨텍스트 비용으로 되돌아오는 부채인가. "낡으면 만료시키고 분기마다 처분한다"가 지금의 답이지만, 처분 속도가 생산 속도를 못 따라가는 시점은 언젠가 온다.

위키가 더 커지면 그때는 구조를 어떻게 잡아야 하는가? 지금 구조는 평평하다 — 섹션 몇 개에 문서가 나란히 눕고, 색인 하나가 전체를 조망한다. 이 구조는 수십 문서까지는 잘 버티는데, 수백 문서가 되면 어떻게 될까. 섹션을 계층화할 것인가, 위키를 쪼갤 것인가("위키 하나는 repo 하나"의 연장선), 아니면 색인을 사람이 읽는 지도가 아니라 에이전트가 질의하는 검색 계층으로 바꿀 것인가. 어느 쪽이든 "에이전트가 필요한 지식에 몇 번의 열람으로 닿는가"가 판정 기준이 될 텐데, 그 임계점을 아직 실측해 본 적이 없다.

이 두 질문의 답은 문서를 수백 장 쌓아본 뒤에야 나올 것 같다. 답이 생기면 그때 진짜 최종판을 쓰겠다. 비슷한 걸 굴려본 분의 의견은 언제나 환영이다.

그 너머 — 그래프 엔지니어링. 이 하네스는 아직 루프 엔지니어링까지만 써봤다. 그 너머에 눈여겨보는 후보 답이 하나 있다 — 위키를 평평한 문서 더미가 아니라 문서를 노드로, 문서 사이의 참조를 간선으로 보는 그래프로 다루는 것이다. 색인이 목록이 아니라 그래프의 진입점이 되고, 에이전트의 탐색이 "섹션을 훑는다"에서 "연결을 따라간다"로 바뀌면, 위의 임계점 문제가 다르게 풀릴지도 모른다. 다만 아직 가보지 않은 길이라, 여기 적을 수 있는 건 방향뿐이다.

repo는 여기에: github.com/atmigtnca/my-agent-harness — "Use this template"으로 찍어서 쓰시면 된다.

조회 1댓글 0

댓글

아직 댓글이 없습니다. 첫 댓글을 남겨보세요.