MMax's Develop Story

팀 하네스 설계기 2편: 티켓 하나가 parent에 병합되기까지

Max··32분 읽기
Claude CodeHarness EngineeringTDDGit Worktree오케스트레이션회고

하네스를 설계할 때 가장 오래 붙잡고 있던 질문은 "어떻게 빨리 끝낼까"가 아니었다. "세션이 중간에 끊겼다가 사흘 뒤에 다시 열리면, 하네스는 어디서부터 이어야 하는지 스스로 알 수 있는가." 이 질문에 답하다 보니 구현 파이프라인보다 재개 판정 로직이 더 길어졌다.

1편에서는 kc-admin 하네스를 만들며 내린 결정들을 다뤘다. SSOT 문서 한 방향 원칙, [OPEN] 질문 게이트, 사람 게이트의 위치, 티켓 단위 워크트리 격리. 이번 편은 그 원칙들이 실제로 어떻게 도는지를 따라간다.

담당자가 외울 것은 슬래시 커맨드 두 개다. /ssot-creator와 /ssot-driven-dev. 예외 경로로 프론트에 API 계약을 먼저 넘기는 /ssot-driven-mock-document가 있고, 개발이 끝난 뒤의 이슈를 받는 /ssot-followup이 있다. 네 스킬 모두 같은 구조를 공유한다. 스킬을 호출한 메인 컨텍스트가 오케스트레이터가 되고, 무거운 일은 서브에이전트 노드에 위임한다.

1. 전체 흐름: 문서에서 parent 병합까지

① /ssot-creator KA-123 <컨플 URL> <피그마 URL>
   → 에픽 parent 브랜치·상주 워크트리 확보
   → 문서별 병렬 분석 → SSOT 초안 → 형식 검사 → rubric 채점(최대 2라운드)
   → [OPEN] 질문 댓글 미리보기 → 담당자 승인 → 스토리 티켓 댓글 게시

② 기획자 답변 → /ssot-creator KA-123 재호출 (갱신 모드)
   → 답변·문서변경·프론트 변경요청 회수 → 전 문서 재분석 → SSOT 병합 갱신
   → 담당자 "승인" → status: approved

③ /ssot-driven-dev KA-123
   → 작업 티켓 생성 → 의존성 DAG → 투입 승인 → 티켓별 워크트리 병렬 구현
   → 적대적 리뷰 → 커밋 → "개발완료" 전환 → parent 자동 병합

④ (사람) parent → dev MR → 개발 서버 테스트 → parent → live

하네스가 자동으로 하는 병합은 티켓 브랜치 → 에픽 parent 하나뿐이다. 그 위의 승격은 전부 사람 몫이다.

2. /ssot-creator: 문서 여러 개를 SSOT 하나로

/ssot-creator는 그래프로 정의돼 있다. 각 노드는 SKILL.md에 알파벳 한 글자로 이름이 붙어 있고, 오케스트레이터는 엣지 순서대로 실행한다.

단계하는 일실행 주체
게이트 체인인자·슬러그 규칙 확인, 티켓이 스토리 유형인지 검증, parent 브랜치를 live에서 확보오케스트레이터
회수 (갱신 모드만)티켓 댓글에서 [OPEN] 답변, 새 문서 링크, 프론트 [변경요청], 기획자 [문서변경] 회수오케스트레이터
문서 분석문서 1개당 분석 노드 1개를 한 메시지에 병렬 fan-outdoc-analyst × N
취합·초안분석 결과를 병합해 SSOT 초안 작성 또는 기존 SSOT 병합 갱신오케스트레이터
형식 검사템플릿 완비, 출처 표기, AC 형식, 작업 분해ssot-checker
rubric 채점원문과 대조해 7항목 채점, 미달이면 보완 후 재채점 (최대 2라운드)ssot-reviewer
댓글 승인 루프게시될 질문 미리보기 → 승인 또는 피드백 반영 반복담당자
Jira 연동스토리 본문 등록, 질문 게시, 변경 전파, 답글오케스트레이터
승인 처리status: approved, 작업 분해대로 하위 티켓 준비담당자 "승인" 후

두 가지가 눈여겨볼 만하다.

첫째, 형식 검사와 내용 채점을 분리했다. ssot-checker는 sonnet 모델로 원문을 읽지 않고 형식만 본다. 싸고 빠르다. ssot-reviewer는 opus 모델로 Confluence·Figma 원문을 직접 열어 대조한다. 비싸다. 형식 위반을 먼저 싼 노드로 걸러 내야 비싼 노드가 본질적인 결함에 집중한다.

둘째, rubric은 점수를 차단 항목으로 나눈다.

항목차단무엇을 보나
1. 완전성⛔원문의 모든 요구사항·정책·표·예외가 반영됐나
2. Figma 일치화면 필드·상태(빈/로딩/에러)·인터랙션
3. 엣지케이스단골 누락 항목이 요구사항이나 질문 어느 쪽에든 있나
4. AC 품질⛔Given/When/Then이고 테스트로 기계 검증 가능한가
5. 출처⛔모든 요구사항에 출처가 있나, 창작은 없나
6. 미확정 질문 품질문서에 없는 결정이 단정돼 있지 않나, 질문이 기획자 언어인가
7. 작업 분해한 세션 완료 단위인가, 담당 AC와 의존이 명시됐나

14점 만점에 13점 이상이면 승인 권고지만, 차단 항목 하나라도 0점이면 점수와 무관하게 승인 불가다. 완전성은 구현 전체의 근거이고, AC 품질은 테스트 매핑의 근거이고, 출처는 추측 금지 원칙의 기계 검증점이기 때문이다.

재미있는 지점은 보완 규칙이다. 리뷰어가 "원문에 없는 결정이 단정적으로 적혀 있다"고 지적하면, 오케스트레이터는 본문을 그럴듯하게 채우는 게 아니라 그 항목을 [OPEN] 질문으로 옮긴다. 그 편이 6번 점수를 올리는 정답이다. 2라운드 후에도 미달이면 진행을 막지 않고 잔여 지적을 보고에 넣는다. 남은 지적은 대개 기획자 답변을 기다려야 풀리는 것들이고, 그 회수 경로가 다음 단계에 있기 때문이다.

채점 결과는 세션이 아니라 SSOT 문서 9절에 한 줄로 남긴다.

- 2026-08-xx: rubric 채점 11/14 · 79점 (승인 불가: 차단 5.출처) - 잔여: Q7 답변 대기
- 2026-08-xx: rubric 채점 13/14 · 93점 (승인 권고) ↑ 前 79점

승인은 며칠 뒤에 올 수 있고, 갱신 모드는 회차마다 재호출된다. 추이가 파일에 있어야 "이번 갱신이 문서를 좋게 만들었는가"를 대조할 수 있다.

3. /ssot-driven-dev 스토리 그래프: DAG와 워크트리 병렬

승인된 스토리에 /ssot-driven-dev KA-123을 호출하면 스토리 그래프가 돈다.

스토리 그래프: DAG ready 계산 → 워크트리 병렬 구현 → 리뷰 → 직렬 병합

  1. parent 확보: 모든 게이트보다 먼저 에픽 parent 브랜치와 상주 워크트리를 확인한다. SSOT 문서가 그 브랜치에만 있기 때문이다.
  2. 게이트·티켓 확보: status: approved를 확인한다. 이 확인은 반드시 작업 티켓 생성보다 앞이다. 승인 전 SSOT가 Jira에 티켓을 만드는 순간 외부 부수효과가 생긴다.
  3. DAG·ready 계산: 작업 분해 표의 선행 의존으로 DAG를 만들고, 선행이 모두 완료된 티켓을 ready로 잡는다.
  4. 투입 승인 게이트: 투입할 티켓, 생성할 작업 티켓, 재개 대상과 근거, 워크트리 수, 완료 시 parent까지 자동 병합된다는 사실을 보여 주고 멈춘다.
  5. 작업 티켓 생성·역기입·즉시 커밋: 승인 직후 Jira 티켓을 만들고 그 ID를 SSOT 작업 분해 표에 적어 바로 커밋한다. 역기입이 워킹트리에만 남은 채 끊기면 다음 실행이 같은 티켓을 또 만든다.
  6. 워크트리 준비 → 구현 → 리뷰 → 병합: ready 티켓마다 반복하고, 티켓이 완료될 때마다 ready를 다시 계산해 새로 풀린 티켓을 즉시 투입한다.

이 그래프의 실행 규칙은 짧지만 하나하나가 실측에서 나왔다.

  • fan-out은 한 메시지에 모든 Agent 호출을 담는다. 하나씩 순차로 부르면 그래프 위반이다.
  • 티켓 간에 barrier를 두지 않는다. 티켓 A의 구현이 끝나면 다른 티켓을 기다리지 않고 바로 A의 리뷰로 간다.
  • 구현 노드는 백그라운드 Agent로 띄운다. 포그라운드로 fan-out하면 결과가 전부 끝난 뒤 한꺼번에 도착해 사실상 barrier가 된다. 승인 대기로 턴이 끝나도 다른 티켓의 구현은 계속 돈다.
  • 전체 테스트를 도는 gradle 빌드는 동시에 3개까지. 넘는 티켓은 슬롯이 빌 때 투입한다.
  • 병합은 직렬이다. parent ref와 상주 워크트리는 공유 자원이라 한 번에 한 티켓만 병합한다.

병렬은 구현과 리뷰에서만 허용하고, 공유 자원에 쓰는 순간에는 직렬로 좁힌다. 백엔드에서 수없이 본 패턴이 에이전트 오케스트레이션에서도 그대로 통한다.

4. 작업 그래프: 티켓 한 장의 TDD 파이프라인

스토리 그래프의 구현 노드는 implementer 서브에이전트가 맡고, 그 안에서는 작업 그래프가 돈다. 작업 티켓 하나를 직접 지목해 호출하면 오케스트레이터가 이 단계를 직접 수행한다.

단계내용
사전 점검 (T0)SSOT 존재·승인·슬러그 일치, 담당자의 미커밋 수동 수정 감지, [OPEN] 답변 회수, 세션이 하네스 루트에서 열렸는지, 티켓 워크트리·브랜치 일치
재개 스캔 (T1)변경분 AC 계산 → AC별 상태 분류 (5절)
TDD 루프 (T2)AC 단위 Red → Green → Refactor. 같은 워크트리라 AC 병렬화 금지
완료 검증 (T3)./gradlew build (전체 테스트 + checkstyle + 라인 커버리지 100%), OpenAPI 생성, AC ↔ 테스트 전수 매핑, 스키마·코드값 문서 일치
리뷰 루프 (T4)reviewer → fixer, 최대 2라운드
기록·커밋 (T5)리뷰 로그, 수동 테스트용 .http 생성, 커밋, 작업 이력 댓글, "개발완료" 전환, parent 병합

TDD의 Red는 반드시 @DisplayName("AC-n: ...")이 붙은 실패 테스트로 시작한다. 이 한 줄이 SSOT의 AC와 코드를 잇는 유일한 끈이다.

@Test
@DisplayName("AC-3: 제목 없이 공지를 등록하면 400을 반환한다")
void registerNotice_withoutTitle_returns400() throws Exception {
    // Given / When / Then 이 SSOT §6 의 AC-3 문안과 1:1
}

리뷰 루프에서 가장 공들인 부분은 리뷰 독립성이다. reviewer 프롬프트에는 SSOT 경로, 담당 AC 범위, diff 명령, 워크트리 경로만 넣는다. 구현 과정 설명이나 판단 근거는 절대 전달하지 않는다. 구현자의 변명을 들은 리뷰어는 이미 리뷰어가 아니다.

diff를 만드는 방식에도 함정이 두 개 있었다.

# ❌ 구현은 리뷰 통과 후에 커밋되므로 이 diff 는 비어 있다
git -C <워크트리> diff <parent>...HEAD
 
# ❌ parent tip 기준이면 티켓 진행 중 parent 에 쌓인 남의 커밋이 "되돌린 변경"으로 섞인다
git -C <워크트리> diff --cached <parent>
 
# ✅ 전부 스테이징한 뒤 파생 시점(merge-base) 기준, SHA 는 리터럴로 넘긴다
git -C <워크트리> add -A
git -C <워크트리> diff --cached 3f9c2e1

SHA를 $(git merge-base ...) 명령 치환으로 넘기지 않는 이유도 있다. 명령 치환이 들어간 명령은 권한 설정의 프리픽스 allow에 매칭되지 않아, 서브에이전트가 라운드마다 확인 프롬프트에 걸린다. 백그라운드 노드에서 프롬프트는 곧 정지다.

완료 시 남는 .http 파일은 개발자가 bootRun 후 IDE에서 바로 실행하는 수동 테스트 파일이다. 빌드 게이트 대상은 아니고, 컨트롤러 하나당 파일 하나, 요청 제목마다 어떤 AC를 검증하는지 적는다.

@baseUrl = http://localhost:8080
 
### ✅ 공지 등록 - 200 (AC-1)
POST {{baseUrl}}/api/notices
Content-Type: application/json
 
{
  "title": "점검 안내",
  "content": "8월 15일 새벽 점검이 있습니다."
}
 
### ⛔️ 공지 등록 - 400 제목 누락 (AC-3)
POST {{baseUrl}}/api/notices
Content-Type: application/json
 
{
  "title": "",
  "content": "내용"
}

요청 값을 지어내지 않는다는 원칙이 여기에도 적용된다. 값은 해당 컨트롤러 테스트가 쓴 본문을 그대로 옮기고, 메일 발송을 유발하는 요청에는 📧 실제 발송 표시를 붙여 개발자가 실행 전에 수신자를 바꿀지 판단하게 한다.

5. 기법 ①: 상태 파일 없는 재개

중단·재개를 위해 처음 떠올린 건 state.json 같은 진행 상태 파일이었다. 결국 만들지 않았다. 상태 파일은 코드와 따로 놀기 시작하는 순간 거짓말을 한다. 대신 이렇게 정했다.

AC ↔ 테스트 매핑이 곧 진행률이다.

재개 스캔은 담당 AC마다 @DisplayName("AC-n") 테스트의 존재 여부와 통과 여부를 워크트리의 현재 파일로 판정한다.

분류판정처리
완료테스트 있음 + 통과 + 변경분에 없음손대지 않는다
무효화테스트가 통과해도 변경분에 포함새 AC 문안에 맞게 테스트부터 고친다 (그게 Red)
진행 중테스트가 커밋돼 있으나 실패·미완성 (WIP 잔재)중단 지점부터 이어간다
미완료테스트 없음신규 구현
폐기SSOT에 폐기 표기, 코드는 남아 있음자동 삭제하지 않는다. 후보만 보고하고 정지

git diff는 보조 자료일 뿐이다. 이미 parent에 병합된 티켓을 재파생해 재개하면 diff가 비어 있다. diff를 1차 판정에 쓰면 전 AC가 미완료로 오판된다.

6. 기법 ②: 변경 감지는 커밋 시각이 아니라 댓글 타임라인으로

가장 까다로웠던 문제는 "문안만 바뀐 AC" 였다. 기획자 답변으로 AC-7의 문안이 바뀌어도, 예전 테스트는 그대로 통과한다. 위 분류만 쓰면 AC-7은 "완료"로 잡혀 영영 재작업되지 않는다.

그래서 재개 스캔 앞에 변경분 AC 집합 계산을 둔다. 신호는 두 개다.

  1. 작업 티켓 댓글의 전파 메시지: /ssot-creator 갱신 모드가 남기는 📐 SSOT 갱신 전파, /ssot-followup이 남기는 🐞 구현 결함 전파.
  2. SSOT 9절의 갱신 줄: - 2026-09-01: 갱신 - [ANSWERED] Q4 반영 (근거: 댓글 링크. 영향 AC: AC-12, AC-17) 같은 줄.

반영 여부는 전파 댓글 뒤에 그 티켓의 작업 이력 댓글이 있고, 거기 ↩️ 반영한 변경분 마커가 그 전파를 가리키는지로 판정한다. 커밋 시각은 쓰지 않는다. 재파생한 브랜치의 HEAD는 parent의 최신 커밋(남의 작업 포함)이라 "이 티켓이 마지막으로 구현된 시점"을 뜻하지 않는다.

두 신호는 합집합으로 쓰되, 어긋나면 SSOT 9절이 이긴다. 한쪽에만 있어도 변경분으로 본다. 놓치는 쪽이 불필요하게 재작업하는 쪽보다 나쁘기 때문이다.

Jira MCP가 연결되지 않아 댓글을 못 읽는 날도 대비했다. 리뷰 로그 최상단에 "반영 기준" 헤더를 남긴다.

## 반영 기준
- SSOT: docs/ssots/KA-123-.../ssot-KA-123-....md: §9 마지막 반영 줄: 갱신 2026-09-01
- 담당 AC: AC-1 ~ AC-8
- 이번 회차 처리: 완료 AC-1,2 / 재작업(무효화) AC-7 (📐 09-01 반영) / 신규 AC-8

커밋된 파일이라 Jira 없이도, 헤더의 마지막 반영 일자 뒤에 추가된 9절 갱신 줄만 보면 미반영 변경분이 나온다. 같은 정보를 Jira와 git에 이중으로 남기는 것이다.

7. 기법 ③: 멱등성과 "정지 전 커밋"

이 하네스는 하루에도 몇 번씩 멈춘다. 댓글 승인 대기, 투입 승인 대기, 폐기 AC 삭제 승인 대기. 멈춘 상태로 며칠이 지나기도 한다. 그래서 규칙 하나를 모든 스킬에 박았다.

휴먼 게이트에서 턴을 끝내기 전에, 그 시점까지의 산출물은 반드시 커밋돼 있어야 한다.

워킹트리에만 있는 [ANSWERED] 반영은 git stash나 checkout 한 번에 날아간다. 그리고 재개 판정의 폴백(git log -- <SSOT>)은 커밋 이력을 전제한다. 커밋 방식도 엄격하다.

# 스킬이 쓰지 않은 dirty 파일이 있으면 add 하지 않고 "담당자 미커밋 수정 발견" 보고
git -C <parent워크트리> status --porcelain -- docs/ssots/<슬러그>
 
# 이번 실행에서 쓴 파일만 명시 add, 디렉토리째 add 금지
git -C <parent워크트리> add docs/ssots/<슬러그>/ssot-<슬러그>.md docs/ssots/<슬러그>/run-log.md
 
# pathspec 한정 커밋: 담당자가 이미 스테이징해 둔 다른 경로가 휩쓸리지 않는다
git -C <parent워크트리> commit -m "📝 docs: <슬러그> SSOT 갱신 [ANSWERED] Q3 반영" -- docs/ssots/<슬러그>

디렉토리째 add하면 담당자가 손으로 고친 SSOT가 스킬 커밋으로 위장된다. 그러면 "최근 커밋이 스킬 커밋이 아니면 수동 수정으로 본다"는 감지 로직이 무력화된다. 커밋 메시지에 괄호·파이프·백틱을 넣지 않는 규칙도 있다. 훅이 명령 원문으로 pathspec을 판정하기 때문이다.

재진입 분기도 상태별로 갈라 놨다.

  • 병합만 남은 재진입: 완료 커밋·작업 이력 댓글·"개발완료" 전환까지 끝났는데 병합만 충돌로 보류된 경우. 구현·리뷰·댓글을 다시 하지 않고 병합만 재시도한다. 이때 빈 diff는 오류가 아니라 정상 신호다. 단, 완료 커밋 이후에 담당자의 충돌 해소 커밋이 있으면 그 코드는 게이트를 거치지 않았으므로 빌드와 리뷰 1라운드를 다시 돌린다.
  • BLOCKING 보류 재진입: 리뷰 2라운드를 소진하고 BLOCKING이 남아 멈춘 경우. 리뷰 로그에 라운드별 기록을 먼저 남기고 WIP 커밋한다. 재실행 시 같은 리뷰를 반복하지 않고, 담당자가 수용을 명시했을 때만 진행한다. 기록이 없으면 같은 BLOCKING과 같은 REJECTED가 매 회차 반복된다.
  • 완료 판정은 Jira 상태가 아니라 parent의 리뷰 로그 존재로: 담당자가 티켓을 "개발완료" 이후 단계로 옮겨도 재투입되지 않는다. git cat-file -e <parent>:docs/ssots/<슬러그>/review-log-<티켓>.md가 성공하면 완료다.

마지막으로 실행 로그(run-log.md)는 append-only다. 회차 종료 때 한 번 쓰던 것을 9월 3일부터 티켓이 parent에 병합될 때마다 중간 기록을 즉시 커밋하도록 바꿨다. 스토리 하나를 돌리던 중 한 티켓은 병합되고 다른 티켓이 진행 중일 때 회차가 끊기자, 이미 병합된 사실이 실행 로그에 남지 않는 구멍이 실측됐기 때문이다.

8. 기법 ④: 역방향 루프와 사후 관리

구현 중에 SSOT 결함을 발견하면 어떻게 할까. 규칙은 결함의 성격으로 나눈다. 국소(특정 AC만 영향)면 그 AC만 보류하고 나머지를 계속한다. 구조(도메인 모델 자체)면 전체를 멈춘다.

스토리 그래프에서는 역할 분담이 엄격하다. implementer는 SSOT를 고치지도, 커밋하지도, 댓글을 쓰지도 않는다. 보류 상태, 중단 지점, 기획자 언어로 쓴 [OPEN] 후보 문안을 반환만 한다. 오케스트레이터가 parent의 SSOT에 등록·커밋하고, 워크트리에 WIP 커밋을 남기고, 댓글 승인 루프를 돌린 뒤, 보류된 작업 티켓마다 ⏸ 구현 보류 교차 참조 댓글을 단다. 공유 자원에 쓰는 주체를 하나로 좁힌 것이다.

개발이 끝난 뒤의 이슈는 /ssot-followup이 받는다. 이 스킬의 핵심은 결정 트리 하나다.

1. 해결하면 사용자에게 보이는 동작(화면·응답 값·업무 규칙·권한)이 바뀌는가?
   ├─ 예 → 2
   └─ 아니오 → 4
2. SSOT §6 에 그 동작을 규정한 AC 가 있는가?
   ├─ 있고, 코드가 AC 를 위반한다     → A 구현 결함   (§9 결함 줄 + 🐞 전파 → /ssot-driven-dev 재작업)
   ├─ 있는데, AC 문안이 기획 의도와 다르다 → B SSOT 결함  (/ssot-creator 갱신 모드로 넘김)
   └─ 없다 → 3
3. 기획 원문에 근거가 있을 가능성이 있는가?
   ├─ 있다 → B SSOT 결함
   └─ 없다 → C 신규 요구 ([OPEN] 으로 기획자에게 질문)
4. 동작은 그대로 두고 성능·안정성·보안·구조만 바꾸는가?
   ├─ 예 → D 기술 이슈 (kind: technical SSOT 생성 → 게이트 정상 통과)
   └─ 아니오 → 분류 불가, 담당자 확인

A로 분류된 구현 결함이 특히 흥미롭다. SSOT는 옳으므로 6절을 고치지 않고, 9절에 결함 줄만 추가한다. 구현 스킬의 변경분 계산은 갱신과 결함 두 접두를 같은 변경 신호로 읽는다. 그러면 문안이 그대로인 AC가 무효화 대상이 되고, 전파 댓글의 재현 조건이 그대로 Red 테스트가 된다. 기존 테스트는 지우지 않는다. 통과하던 테스트가 AC를 온전히 검증하지 못했다는 뜻이므로 재현 케이스를 보강한다.

9. 시니어의 시선으로 정리하는 인사이트

① 재개 로직이 구현 로직보다 길어야 정상이다

/ssot-driven-dev SKILL.md 365줄 중 TDD 루프 자체는 열 몇 줄이다. 나머지 대부분은 사전 점검, 재개 판정, 재진입 분기, 병합 정리다. 에이전트 파이프라인의 난이도는 정상 경로가 아니라 중단 지점의 개수에 비례한다.

② 진행 상태는 산출물에서 계산하라

상태 파일을 따로 두지 않고 AC ↔ 테스트 매핑, parent의 리뷰 로그 존재, 9절 갱신 줄, 댓글 마커에서 상태를 계산했다. 상태를 저장하는 대신 상태를 다시 계산할 수 있는 증거를 남기는 것이 분산 환경에서 배운 원칙과 같다.

③ 병렬은 읽기와 계산에, 직렬은 공유 쓰기에

문서 분석, 구현, 리뷰는 병렬로 돌리고, parent 병합과 실행 로그 쓰기는 직렬로 좁혔다. SSOT 수정은 오케스트레이터만 한다. 쓰기 주체를 하나로 만드는 것이 에이전트 동시성의 가장 싼 해법이다.

④ 모든 신호는 두 군데에 남긴다

변경 신호는 Jira 댓글과 SSOT 9절에, 반영 기록은 작업 이력 댓글과 리뷰 로그 헤더에 이중으로 남긴다. 외부 시스템(Jira MCP)이 죽어도 git만으로 판정이 끝나야 하네스가 멈추지 않는다.

⑤ 놓치는 것과 과하게 하는 것 중 무엇이 더 나쁜지 미리 정해 둔다

변경분 판정에서 "한쪽 신호에만 있어도 재작업"으로 정한 것, 폐기 AC는 "자동 삭제 대신 보고"로 정한 것. 둘 다 오류의 비용이 비대칭이라는 판단에서 나왔다. 불확실할 때의 기본값을 문서에 적어 두면 에이전트가 그 자리에서 즉흥적으로 고르지 않는다.

마무리: 파이프라인은 끊기는 것을 전제로 설계한다

한 문장으로 줄이면 이렇다. 좋은 에이전트 파이프라인은 끝까지 한 번에 도는 파이프라인이 아니라, 어디서 끊겨도 다음 호출이 정확히 그 자리에서 이어 가는 파이프라인이다.

마지막 편에서는 이 흐름을 받치는 부품들을 하나씩 연다. 7종의 서브에이전트가 각각 어떤 도구와 모델을 받고 무엇을 금지당하는지, 스킬을 수동 호출 전용으로 잠근 이유, 그리고 765줄짜리 bash-guard.py를 포함한 훅 6종이 "규칙을 고쳐서 리뷰를 통과하는" 경로까지 어떻게 막는지다.