레거시 파일 수천 개를 옮기는 하네스 직접 쌓아 보기 — 감독자 패턴 실습기
리뷰 팀은 "동시에 보고 끝내는" 팀이었고, 구현 팀은 "Phase마다 팀을 갈아끼우는" 팀이었다. 세 번째로 만든 건 같은 작은 일이 수천 번 반복되는 일을 맡는 팀이다. 레거시 .jsx 파일들을 TypeScript로 옮기는 마이그레이션. 이번엔 감독자 패턴을 처음으로 직접 짜 봤다.
| 항목 |
내용 |
| 실습일 |
2026-11-01 |
| 실습 위치 |
~/Desktop/side/harness/legacy_migration |
| 만든 것 |
에이전트 4명 + 감독자 스킬 1개 + CLAUDE.md + 상태 파일 구조 |
| 직접 설계한 것 |
baseline-tester, migration-worker, migration-verifier, legacy-migration 스킬 |
| 가져다 쓴 것 |
dependency-mapper 정의, 샘플 레거시 코드 17개 |
| 진행 상황 |
하네스 구축·검사 완료, 본 실행 결과는 다음 글 |
1. 시작하며: 이번엔 일의 모양이 다르다
앞의 두 팀을 만들면서 "일의 모양이 패턴을 정한다"는 걸 배웠다. 그래서 이번에도 먼저 일의 모양부터 따져 봤다. 레거시 3,500개 파일을 바꾼다고 치면 성질이 네 가지다.
- 반복: 파일 하나 바꾸는 일 자체는 작고 비슷하다. 그게 수천 번.
- 순서:
UserCard → useAuth → api → http. 아래부터 바꿔야 위가 안 깨진다.
- 불균형: 같은 개수여도 어떤 묶음은 쉽고 어떤 묶음은 어렵다.
- 중단: 이 정도 양이면 중간에 반드시 끊긴다.
1-1. 처음 떠올린 두 방법이 왜 안 되는가
Claude 하나에게 다 시키기. 제일 먼저 떠오르지만 바로 접었다.
- 맥락이 넘쳐서 100번째와 3,000번째 파일의 변환 방식이 달라진다.
- 어디까지 했는지가 대화 속에만 있어서 끊기면 처음부터다.
- 한 파일이 깨져도 다음으로 넘어가 버린다.
리뷰 팀처럼 처음에 똑같이 나누기(고정 팬아웃). 리뷰 팀에서 잘 썼으니 이번에도 될까 싶었는데 아니었다.
- 워커 A가
UserCard를 바꾸는 동안 그게 쓰는 http는 워커 C가 아직 손도 안 댔을 수 있다. 순서가 찢어진다.
- 쉬운 묶음 받은 워커는 놀고, 어려운 묶음 받은 워커는 한참 걸린다.
- 이미 다 나눠 준 뒤라 실패한 일을 다시 나눌 수가 없다.
1-2. 그래서 감독자
정리하고 보니 필요한 건 대기열을 쥔 사람이었다. 주방장이 주문서를 들고 있다가, 요리사 손이 비면 지금 바로 만들 수 있는 주문 하나를 건넨다. 소스가 아직 안 끓었으면 그 소스가 들어가는 요리는 내주지 않는다. 일을 미는 게 아니라 워커가 가져가는 구조다.
| |
혼자 다 하기 |
처음에 나누기 |
감독자 |
| 분배 시점 |
없음 |
처음 한 번 |
워커가 빌 때마다 |
| 의존 순서 |
머릿속 |
찢어짐 |
대기열이 지킴 |
| 일의 불균형 |
— |
노는 워커 |
자동 흡수 |
| 실패 처리 |
묻힘 |
못 옮김 |
재배정, 상한 넘으면 사람 |
| 중단 후 재개 |
처음부터 |
어려움 |
상태 파일에서 이어서 |
2. 팀 설계: 4단계, 4명 + 감독자
예제 저장소를 열어 보니 이번 폴더엔 dependency-mapper 에이전트 하나와 샘플 코드 17개뿐이었다. README에 나오는 mock-mapper.sh, verify.sh도 없었다. 리뷰 팀 때 "저장소는 단계별 스냅샷"이라는 걸 한 번 겪어서 이번엔 덜 당황했다. 나머지 팀원은 직접 설계하기로 했다.
먼저 워크플로를 4단계로 잡았다.
| 단계 |
담당 |
하는 일 |
패턴 |
| 1. 의존 매핑 |
dependency-mapper |
import 그래프 → 배치와 순서 |
— |
| 2. 기준선 테스트 |
baseline-tester |
변환 전 동작을 테스트로 박제 |
— |
| 3. 점진 변환 |
감독자 + migration-worker 여럿 |
시작 가능한 배치를 하나씩 가져감 |
감독자 |
| 4. 검증 |
migration-verifier |
배치마다 PASS/FAIL 판정 |
생성-검증 |
구현 팀에서 배운 "패턴의 층"이 여기서도 그대로 보였다. 바깥 뼈대는 파이프라인(1→2→3·4), 안쪽 엔진은 감독자와 생성-검증. 다만 이번엔 3단계와 4단계가 배치 단위로 맞물려 계속 돈다.
[1. 의존 매핑] ──▶ [2. 기준선 테스트] ──▶ ┌──────── 3·4단계 루프 (배치 단위) ────────┐
import 그래프 변환 전 동작 박제 │ 감독자(메인 세션) ── 대기열(batches.json) │
→ 배치·순서 전부 통과 못 하면 정지 │ │ 시작 가능 배치 출고 (최대 3 동시) │
│ ▼ │
│ migration-worker ×N ──▶ migration-verifier│
│ ▲ FAIL(사유) │ PASS │
│ └── pending 복귀 ◀───┘ → verified │
│ 3회째 FAIL → 되돌림 + failed + 위층 차단 │
└──────────────────────────────────────────┘
감독자는 에이전트로 만들지 않았다. 구현 팀의 PM처럼 스킬을 읽은 메인 세션이 감독자다. 워커끼리 대화할 일도 없어서 에이전트 팀 모드도 안 켰다. 배치 사이의 정보는 "이미 변환된 파일의 타입을 읽는다"로 파일을 통해 오간다. 일반 서브에이전트로 충분하다.
변환 대상은 샘플이 .jsx라서 JavaScript → TypeScript로 정했다.
2-1. 권한 설계: tools 칸이 곧 직무 분리
| 담당 |
모델 |
tools |
일부러 뺀 것 |
| dependency-mapper |
(원본 유지) |
Read, Grep, Bash |
Write — 저장은 감독자가 |
| baseline-tester |
sonnet |
Read, Grep, Write, Bash |
Edit — src는 읽기만 |
| migration-worker |
sonnet |
Read, Grep, Write, Edit, Bash |
(본문에서 tests/·남의 배치·commit 금지) |
| migration-verifier |
sonnet |
Read, Grep, Bash |
Edit, Write — 판정만 |
표를 채우면서 의도한 건 세 가지다.
- 테스트를 쓰는 사람은 코드를 못 고친다 → 테스트 통과시키려고 코드를 바꾸는 일이 불가능.
- 판정하는 사람은 아무것도 못 고친다 → 보다가 편한 쪽을 슬쩍 고쳐 버리는 일이 불가능. 구현 팀 boundary-verifier에서 배운 그대로다.
- 아무도 커밋하지 않는다 → 커밋은 내가 한다.
2-2. 모델 설계: 이번엔 전원 sonnet
구현 팀 때 boundary-verifier는 opus였다. 두 파일을 동시에 놓고 "구현 탓이냐 설계 탓이냐"를 추론해야 했으니까. 이번 verifier는 다르다. tsc, vitest, git diff가 내놓는 결과로 판정이 거의 다 정해지고, 판정도 PASS/FAIL 둘뿐이다. 판단이 도구 출력에 있으면 모델은 한 단계 낮춰도 된다고 봤다. 워커도 규칙이 명확하고 배치가 작으니 sonnet. 반복 횟수가 많아서 비용 면에서도 이게 맞다.
3. 판 깔기
mkdir -p ~/Desktop/side/harness/legacy_migration
cd ~/Desktop/side/harness/legacy_migration
git clone --depth 1 https://github.com/revfactory/harness-engineering-with-cc.git /tmp/hewcc
cp -r /tmp/hewcc/ex-13-01-dependency-mapper/sample-src ./src
find src -type f | sort
샘플 17개를 훑어보다가 일부러 심어 둔 함정 두 개를 발견했다.
| 파일 |
함정 |
하네스가 해야 할 일 |
cyclic-a.jsx, cyclic-b.jsx |
서로를 import하는 순환 의존 |
한 배치로 묶고 cyclic 경고 |
truncated.jsx |
중간에 잘린 파일 (파싱 실패) |
배치에서 빼고 manual-queue로 |
나머지 15개로 의존 층을 직접 그려 봤다. 실행했을 때 맞는지 비교할 정답지다.
1층 (의존 없음) utils/http, utils/format, components/Footer, standalone
2층 utils/api
3층 hooks/useAuth, hooks/useFetch, components/Header
4층 context/SessionContext, components/UserCard
5층 components/Profile, components/UserList
6층 pages/Dashboard, pages/Settings
따로 cyclic-a + cyclic-b (한 배치, cyclic 경고)
제외 truncated (manual-queue)
되돌리기를 git으로 할 거라 레거시 상태를 먼저 커밋해 두고, TypeScript와 vitest를 깔았다.
git init
git add src
git -c user.name=practice -c user.email=practice@local commit -m "legacy baseline"
npm init -y
npm install -D typescript vitest
npm pkg set scripts.test="vitest run" scripts.typecheck="tsc --noEmit"
cat > tsconfig.json << 'EOF'
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "Bundler",
"jsx": "preserve",
"strict": true,
"allowJs": true,
"checkJs": false,
"noEmit": true,
"skipLibCheck": true
},
"include": ["src"]
}
EOF
allowJs: true가 중요했다. 마이그레이션 도중엔 .tsx와 .jsx가 섞여 있다. 이 상태에서도 타입 체크가 돌아야 배치마다 검증할 수 있다.
4. CLAUDE.md: 원칙 네 줄
cat > CLAUDE.md << 'EOF'
# legacy_migration — 레거시 마이그레이션 하네스
## 개요
- 진입점: legacy-migration 스킬 ("src를 TypeScript로 마이그레이션해 줘")
- 메인 세션이 감독자. 워커는 서브에이전트
- 상태 파일: _workspace/migration/batches.json (진행 상태의 유일한 기준)
## 워크플로
1. 의존 매핑 → 2. 기준선 테스트 → 3. 점진 변환 → 4. 배치별 검증
## 원칙
- 마이그레이션은 동작을 바꾸지 않는다. 버그도 그대로 옮긴다. 고치는 건 별도 작업
- 기준선 테스트는 변환 중 절대 수정하지 않는다
- 배치 실패가 상한을 넘으면 그 배치 파일을 git으로 되돌리고 manual-queue에 기록
- 누구도 git commit 하지 않는다. 커밋은 사람이 한다
EOF
"버그도 그대로 옮긴다"를 넣을지 한참 고민했다. 바꾸는 김에 고치면 좋지 않나? 그런데 그러면 테스트가 깨졌을 때 변환이 잘못된 건지 일부러 고친 건지 구분이 안 된다. 기준이 흔들리면 검증 자체가 의미가 없어진다. 그래서 고치는 건 별도 작업으로 뺐다.
5. 팀원 명부
5-1. dependency-mapper: 가져와서 배치 크기만 줄였다
mkdir -p .claude/agents
cp /tmp/hewcc/ex-13-01-dependency-mapper/.claude/agents/dependency-mapper.md .claude/agents/
sed -i 's/10~50/2~4/' .claude/agents/dependency-mapper.md
정의를 열어 보니 tools: Read, Grep, Bash로 Write가 없고, 복잡도 공식과 배치 필드 8개가 들어 있었다.
| 필드 |
의미 |
id |
배치 식별자 |
files |
배치에 속한 파일 |
depends_on |
먼저 verified 돼야 하는 배치 |
complexity |
복잡도 점수 (감독자의 우선순위) |
pattern |
변환 유형 |
status |
pending → in_progress → converted → verified | failed |
attempts / max_attempts |
시도 횟수 / 상한 |
배치 크기는 원래 10
50이다. 그런데 파일이 17개뿐이라 그대로 두면 배치가 한두 개밖에 안 나오고, 감독자가 일을 나눠 주는 모습을 볼 수가 없다. **분배가 눈에 보이도록 2
4로 줄였다.**
5-2. baseline-tester: "올바른 동작"이 아니라 "지금의 동작"
cat > .claude/agents/baseline-tester.md << 'EOF'
---
name: baseline-tester
description: 마이그레이션 전에 레거시 코드의 현재 동작을 테스트로 박제한다. 변환 전 코드에서 전부 통과해야 한다. 기준선, 박제, 회귀 테스트 키워드에서 사용.
model: sonnet
tools: Read, Grep, Write, Bash
---
## 역할
- src의 export마다 현재 동작을 그대로 기록하는 테스트를 쓴다.
- 기대값은 "올바른 동작"이 아니라 "지금의 동작"이다. 버그도 그대로 박제한다.
## 입력
- src/ 전체
- batches.json (manual-queue에 들어간 파일은 제외)
## 절차
1. 파일마다 export 목록을 뽑는다.
2. 외부 호출(fetch 등)은 vi.fn으로 막고, 무엇이 어떤 인자로 호출되는지까지 기록한다.
3. tests/baseline/에 테스트 작성. import 경로에 확장자를 쓰지 않는다 (.jsx → .tsx 후에도 그대로 동작하도록).
4. npm test 실행. 레거시 코드에서 전부 통과해야 한다.
## 출력
- tests/baseline/*.test.js
- 통과 결과 요약 (파일별 테스트 수)
## 에러 핸들링
- src 파일은 읽기만 한다. Edit이 없다.
- 실행하면 문제가 생기는 동작(예: 무한 재귀)은 "그 문제가 일어난다"를 기대값으로 박제한다.
- 레거시에서 통과하지 않는 테스트는 기준선이 아니다. 통과할 때까지 테스트를 고친다.
EOF
이 에이전트를 쓰면서 마이그레이션 검증의 핵심이 뭔지 정리됐다. "동작이 안 바뀌었다"를 확인하려면, 바꾸기 전 동작을 먼저 사진 찍어 둬야 한다. 순환 의존 파일이 스택을 터뜨리면 "스택이 터진다"가 정답이다.
작은 요령 하나: import 경로에 확장자를 안 쓰게 했다. 그래야 .jsx가 .tsx로 바뀌어도 테스트를 한 글자도 안 고쳐도 된다.
5-3. migration-worker: 유일하게 손을 가진 팀원
cat > .claude/agents/migration-worker.md << 'EOF'
---
name: migration-worker
description: 감독자에게 배치 하나를 받아 그 배치의 파일만 JavaScript에서 TypeScript로 변환한다. 여러 명이 동시에 일한다. 변환, 마이그레이션 워커 키워드에서 사용.
model: sonnet
tools: Read, Grep, Write, Edit, Bash
---
## 역할
- 받은 배치의 파일만 변환한다. 다른 배치 파일은 건드리지 않는다.
- 동작은 바꾸지 않는다. 버그도 그대로 옮긴다.
## 입력
- 배치 하나 (id, files, pattern)
- 재시도라면 이전 검증 실패 사유
## 변환 규칙
1. .jsx → .tsx (JSX가 없으면 .ts). git mv로 이름을 바꿔 이력을 남긴다.
2. 함수 인자와 반환값에 타입을 붙인다. 모르면 unknown으로 두고 any는 쓰지 않는다.
3. import 경로는 확장자 없이 유지한다.
4. 로직, 문자열, 호출 순서는 그대로 둔다.
## 절차
1. 배치 파일과, 그 파일이 import하는 이미 변환된 파일의 타입을 읽는다.
2. 변환 규칙대로 바꾼다.
3. npx tsc --noEmit으로 이 배치 파일에 오류가 없는지 확인한다.
4. 감독자에게 변환한 파일 목록과 특이사항을 돌려준다.
## 에러 핸들링
- tests/ 아래 파일은 절대 수정하지 않는다.
- 동작을 바꿔야만 타입이 맞는 경우 변환을 멈추고 감독자에게 사유를 보고한다.
- git commit 금지.
EOF
리뷰 팀 refactorer 때처럼, 손을 가진 팀원일수록 제약을 많이 걸었다. 특히 마지막 탈출구 — "동작을 바꿔야만 타입이 맞으면 멈추고 보고" — 를 꼭 넣고 싶었다. 억지로 타입을 맞추다 로직을 건드리는 게 마이그레이션에서 제일 조용히 틀리는 방식이라서다.
5-4. migration-verifier: 판정 순서를 고정했다
cat > .claude/agents/migration-verifier.md << 'EOF'
---
name: migration-verifier
description: 변환이 끝난 배치 하나를 검증해 PASS 또는 FAIL만 판정한다. 직접 고치지 않는다. 검증, 배치 검증 키워드에서 사용.
model: sonnet
tools: Read, Grep, Bash
---
## 역할
- 배치 하나가 변환 규칙을 지켰고 동작이 그대로인지 판정한다.
## 판정 순서 (반드시 이 순서로)
1. npm run typecheck — 이 배치 파일에서 타입 오류가 있으면 FAIL
2. npm test — 기준선 테스트 중 하나라도 실패하면 FAIL (동작이 바뀜)
3. git diff로 이 배치 외 파일이나 tests/가 바뀌었으면 FAIL (경계 침범)
4. any 사용, 로직 변경 흔적이 있으면 FAIL
5. 모두 통과하면 PASS
## 출력
- verdict: PASS | FAIL
- 실패 단계 번호, 파일:라인, 근거 (도구 출력 인용)
## 에러 핸들링
- Edit, Write가 없다. 직접 고치지 않는다.
- 도구 출력 없이 판정하지 않는다.
EOF
boundary-verifier를 고치면서 얻은 교훈 — 판정 에이전트에는 판정의 뜻이 아니라 순서를 준다 — 를 처음부터 적용했다. 그리고 boundary-verifier와 비교해 보니 차이가 분명했다.
| |
boundary-verifier (구현 팀) |
migration-verifier (이번) |
| 판정 |
PASS / FIX / REDO |
PASS / FAIL |
| 되돌아갈 곳 |
구현 또는 설계 |
워커뿐 (설계 문서가 없음) |
| 기준 |
설계 문서 두 개 |
기준선 테스트 하나 |
| 모델 |
opus |
sonnet |
REDO가 없는 이유는 간단하다. 되돌아갈 설계가 없다. 기준이 기준선 테스트 하나라 PASS/FAIL이면 충분하다. "도구 출력 없이 판정하지 않는다"는 리뷰 팀에서 haiku 담당이 근거 없이 지적하던 걸 보고 넣은 줄이다.
6. 감독자 스킬: 이번 실습의 핵심
mkdir -p .claude/skills/legacy-migration
cat > .claude/skills/legacy-migration/SKILL.md << 'EOF'
---
name: legacy-migration
description: 레거시 코드 대규모 마이그레이션을 감독자 패턴으로 진행한다. "마이그레이션해 줘", "TypeScript로 바꿔 줘" 요청에서 사용.
---
# 레거시 마이그레이션 감독자
메인 세션이 감독자다. 직접 변환하지 않는다. 대기열을 관리하고 일을 나눠 주고 판정을 받는다.
## 상태 파일
- _workspace/migration/batches.json — 진행 상태의 유일한 기준. 상태가 바뀔 때마다 즉시 저장한다.
- _workspace/migration/manual-queue.md — 사람이 봐야 할 파일과 사유
- 배치 status: pending → in_progress → converted → verified | failed
## 1단계: 의존 매핑
- dependency-mapper에게 target_glob "src/**/*.jsx"를 넘긴다.
- 돌려받은 batches.json 초안을 저장한다. max_attempts가 비어 있으면 3.
- 파싱 실패 파일은 manual-queue.md에 기록한다.
- cyclic 경고가 붙은 배치를 사용자에게 알린다.
## 2단계: 기준선 테스트
- baseline-tester를 부른다.
- npm test가 레거시 코드에서 전부 통과해야 3단계로 간다. 하나라도 실패하면 멈춘다.
## 3단계: 점진 변환 (감독자 루프)
반복한다, pending 배치가 없어질 때까지:
1. 시작 가능 배치 = status가 pending이고 depends_on의 모든 배치가 verified인 것
2. 비어 있는 워커 자리(최대 3)만큼 시작 가능 배치를 꺼낸다. complexity가 높은 것부터.
3. 꺼낸 배치를 in_progress로 바꾸고 저장한 뒤 migration-worker에게 하나씩 넘긴다.
4. 워커가 돌아오면 converted로 바꾸고 4단계 검증으로 보낸다.
5. 워커 자리가 비면 바로 1로 돌아가 다음 배치를 꺼낸다. 다른 워커를 기다리지 않는다.
## 4단계: 배치별 검증 (생성-검증 루프)
- migration-verifier에게 배치를 넘긴다.
- PASS → verified로 저장. 이 배치를 기다리던 배치들이 시작 가능해진다.
- FAIL → attempts + 1
- attempts < max_attempts: 실패 사유를 붙여 pending으로 되돌린다 (다시 대기열로)
- attempts = max_attempts: 그 배치 파일을 git checkout으로 되돌리고 failed로 저장, manual-queue.md에 기록.
이 배치에 의존하는 배치들도 failed로 막고 사유를 남긴다.
## 끝
- verified / failed / manual-queue 건수를 사용자에게 보고한다.
- 커밋은 하지 않는다.
EOF
쓰고 나서 다시 읽어 보니 감독자 패턴을 결정하는 건 결국 세 줄이었다.
- "다른 워커를 기다리지 않는다." 이 한 줄이 팬아웃과 감독자를 가른다. 팬아웃은 셋이 다 끝나야 다음 라운드, 감독자는 끝난 자리부터 바로 채운다. 이 줄이 빠지면 그냥 라운드제 팬아웃이 된다.
- 시작 조건은
converted가 아니라 verified. 처음엔 converted로 썼다가 고쳤다. 검수 안 된 기초 위에 2층을 올리면, 아래가 나중에 FAIL 났을 때 위까지 다 다시 해야 한다.
- FAIL은 재배정, 상한이면 되돌리고 위층까지 막는다. 반쯤 바뀐 파일을 남기지 않고, 깨진 기초 위에 쌓지 않는다.
complexity 높은 것부터 꺼내는 것도 의도가 있다. 오래 걸리는 일을 먼저 출발시켜야 마지막에 한 워커만 혼자 붙잡고 있는 꼬리가 짧아진다.
6-1. 상태 파일 하나로 끝까지
구현 팀 때까지는 진행 상태가 Phase 산출물에 흩어져 있었다. 이번엔 수천 번 반복이라 그러면 안 된다. 모든 진행 상태를 batches.json 하나에 두고, 바뀔 때마다 즉시 저장하게 했다. 세션이 끊기면 새 세션이 이 파일만 읽고 이어 가면 되고, 나도 이 파일 하나만 열면 진행률을 안다.
pending ──▶ in_progress ──▶ converted ──▶ verified
▲
└──── FAIL (3회 미만, 실패 사유 첨부) ───────┘
FAIL 3회째 ──▶ failed (되돌림 + manual-queue + 위층 차단)
7. 결재함 만들고 검사하기
mkdir -p _workspace/migration tests/baseline
echo "_workspace/" >> .gitignore
echo "node_modules/" >> .gitignore
find CLAUDE.md .claude tsconfig.json -type f | sort
ls package.json tsconfig.json
claude plugin validate .claude/agents
여기서 하나 당황한 게 있었다. 에이전트가 제대로 등록됐는지 보려고 /agents를 쳤는데 안 먹었다. 이 버전에선 /agents 마법사가 빠졌다. /list-agents는 에이전트가 아니라 세션 목록이었다. 결국 이렇게 확인했다.
- 세션에서
@mig, @dep, @base를 치면 자동완성에 (agent) 표시가 붙은 항목 4개가 뜨는지
- 파일 형식은
claude plugin validate .claude/agents로
그리고 리뷰 팀 때 데인 기억이 있어서 이번엔 처음부터 지켰다. 정의 파일이 있는 legacy_migration 폴더에서 새 세션을 연다. 세션 중에 폴더를 옮겼다가 에이전트가 정식 로드되지 않아 tools 제한이 무시됐던 사건 — verifier의 "Edit 없음"이 그냥 부탁이 돼 버리면 이 하네스는 의미가 없다.
cd ~/Desktop/side/harness/legacy_migration
claude
처음 돌릴 땐 shift+tab으로 auto mode를 꺼서 단계마다 눈으로 확인하고, 진행 중엔 /tasks로 워커와 모델을 볼 생각이다.
src를 TypeScript로 마이그레이션해 줘
8. 돌리기 전에 짚어 본 것들
설계를 마치고 다시 훑어보니 걸리는 데가 몇 군데 있었다. 실행하면서 실제로 터지는지 같이 보려고 적어 둔다.
| # |
걸리는 점 |
왜 |
대응 후보 |
| 1 |
병렬 워커 간 오판정 |
워커 3명이 같은 작업 트리에서 일한다. verifier의 git diff에 다른 워커의 변경이 섞이면 "경계 침범" FAIL이 잘못 나올 수 있다. npm test도 남의 반쯤 바뀐 파일 때문에 깨질 수 있다 |
diff를 git diff -- <배치 파일>로 한정, 또는 검증만 직렬화 |
| 2 |
truncated.jsx와 타입 체크 |
allowJs: true라 tsc가 잘린 JS도 파싱한다. 워커가 돌리는 npx tsc --noEmit이 매번 구문 오류를 볼 수 있다 |
manual-queue 파일을 tsconfig.exclude에 추가 |
| 3 |
git mv 뒤 되돌리기 |
이름을 바꾼 뒤 git checkout만으로는 원래 .jsx가 안 돌아오고 .tsx가 남을 수 있다 |
원래 경로 restore + 새 파일 삭제를 절차로 명시 |
| 4 |
컴포넌트 테스트 의존성 |
깐 건 typescript, vitest뿐. 컴포넌트를 렌더링하는 기준선이라면 react 등이 더 필요할 수 있다 |
샘플 import 보고 사전 설치 |
| 5 |
차단 범위 |
"의존 배치도 failed"가 바로 위층만인지 그 위 전부인지 애매하다 |
그래프 상위 전체를 막는다고 명시 |
9. 실행하면 확인할 것
| 단계 |
확인 항목 |
결과 |
| 1. 의존 매핑 |
내가 그린 층 구조와 맞는가 / 순환 두 파일이 한 배치 + 경고 / truncated → manual-queue |
☐ |
| 2. 기준선 |
레거시에서 전부 통과 / 순환 파일의 "스택 터짐"이 박제되는가 |
☐ |
| 3. 점진 변환 |
1층부터 나가는가 / 한 워커가 끝나자마자 다음 배치가 나가는가 / 남의 배치 파일을 안 건드리는가 |
☐ |
| 4. 검증 |
FAIL이 사유와 함께 대기열로 돌아가는가 / 3회째면 되돌려지고 위층이 막히는가 |
☐ |
| 전체 |
batches.json만 보고 진행 상황을 알 수 있는가 |
☐ |
| 8장 |
걸리는 점 1~5 중 실제로 터진 것 |
☐ |
10. 세 팀을 나란히 놓고 보니
| |
리뷰 팀 |
구현 팀 |
마이그레이션 팀 |
| 일의 모양 |
한 번 보고 끝 |
단계마다 다른 일 |
같은 일 수천 번 |
| 주 패턴 |
팬아웃 & 팬인 |
Phase별 조합 |
감독자 + 생성-검증 |
| 분배 |
처음에 한 번 |
Phase마다 팀 교체 |
워커가 빌 때마다 |
| 진행 상태 |
보고서 |
Phase 산출물 |
상태 파일 하나 |
| 검증 기준 |
정의 파일의 기준 |
설계 문서 |
기준선 테스트 |
| 워커 간 대화 |
없음 |
필요 (팀 모드) |
없음 (서브에이전트) |
| verifier 모델 |
— |
opus |
sonnet |
리뷰 팀이 한 장면을 잘 찍는 법, 구현 팀이 장면을 이어 붙여 영화를 만드는 법이었다면, 이번 팀은 같은 장면을 수천 번 찍어도 흔들리지 않게 촬영장을 운영하는 법이었다.
11. 정리
- 일의 모양이 패턴을 정한다. 반복·순서·불균형·중단, 이 네 가지가 감독자를 불렀다.
- 감독자의 본질은 대기열 + 당겨 가기 + 상태 파일이다. 처음에 나누지 않고, 빌 때마다 시작 가능한 일을 준다. "다른 워커를 기다리지 않는다" 한 줄이 팬아웃과의 차이다.
- 합격 기준을 기계가 판정할 수 있게 만든다. 지금의 동작을 버그까지 기준선 테스트로 박제하니 verifier가 sonnet + PASS/FAIL로 충분해졌다.
- 검수 끝난 것 위에만 쌓는다. 시작 조건은
verified.
- 실패는 멈추고 기록하고 사람에게. 상한 → 되돌림 → 위층 차단 → manual-queue.
- 앞에서 배운 건 그대로 재사용된다. 같은 뼈대의 정의 파일,
tools로 권한 분리, 판정 순서 명시, 커밋은 사람, 정의 파일 위치에서 새 세션.
다음엔 실제로 돌려서 batches.json과 manual-queue.md를 들고 9장 표를 채우고, 8장에서 걸렸던 점들이 실제로 터지는지 확인할 생각이다. 일부러 FAIL을 유도해서 3회 차단까지 가는지도 볼 예정이다.
참고