서로 다른 agent가 같은 나무를 차지하려 하지 않도록
「Isolate by directory, coordinate by task ID.」두 개의 평면이 서로 간섭 없이 운영됩니다.
병렬 agent의 한 가지 난제
s09-s11에서 여러 teammate를 동시에 실행할 수 있게 되었지만, 숨겨진 함정이 있습니다: 모두 동일한 작업 디렉토리를 사용합니다. alice가 auth.py를 수정하는 동시에 bob도 auth.py를 수정하면—git 충돌, 파일 혼란, 테스트 간 오염이 발생합니다.
s12의 해법은 git worktree입니다: 동일한 저장소에서 git worktree add를 통해 서로 다른 디렉토리에 서로 다른 브랜치를 체크아웃할 수 있습니다. 각 agent는 자신만의 worktree를 갖고 자신의 디렉토리에서 작업하므로 서로 간섭하지 않습니다.
my-repo/ # 주 작업 공간(lead 사용) .worktrees/ alice-auth/ # alice의 격리 디렉토리 (branch: wt/alice-auth) bob-api/ # bob의 격리 디렉토리 (branch: wt/bob-api) index.json # worktree 레지스트리 events.jsonl # 생명주기 이벤트 로그
두 평면의 역할 분담
s12는 시스템을 명시적으로 두 개의 평면으로 분리합니다:
- 제어면 (control plane):
.tasks/의 JSON 파일. task는 "무엇을 할 것인가"의 추상 단위로, 의존성·owner·status를 가집니다. - 실행면 (execution plane):
.worktrees/의 디렉토리. worktree는 "어디서 할 것인가"의 물리적 컨테이너로, 하나의 디렉토리 + 하나의 브랜치로 구성됩니다.
두 평면은 task.worktree와 worktree.task_id로 양방향 바인딩됩니다.
이런 분리 덕분에: task 관리 메커니즘을 재사용하면서 실행 환경만 교체할 수 있습니다(예: git worktree 대신 Docker 컨테이너). 또는 task 메커니즘만 교체하면서 worktree를 재사용할 수도 있습니다(예: JSON 파일 대신 데이터베이스).
생명주기 데모
아래에서 전체 흐름을 실행해 봅니다: task 생성 → worktree 생성 → 명령 실행 → keep 또는 remove. 각 단계는 이벤트 로그(events.jsonl)에 기록됩니다—프로덕션에서는 이를 기반으로 가시성을 확보합니다.
keep vs remove
worktree가 임무를 마쳤을 때 두 가지 종료 방식이 있습니다:
remove:git worktree remove --force로 디렉토리를 삭제합니다. 브랜치도 일반적으로 정리됩니다. "이 실험은 필요 없다"는 뜻입니다.keep:.worktrees/index.json에status: kept만 표시하고, 물리적 디렉토리는 보존합니다. "이 결과는 사용할 것이니 나중에 merge 대기"라는 뜻입니다.
일반적인 패턴: subagent가 실험적 리팩터링을 실행 → 결과가 좋으면 → keep → 사람이 리뷰 → 메인 브랜치에 merge → 수동으로 remove. 결과가 나쁘면? 바로 remove로 버립니다.