Files
multi-agent-mux/docs/design/reviews/review-claude-e89f62d5.md
T
GodopuandClaude Fable 5.1 dcf2fab06e docs(design): archive the four design reviews of the worktree operating model
claude, agy, grok: PASS with amendments; codex: NOT PASS (recipe overstates
current env-override support; review identity and lifecycle ownership
underspecified). Direction (creator per worktree, shared reviewer pool)
agreed by all four.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
2026-09-14 11:14:07 +09:00

17 KiB

🔍 Design Review — Job e89f62d5 (워크트리 운영 모델 제안, design/worktree-model @ fe96db1)

  • Reviewer: planner-reviewer-claude-01 (role: planner,reviewer) · 2026-09-14
  • 대상: docs/design/worktree-creator-model.md(워크트리 multi-agent-mux-wt/design-worktree-model, 커밋 fe96db1, 1파일 +86).
    • 본 체크아웃은 이 잡 내내 main @ 5611859(v4.5.0 코드)에 있다.
    • 아래의 코드 근거(파일:라인)는 모두 이 main 기준이다.
  • 방법:
    • 문서를 정독했다.
    • 문서가 기대는 코드 경로를 create·status·monitor·stop·resume·delegate-job·loop 러너 순으로 추적했다.
    • scratchpad 임시 git 저장소에서 실험 1건을 했다(gitignore 와 symlink).
    • 문서·코드는 수정하지 않았다. 체크아웃 전환, 워크트리 조작, 실제 .mam·세션 조작도 없었다.

0. 요약

항목 판정 핵심
§6-Q1 두 문제 해소 보완 커밋 격리는 해소된다. 병렬성은 "루프 밖 creator 병렬 + 리뷰 루프 1개" 로만 가능하다(루프 lock 이 두 번째 루프를 거부). 놓친 경로 5건(§2)
§6-Q2 / 브리프 (1) 제어 평면 env override 보완 status·monitor·stop 은 행의 pane.cwd 를 존중해 안전하다. 그러나 resume 은 --workspace $WT 가 필수이고, 모니터 데몬이 워크트리 코드로 뜰 수 있다
브리프 (2) 주입 문구 상대경로 동의 + 범위 확대 brief 경로뿐 아니라 --registry-dir .mam/jobs 와 Output Report Path 도 상대경로다. 이대로면 creator 의 started 가 실패한다(rc 1 agent_blocked). §5-1 은 첫 사용 전 필수다
브리프 (3) 러너 diff·base_commit 동의 + 구체화 runner.py:397-411, :564-565 는 모두 workspace_root 기준이다. --worktree 는 diff·base 만 바꾸고 wrapper·레지스트리·체크포인트는 본 체크아웃에 두어야 한다
브리프 (4) .mam 경합·§5-4 보완 문서의 "경고" 가 아니라 거부다(run_loop.sh:189-203). 1단계는 §5-4 없이 시작할 수 있다
브리프 (5) 놓친 실패 경로 보완 .venv symlink 가 무시되지 않음(실험), .pytest_cache 미무시, 리뷰어가 creator 의 살아 있는 트리에서 검증, 워크트리 모니터 데몬, 워크트리 삭제 뒤 resume 불가
§6-Q3 / 브리프 (6) A/B/C 평가 동의 + 대안 2건 C 가 맞다. 추가로 ① 본 체크아웃 커밋 가드, ② 리뷰마다 detached 워크트리
§6-Q4 §5 누락·과잉 보완 §5-1·§5-2·§5-3 범위를 넓히고 3건을 추가한다. §5-4 는 보류해도 된다
§6-Q5 / 브리프 (7) #17 항목 4 순서 조건부 동의 레시피·문서 먼저. 단 §5-1·§5-2 두 코드 변경은 지금 한다

결론: 근본 결함은 없고 C 안은 채택 가능하다. 다만 §7 의 문서 수정과 §5 보강이 첫 적용 전에 필요하다.

1. 브리프 (1) · §6-Q2 — 제어 평면 env override 가 스킬마다 존중되는가

구성요소 override 존중 워크트리 pane.cwd 행 처리 근거
lib.sh WORKSPACE_ROOT(:26)와 AGENT_SESSIONS_YAML(:34)을 env 우선으로 쓴다 — lib.sh:26, :34
create yaml·레지스트리 ✅(create_session.sh:353, :372-386) pane cwd = --workspace(:219, :223, :229)이고 행에 기록된다(:309, :428, :494). herdr 서버는 --herdr-session 을 주지 않으면 워크트리 슬러그가 된다(:163-172) 아래 ⚠️1
status yaml ✅(status.sh:22) claude 대화 키를 행의 cwd 로 계산한다 ✅(:59-76) 안전
monitor (reconcile) yaml·WORKSPACE_ROOT ✅ herdr 서버를 행마다 모아 조회한다(reconcile.sh:491-500). 다른 서버의 워크트리 행도 관측되므로 거짓 drift A 가 없다. drift B 자동 등록은 본 체크아웃 아래 pane 만 한다(:651-653). C0 검증은 행 cwd 를 쓴다(:727-730, verify_session.py:90-93) 안전
stop yaml ✅, purge 마커는 본 체크아웃 .mam(stop_session.sh:103) uuid 캡처·purge 가 행 cwd 를 쓴다(:133-143, :189). herdr 소켓은 행의 herdr_session 을 먼저 쓴다(lib.sh:1484-1490) 안전
resume yaml ✅ --workspace 가 필수다(resume_session.sh:42). 행을 cwd == --workspace 로 찾는다(workspace_uuid.py:58). claude 키는 워크트리 경로다(verify_session.py:75-81) ⚠️2
delegate-job 래퍼 .mam.env 는 REPO_ROOT(= 본 체크아웃, :20-24) ✅. MAM_JOBS_DIR 은 참조하지 않는다. REGISTRY_DIR_DEFAULT=".mam/jobs"(:71) 주입 문구·브리프가 상대경로다 §3 에서 다룬다
loop 러너 workspace_root = MAM_REAL_ROOT/WORKSPACE_ROOT/cwd(runner.py:58) — §4 에서 다룬다
  • ⚠️1 워크트리 모니터 데몬:
    • create 는 start_watchdog "$DELEGATE_JOB_ID" "$WORKSPACE"(create_session.sh:527)를 부른다.
    • 그러면 데몬이 <WT>/.agents/skills/…/reconcile.sh(daemon.py:35-36, :63-85)로 뜬다. 중복 판정이 스크립트 경로 기준(:39-55)이라 본 체크아웃 데몬을 보지 못한다.
    • WORKSPACE_ROOT=$MAIN 이 상속되면, 이 데몬은 본 체크아웃의 monitor.lock 을 얻지 못해 곧 끝난다.
    • 그러나 본 체크아웃 데몬이 없던 순간이면 이 데몬이 워크스페이스 모니터가 된다. 그러면 제어 평면이 이슈 브랜치의 미검토 코드로 돈다.
    • §3 레시피에 MAM_NO_SUBSCRIBER_DAEMON=1(#19 옵트아웃)을 넣어야 한다.
  • ⚠️2 resume:
    • --workspace $WT 로 불러야 워크트리 행과 대화를 찾는다. $MAIN 을 주면 행을 찾지 못하거나 pane 이 본 체크아웃 cwd 로 떠 대화 키가 어긋난다.
    • 워크트리를 지우면 대화를 재개할 수 없다(키가 경로다). 같은 경로로 워크트리를 다시 만들어야 한다.
    • 문서 §2-C 의 "끝나면 정지(대화 보존, 필요 시 재개)" 에 "재개하려면 워크트리를 유지하거나 같은 경로로 재생성" 을 덧붙여야 한다.
  • pane 런타임 env (확인 권고):
    • create 는 herdr 에 CMD_FULL 만 넘긴다(create_session.sh:219). create 호출자가 export 한 override 가 에이전트 프로세스의 env 에 남는지는 herdr 서버 동작에 달렸다.
    • 남지 않으면 creator 가 워크트리에서 bash .agents/skills/… 로 부르는 스킬은 워크트리의 .mam 을 제어 평면으로 쓴다(lib.sh:26).
    • 브리프에 "워크트리에서 제어 평면 스킬을 실행하지 말 것" 을 넣거나, CMD_FULL 을 env WORKSPACE_ROOT=… MAM_JOBS_DIR=… 로 감싸야 한다.

2. §6-Q1 · 브리프 (5) — 두 문제의 해소와 놓친 경로

  • 체크아웃 전환 사고: 커밋하는 주체(creator, 오케스트레이터)가 모두 워크트리에서 커밋하므로 해소된다.
    • 다만 본 체크아웃에서 누구든 git checkout 할 수 있다는 점은 그대로다.
    • 싸고 직교하는 방어로 본 체크아웃 커밋 가드를 함께 권한다: pre-commit hook 이 git branch --show-current 를 확인해 거부한다.
  • 병렬성: 루프 lock 이 같은 .mam 에서 두 번째 루프를 exit 1 로 거부한다(run_loop.sh:144, :189-203, loop SKILL.md :235).
    • 따라서 C 안의 1단계 병렬은 다음 형태뿐이다: creator 구현은 루프 밖(delegate-job direct 나 대화)에서 병렬로, 리뷰 루프는 한 번에 하나.
    • §4 와 §6-Q4 에 이 운영 형태를 명시해야 한다.
  • 놓친 실패 경로:
    1. 리뷰어가 creator 의 살아 있는 워크트리에서 검증한다(§2-C "그 경로에서 git diff·테스트").
      • creator 가 리뷰 중에 커밋·편집하면 검증 대상이 바뀐다. #18·#19 에서 리뷰 중 HEAD 이동이 세 번 있었다.
      • 리뷰어의 테스트 산출물이 creator 트리에 남는다.
      • → 리뷰마다 detached 워크트리(git worktree add --detach "$TMP/review-<job>" <head sha>, 리뷰 뒤 제거)로 검증 대상을 불변으로 만들 것을 권한다. 새 에이전트 인스턴스가 필요 없는 C 안의 보강이다.
    2. §3 의 .venv symlink 가 git 에서 무시되지 않는다. 실험(임시 저장소, .gitignore 에 .venv/): symlink .venv 는 ?? .venv 로 보인다. 디렉터리 전용 패턴은 symlink 에 맞지 않는다.
      • 그래서 creator 의 git add -A 가 symlink 를 커밋한다.
      • 또 저장소 .gitignore 에 .pytest_cache 가 없어서(.gitignore:14-25 에 .mam/·.venv/·__pycache__/·.mam.env* 만 있음), -p no:cacheprovider 없이 돌린 pytest 도 산출물을 남긴다.
      • → 워크트리의 .git/info/exclude 에 /.venv·.pytest_cache/ 를 넣는 것을 준비 헬퍼(§5-3)에 포함한다.
    3. 워크트리 모니터 데몬(§1 ⚠️1).
    4. 워크트리 삭제 뒤 남는 yaml 행: herdr 가 죽으면 reconcile 이 drift A 로 terminated 표기한다(reconcile.sh:581-600). 행은 남지만 대화는 재개할 수 없다. → 해체 순서를 문서화한다: stop → (재개 불필요 확인) → 워크트리 제거.
    5. 토픽 지문·HMAC: 문제없다.
      • 잡 토픽은 레코드의 topic_prefix(python/mqtt/jobs/<id>, mqtt_common.py:137, :351)이고, HMAC 은 잡 레코드의 auth_token 이다. 레지스트리 경로만 맞으면(§3) 워크트리와 무관하다.
      • 지문은 모니터 구독에만 쓰인다(reconcile.sh:288-291). 제어 평면이 하나면 지문도 하나다.
      • 문서 §1 표의 해당 행에 이 점을 적어 둘 것.
    6. (부수) 러너는 리포트를 workspace_root/.agents/reports/… 로 보관한다(runner.py:554, :770-780). C 안에서는 본 체크아웃, 즉 main 트리로 간다. 이슈 브랜치로 보낼지 정해야 한다.

3. 브리프 (2) — 주입 문구의 상대경로 (§5-1)

  • 동의, 그리고 범위가 더 넓다. 래퍼가 에이전트에 주는 세 경로가 모두 cwd 상대다.
문자열 위치 워크트리 cwd creator 에게
브리프 .mam/jobs/$JOB_ID/brief.md 래퍼 :199, :455 없음(문서가 짚은 부분)
발행 명령 --registry-dir $REGISTRY_DIR(기본 .mam/jobs, :71) :198, :454 $WT/.mam/jobs 로 해석된다 → job record not found, rc 1. heartbeat 타이머도 상대 레지스트리·cwd 로 뜬다. started 가 안 나가므로 T 뒤 agent_blocked 로 닫힌다
Output Report Path .mam/jobs/$JOB_ID/…/report-final.md :178, :352 리포트가 $WT/.mam/… 에 쓰인다. 러너의 find_report(runner.py:389-395, 본 체크아웃 .mam)가 찾지 못한다
  • 래퍼가 MAM_JOBS_DIR 을 보지 않으므로 env override 로는 풀리지 않는다.
  • → §5-1 을 "REGISTRY_DIR 을 절대경로로 정규화하고, 브리프·발행 명령·리포트 경로 세 곳 모두 절대경로로" 로 고치고, 첫 C 안 적용 전 필수로 표시한다.

4. 브리프 (3)·(4) — 러너 diff·base_commit, .mam 경합

  • (3) 동의:
    • collect_changes_diff 는 workspace_root 의 스크립트와 cwd 를 쓴다(runner.py:397-411).
    • base_commit 은 workspace_root 의 HEAD 다(:564-565). 본 체크아웃이 main 이면 diff 는 비고 base 는 main 이다. 이번 #18 브리프들의 "(no changes since base commit)" 과 같은 증상이다.
    • §5-2 의 --worktree 는 다음을 정확히 지정해야 한다: 바뀌는 것은 diff 수집 경로, base_commit(워크트리에서 git merge-base main HEAD), 브리프에 넣는 경로·base·head 뿐이다. 그대로 두는 것은 wrapper 경로(:288), 레지스트리, 체크포인트(:60), 리포트 보관이며 본 체크아웃의 것을 쓴다.
  • (4) 보완:
    • 문서 §4 는 "동시 루프 금지" 를 경고로 서술하지만, 실제로는 lock 이 거부한다(run_loop.sh:189-203). 체크포인트도 파일 하나다(runner.py:60).
    • 세션 DB 는 SQLite 읽기이고 timeout=10(runner.py:199-203)이다. 쓰기 경합은 yaml 의 atomic_dump_yaml(flock) 쪽이다.
    • 리뷰어 busy 는 rc 5 → MAM_SESSION_BUSY_RETRY_S(기본 30) 뒤 1회 재시도(runner.py:312-322) → 실패로 이어진다. 루프 둘이 같은 리뷰어를 두고 다투면 한쪽이 실패한다.
    • 결론: 1단계를 "리뷰 루프는 하나씩, creator 구현만 병렬" 로 시작하면 §5-4 는 필요 없다. 루프 여러 개를 동시에 돌리는 것은 §5-4(루프 id 별 marker·체크포인트, 리뷰어 대기 큐)를 끝낸 뒤로 미룬다.

5. §6-Q3 · 브리프 (6) — A/B/C 평가

  • 동의:
    • A 의 비용(에이전트 수·토큰 한도가 이슈 수에 비례, 문맥 초기화, 관측 분산) 평가는 타당하다.
    • B 는 creator 가 여전히 본 체크아웃에서 브랜치를 옮기므로 #18 유형이 남는다는 판단이 옳다.
    • C 가 쓰는 쪽만 격리하는 최소 변경이다.
  • 대안·보강 2건:
    1. 본 체크아웃 커밋 가드(pre-commit hook 또는 커밋 명령 래퍼의 git branch --show-current 확인). 문제 2 를 C 와 무관하게 막는 가장 싼 방어다. C 채택과 함께 도입할 것.
    2. 리뷰마다 detached 워크트리(§2-1). 리뷰어 풀은 공유하되 검증 대상은 불변으로 만든다. A 의 격리 이점을 에이전트 증설 없이 얻는다.

6. §6-Q4 · §6-Q5 — §5 작업 항목과 #17 항목 4 순서

  • §5 수정 제안:
    • §5-1: 범위 확대(§3). 첫 적용 전 필수.
    • §5-2: --worktree 의 영향 범위 명시(§4). 첫 적용 전 필수.
    • §5-3: 준비 헬퍼에 다음을 포함한다.
      • MAM_NO_SUBSCRIBER_DAEMON=1
      • .git/info/exclude(/.venv, .pytest_cache/)
      • --herdr-session <본 서버> 고정
      • .mam.env 는 워크트리 스크립트를 쓸 때만 필요(래퍼·mqtt_common 은 스크립트 위치 기준으로 찾는다: 래퍼 :20-24, mqtt_common.py:54-65)
    • §5-4: 보류해도 된다(§4).
    • 추가:
      • §5-6: 워크트리 creator 의 stop·resume 레시피와 해체 순서(resume 은 --workspace $WT, 삭제 전 stop).
      • §5-7: 리뷰어 detached 워크트리 규약.
      • §5-8: 본 체크아웃 커밋 가드.
  • §6-Q5 (조건부 동의):
    • 이 모델을 문서·레시피로 먼저 굳히고 #17 항목 4(셸 계층 축소)에서 코드로 흡수하는 순서가 맞다.
    • 단 §5-1(래퍼 경로)과 §5-2(러너 옵션)는 작은 변경이고 없으면 C 안이 동작하지 않으므로 지금 한다. 항목 4 가 래퍼를 lib_py 로 옮길 때 그대로 따라가면 된다.
    • 헬퍼(§5-3)와 큐잉(§5-4)은 항목 4 안에서 구현하는 편이 중복을 줄인다.

7. 문서 수정 지점 (절·문장)

  • §1 표 "모니터 토픽 지문" 행: "(모니터 구독에만 쓰인다. 잡 토픽·HMAC 은 잡 레코드의 topic_prefix·auth_token)" 을 덧붙인다.
  • §2-C "creator = 워크트리 전용 인스턴스 … 끝나면 정지(대화 보존, 필요 시 재개)" 에 "재개는 같은 워크트리 경로가 있어야 한다" 를 추가한다.
  • §2-C "제어 평면 = …": 모니터 데몬 옵트아웃과, pane 런타임 env 에 override 가 남지 않을 수 있다는 점을 추가한다.
  • §2-C "리뷰어 풀 = …": 리뷰어는 creator 의 워크트리가 아니라 head sha 의 detached 워크트리에서 검증한다고 고친다.
  • §3 레시피:
    • MAM_NO_SUBSCRIBER_DAEMON=1 을 추가한다.
    • ln -s .venv 옆에 .git/info/exclude 처리를 적는다.
    • stop·resume 명령 예시를 추가한다.
    • "주입 문구의 상대경로 …" 문장을 발행 명령의 --registry-dir 와 Output Report Path 까지 넓힌다.
  • §4 세 번째 항목: "경고" 를 "lock 이 두 번째 루프를 거부(run_loop.sh:189-203)" 로 고친다. 1단계 운영 형태(구현 병렬, 리뷰 루프 1개)를 명시한다.
  • §5: 1·2·3 을 수정하고 6·7·8 을 추가하며, 4 는 "보류 가능" 으로 표시한다(§6).

8. 종합

  • C 안(쓰는 쪽만 워크트리, 읽는 쪽 공유, 제어 평면 하나)은 두 문제에 대한 올바른 방향이다.
  • status·monitor·stop 이 행의 pane.cwd 와 행별 herdr 서버를 이미 존중하므로 현재 코드와도 대체로 맞는다.
  • 보강이 필요한 곳:
    • 첫 적용 전 필수: 래퍼 경로 3종의 절대경로화, 러너 --worktree 범위, 모니터 데몬 옵트아웃, .venv symlink exclude
    • 운영 규약: resume·해체, 리뷰어 detached 워크트리, 커밋 가드
  • 모두 문서 보강과 작은 코드 변경으로 해결되며 설계를 뒤집을 결함은 아니다.

[VERDICT: PASS]