diff --git a/docs/design/worktree-creator-model.md b/docs/design/worktree-creator-model.md new file mode 100644 index 0000000..aaf4749 --- /dev/null +++ b/docs/design/worktree-creator-model.md @@ -0,0 +1,86 @@ +# 워크트리 운영 모델 제안 — creator 만 워크트리, 리뷰어는 공유 (Design Proposal) + +- 상태: 제안 (리뷰 요청) +- 작성: 오케스트레이터 세션 `dbe6dd10-a210-4b46-b613-58a1860499b6`, 2026-09-14 +- 배경 이슈: #17 항목 4(셸 계층 축소)·#18 사후 분석(체크아웃 전환 사고) + +## 1. 문제 + +MAM 은 **워크스페이스 = `.agents/skills` 가 들어 있는 체크아웃 경로** 라는 가정 위에 서 있다. + +| 경로에서 파생되는 것 | 근거 | +|---|---| +| `WORKSPACE_ROOT` | `lib.sh:26` — `${WORKSPACE_ROOT:-$SKILL_DIR/../..}` | +| `.mam/` (agent-sessions.yaml, jobs 레지스트리, 감사 로그, monitor.lock) | `$WORKSPACE_ROOT/.mam` (gitignore 대상) | +| herdr 서버 이름 | `resolve_herdr_session` — 워크스페이스 경로 슬러그, `--herdr-session` 으로 override | +| 모니터 토픽 지문 | `reconcile.sh:288-290` — `sha256(abspath(workspace_root))[:12]` | +| claude 대화 저장 키 | `~/.claude/projects/` — resume 는 같은 디렉터리여야 함 | + +그 결과 두 가지 문제가 있다. + +1. **동시 이슈 작업이 불가능하다.** 팀 리더 에이전트 5개와 오케스트레이터가 하나의 체크아웃을 공유하므로 브랜치는 한 번에 하나만 체크아웃된다. 이슈 A 의 리뷰 중 이슈 B 의 구현을 시작할 수 없다. +2. **체크아웃 전환 사고가 난다.** #18 작업 중 브랜치 생성 1분 뒤 HEAD 가 `main` 으로 되돌아가(reflog `moving from fix/issue-18 to main`, 18:32) 이후 12개 커밋이 `main` 에 직접 쌓였다. 병합 커밋 없이 push 되어 이력을 되돌리지 않았다. 공유 체크아웃에서는 어떤 프로세스든 `checkout` 하나로 다른 모든 참여자의 커밋 위치를 바꿀 수 있다. + +## 2. 후보안 + +### A. 모두 워크트리별 (오케스트레이터 + creator + 리뷰어) +- 장점: 완전 격리. 현재 코드가 그대로 동작한다(워크트리 = 새 워크스페이스). +- 단점: 이슈마다 에이전트 5개를 새로 띄운다 → 토큰·요청 한도가 이슈 수에 비례(codex 는 이번 세션에서 이미 한도 도달). 리뷰어 대화 문맥이 이슈마다 초기화된다. `.mam`·모니터·토픽 지문이 이슈마다 흩어져 관측이 분산된다. + +### B. 오케스트레이터 커밋만 워크트리, 에이전트는 본 체크아웃 +- 장점: 오케스트레이터 커밋은 격리된다. +- 단점: **병렬성이 없다.** 에이전트는 대화 하나짜리 프로세스라 잡을 하나씩만 처리하고, 두 루프가 같은 creator·리뷰어를 두고 `session_busy`(rc 5) 로 경합해 직렬화된다. creator 는 여전히 본 체크아웃에서 브랜치를 옮겨 커밋해야 하므로 #18 사고 유형이 그대로 남는다. 리뷰어가 검증하는 트리가 브랜치 트리가 아니다. + +### C. (제안) creator 만 워크트리, 리뷰어 풀은 공유, 제어 평면은 하나 +- **creator = 워크트리 전용 인스턴스.** 편집·커밋하는 유일한 에이전트이므로 cwd 가 곧 이슈 브랜치여야 한다. 이슈마다 `creator--` 로 만들고 끝나면 정지(대화 보존, 필요 시 재개). +- **리뷰어 풀 = 본 체크아웃 공유.** 읽기 전용. 브리프가 워크트리 경로·base·head 를 명시하고 리뷰어는 그 경로에서 `git diff ...` 와 테스트를 실행한다. 리뷰가 병목이면 리뷰어 인스턴스를 추가한다. +- **오케스트레이터 커밋 = 워크트리.** 본 체크아웃은 리뷰어의 작업 공간이므로 오케스트레이터는 그곳에서 브랜치를 바꾸지 않는다. +- **제어 평면 = 본 체크아웃의 `.mam` 하나.** 워크트리에서 creator 를 만들 때 `WORKSPACE_ROOT`·`AGENT_SESSIONS_YAML`·`MAM_JOBS_DIR`·`DELEGATE_JOB_LOGS_DIR` 를 본 체크아웃으로 넘기면 같은 yaml 에 등록되고(행의 `pane.cwd` 는 워크트리), 잡 레지스트리·토픽 지문·모니터가 한 곳에 모인다. + +## 3. C 안의 동작 방식 (현재 코드 기준) + +```bash +MAIN=/path/to/multi-agent-mux # 본 체크아웃: 리뷰어 풀 + .mam +WT=/path/to/multi-agent-mux-wt/issue-20 # 이슈 워크트리 +git -C "$MAIN" worktree add -b fix/issue-20 "$WT" main +ln -s "$MAIN/.venv" "$WT/.venv"; git -C "$WT" submodule update --init # 워크트리 준비(.venv, nats-docker) + +# creator 를 워크트리 cwd 로, 그러나 본 체크아웃의 .mam 에 등록 +WORKSPACE_ROOT="$MAIN" AGENT_SESSIONS_YAML="$MAIN/.mam/agent-sessions.yaml" \ +MAM_JOBS_DIR="$MAIN/.mam/jobs" DELEGATE_JOB_LOGS_DIR="$MAIN/.mam/delegate_job_logs" \ + bash "$MAIN/.agents/skills/multi-agent-mux-create/scripts/create_session.sh" \ + --workspace "$WT" --agent claude --role Creator --session creator-claude-issue20 \ + --herdr-session multi-agent-mux --herdr-workspace mam-agent + +# 루프는 본 체크아웃에서, creator 이름만 워크트리용으로 +cd "$MAIN" && bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \ + --creator creator-claude-issue20 --all-reviewer \ + --task "[이슈 #20 @ $WT, branch fix/issue-20, base ] ... 리뷰어는 $WT 에서 git diff ...HEAD 와 pytest 를 실행하라 ..." +``` + +- 브리프 규약: 첫 줄에 `워크트리 경로 / 브랜치 / base sha` 를 적고, 리뷰어에게 "그 경로에서 검증"을 명시한다. 잡 브리프 파일(`.mam/jobs//brief.md`)은 본 체크아웃에 있으므로 creator 는 `$MAIN/.mam/jobs//brief.md` 를 읽는다(주입 문구의 상대경로 `.mam/jobs/...` 는 creator cwd 기준으로는 존재하지 않는다 → **주입 문구를 절대경로로 바꾸는 작은 변경이 필요**). +- 루프 러너의 `base_commit`/diff 수집(`runner.py:399-401`)은 `workspace_root`(본 체크아웃) 기준이다. C 안에서는 워크트리 경로를 기준으로 diff 를 모아야 하므로 **`--worktree ` 옵션(또는 `MAM_LOOP_WORKTREE` env)이 필요**하다. +- 오케스트레이터 커밋 규칙: 커밋 전 `git branch --show-current` 확인(메모리 규칙), 본 체크아웃에서는 `main` 유지. + +## 4. 기대 효과와 한계 + +- 병렬화 이득은 **구현 단계**에서 크다: 워크트리마다 creator 가 동시에 작업하고, 전체 스위트도 워크트리마다 동시에 돈다. +- **리뷰 단계는 풀 크기만큼만 병렬**이다. 이슈 3개를 동시에 돌려도 리뷰어 하나는 잡을 순서대로 처리한다(래퍼는 busy 세션에 rc 5 → 러너가 `MAM_SESSION_BUSY_RETRY_S` 뒤 1회 재시도 → 그래도 busy 면 실패). 다중 루프 동시 기동에는 **리뷰어 큐잉 또는 리뷰어 인스턴스 증설**이 전제다. +- 루프 SKILL.md 의 "동시 루프 기동 금지(NFS lock shadowing)" 는 **같은 `.mam` 을 쓰는 루프**에 대한 경고다. C 안은 `.mam` 을 공유하므로, 다중 루프 동시 기동 전에 이 제약(체크포인트 파일·SQLite 갱신 경합)을 해소해야 한다. +- 워크트리 준비 비용(.venv 링크, 서브모듈, `.mam.env` 복사)은 스크립트 한 줄로 흡수 가능하다. + +## 5. 필요한 변경 (작업 항목) + +1. 주입 문구·브리프의 잡 경로를 절대경로로(`$REGISTRY_DIR//brief.md`). +2. 루프 러너에 `--worktree ` (diff 수집·`base_commit`·리뷰 브리프의 경로 자동 삽입). +3. `create_session.sh` 문서에 "제어 평면 override" 레시피 추가(`WORKSPACE_ROOT`/`AGENT_SESSIONS_YAML`/`MAM_JOBS_DIR`/`DELEGATE_JOB_LOGS_DIR`), 워크트리 준비 헬퍼(`scripts/worktree_prepare.sh`). +4. 다중 루프 동시 기동 시 `.mam` 경합 해소(체크포인트 파일 이름에 루프 id 포함, 리뷰어 busy 시 대기 큐). +5. 리뷰어 브리프 템플릿에 워크트리 경로·base·head 필드 고정. + +## 6. 리뷰어에게 묻는 것 + +1. C 안의 역할 분리(쓰는 쪽만 격리, 읽는 쪽 공유)가 두 문제(병렬성, 체크아웃 전환 사고)를 실제로 해소하는가? 놓친 경로가 있는가(예: 리뷰어가 테스트 중 파일을 만드는 경우, 모니터 reconcile 이 워크트리 cwd 행을 어떻게 다루는지). +2. 제어 평면 공유(env override)가 현재 코드에서 안전한가? 특히 `pane.cwd` 가 워크트리인 행을 status/monitor/stop/resume 스킬이 올바르게 처리하는가. +3. A/B 안 대비 C 안의 비용·이득 평가에 동의하는가? 다른 대안이 있는가? +4. §5 작업 항목의 누락·과잉. 특히 다중 루프 동시 기동의 `.mam` 경합을 먼저 풀어야 하는지, 아니면 "루프는 하나씩, 구현만 병렬" 로 시작해도 되는지. +5. #17 항목 4(셸 계층 축소)와의 순서: 이 모델을 먼저 문서·레시피로 굳히고 항목 4 에서 코드로 흡수하는 순서가 맞는가.