releases/docs/HARNESS.md

7.2 KiB

개발 하네스 — 폐쇄루프 운영 지침

이 저장소에서 AI 코딩 에이전트가 자율적으로, 그러나 검증 없이는 완료를 선언할 수 없게 일하도록 짜 놓은 장치들의 설계와 운영법. 사람과 에이전트 모두 이 문서를 읽는 대상이다.

왜 필요한가

에이전트의 가장 흔한 실패는 코드를 못 짜는 게 아니라 "됐습니다"라고 말해버리는 것이다. 테스트가 깨진 채로, 실행해 보지도 않고, 문서는 옛날 상태인 채로 턴이 끝난다.

해법은 프롬프트로 더 강하게 부탁하는 게 아니라, 에이전트가 건너뛸 수 없는 결정적 층을 두는 것이다. 확률적인 판단(모델)과 결정적인 검사(훅)를 분리한다.

5계층 구조

계층 역할 이 저장소에서
① 하네스 에이전트가 사는 환경 Claude Code + .claude/settings.json
② 루프 계약 "완료"의 정의 AGENTS.md §4 완료 기준 · CLAUDE.md
③ 상태층 세션이 끊겨도 남는 상태 docs/BACKLOG.md 체크박스 · docs/IMPLEMENTATION_AUDIT.md · SessionStart 브리핑
④ 체커 에이전트가 못 건너뛰는 검사 Stop 훅 → dotnet test
⑤ 사람 관문 되돌릴 수 없는 일 앞의 확인 커밋·푸시·배포는 사용자 승인 (/release 스킬)

핵심은 ④다. 나머지는 ④가 있어야 의미가 생긴다.

구성 요소

AGENTS.md                     ← 도구 중립 작업 계약 (Codex/Cursor 등도 읽음)
CLAUDE.md                     ← @AGENTS.md 임포트 + Claude Code 전용 지침
.claude/
  settings.json               ← 훅 등록 + 권한 (커밋됨, 팀 공유)
  hooks/
    session-brief.sh          ← SessionStart: 저장소 상태를 컨텍스트에 주입 (상태층)
    verify-on-stop.sh         ← Stop: 테스트 게이트 (체커)
  rules/                      ← 경로별 자동 로드 규칙 (해당 파일을 열 때만 컨텍스트에 들어옴)
    desktop-wpf.md            ← VideoDownloader.App/**, Browser/**
    engine-core.md            ← Core/**, Server/**
    mobile-maui.md            ← Mobile/**, Mobile.Core/**
    tests.md                  ← Tests/**
  skills/                     ← 호출될 때만 로드되는 절차서
    tdd/SKILL.md              ← /tdd    RED→GREEN→증거
    verify/SKILL.md           ← /verify 완료 기준 전수 점검
    release/SKILL.md          ← /release 배포
  .verify-stamp               ← 마지막 검증 성공 시점 (자동 생성, gitignore)

왜 이렇게 나눴나

  • CLAUDE.md 는 짧게. 매 세션 컨텍스트를 먹고, 길수록 지켜지지 않는다. 200줄 이내가 권장선이다.
  • 경로별 규칙은 rules/ 로. WPF 함정은 WPF 파일을 열 때만 필요하다. 항상 로드하면 낭비다.
  • 절차는 skills/ 로. 배포 순서 같은 건 배포할 때만 필요하다. 안 쓰면 토큰을 안 쓴다.
  • 강제는 훅으로. CLAUDE.md 는 부탁이고 훅은 강제다. "반드시 X 해라"가 지켜지길 원하면 훅에 넣는다.

훅 동작

SessionStart — 상태 브리핑

세션이 시작되면 브랜치·마지막 커밋·변경 파일·검증 상태·미완료 백로그를 컨텍스트에 넣는다. 에이전트가 git status 를 따로 묻지 않고 현재 상황을 알고 시작한다.

Stop — 테스트 게이트

턴을 끝내려 할 때:

  1. stop_hook_active 가 true 면 즉시 통과 — 무한 루프 방지. (Claude Code 는 Stop 훅이 8회 연속 차단하면 강제로 넘긴다. 가드가 없으면 그 8회를 다 태운다.)
  2. 소스(.cs/.csproj/.xaml/.axaml)의 최신 수정시각을 .claude/.verify-stamp 와 비교. 같으면 즉시 통과 — 대화만 한 턴은 지연 0.
  3. 바뀌었으면 dotnet test VideoDownloader.Tests 실행 (증분 약 13초).
  4. 통과 → 스탬프 갱신 후 종료 허용. 실패 → exit 2 로 종료 차단, 실패 출력 60줄을 에이전트에게 전달.

즉 테스트를 깨둔 채로 턴을 끝낼 수 없다.

사용법

에이전트 (자율 동작)

특별히 할 일이 없다. 세션을 열면 브리핑이 들어오고, 계약(AGENTS.md)이 로드되고, 파일을 열면 해당 영역 규칙이 붙고, 턴을 끝내면 테스트가 돈다.

기억할 것 세 가지:

  1. 기능 작업은 /tdd 로 시작한다. 절차를 기억으로 재구성하지 않는다.
  2. 마무리 전에 /verify 로 완료 기준을 전수 점검한다.
  3. Stop 훅이 막으면 우회하지 않는다. 테스트를 지우거나 Skip 처리하는 것은 회귀를 숨기는 것이다. 못 고치면 못 고친다고 보고한다.

사람

/hooks          # 등록된 훅 확인
/context        # 어떤 지침 파일이 실제 로드됐는지 확인
/memory         # CLAUDE.md 등 열어서 편집

게이트를 잠깐 끄고 싶을 때 — 실험 중이라 테스트가 깨진 게 정상인 상황:

// .claude/settings.local.json  (gitignore 대상, 개인용)
{ "disableAllHooks": true }

작업이 끝나면 지운다. 켜 두는 게 기본이다.

게이트가 느리다고 느껴지면 — 변경이 없으면 이미 스킵된다. 그래도 무거우면 verify-on-stop.sh 의 dotnet test 를 --filter 로 좁히는 대신, 전체 실행 유지를 권한다. 부분 검증은 회귀를 놓친다.

확장

새 경로 규칙 추가

.claude/rules/<이름>.md 에 프론트매터로 경로를 지정한다.

---
paths:
  - "VideoDownloader.Avalonia/**"
---
# Avalonia 영역 규칙
...

해당 경로 파일을 열 때만 컨텍스트에 들어온다. 30줄 안쪽으로 유지한다.

새 절차 스킬 추가

.claude/skills/<이름>/SKILL.md. description 이 트리거를 결정하므로 언제 쓰는지를 구체적으로 쓴다 — "코드 리뷰용"보다 "PR 올리기 전 변경 diff 를 검토할 때".

새 게이트 추가

무거운 검사(전체 빌드, 통합 테스트)는 Stop 에 붙인다. PostToolUse 에는 붙이지 않는다 — 편집할 때마다 돌면 개발이 멈춘다. .NET 빌드는 특히 비싸다.

원칙

  • 부탁은 CLAUDE.md, 강제는 훅. 반드시 일어나야 하는 일을 프롬프트에 적어두고 기대하지 않는다.
  • 게이트는 우회 대상이 아니라 계약이다. 막히면 원인을 고친다.
  • 완료 보고에는 실제 출력의 숫자를 인용한다. 기억으로 쓴 "테스트 통과"는 근거가 아니다.
  • 되돌릴 수 없는 일 앞에는 사람을 둔다. 커밋·푸시·배포는 자동화하지 않는다.

참고