Files
multi-agent-mux/.agents/reports/planner-reviewer-claude-01/report-293e905f.md
T
GodopuandClaude Fable 5.1 6b7198f73c release(v4.7.1): bump framework and eight skills to 4.7.1 in lockstep; archive PR #32 merge-side reviews
Issue #25: a promoted plan is reused only with --plan-file; the implicit
newest-plan load is gone in both runners, and an empty planner report no
longer passes silently. VERSIONS.md carries the entry; the four merge-side
review reports and their identity sidecars (base 6fc9194 -> head 1e82651,
evidence review-tree) are archived under .agents/reports/<session>/.

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

13 KiB
Raw Blame History

🔍 Review Report — PR #32 병합 전 독립 리뷰 (job 293e905f)

  • Reviewer: planner-reviewer-claude-01 (role: planner,reviewer) · 2026-09-22
  • 대상: PR #32 fix/issue-25 @ 1e82651, base main 6fc9194 (v4.7.0). 5커밋, 4파일 +486/−31.
  • 검증 경로: /var/folders/.../T/mam-review/loop-20260921T150212Z-97d845/1 (브리프가 지정한 리뷰 워크트리). 실제로 거기서만 검증했다.
  • identity 확인: 검증 경로의 HEAD = 1e826515cc2a07ea1f8da0ff1259e1ea4dde86fd, HEAD^{tree} = 4fd82ad1c2682d12c3f5114818f97291af28a032 — 브리프의 head·tree 와 일치한다(항목 5 identity 가 실제로 작동함을 이 잡 자체가 보여준다).
  • 안전 규칙 준수: 검증 경로·소스 워크트리(multi-agent-mux-wt/pr-32)·본 체크아웃(main 6fc9194) 모두 수정·커밋·checkout 없음. 실험은 전부 scratchpad 임시 디렉터리(uuid4 이름)에서 했고, pytest 는 --basetemp 을 검증 경로 밖으로 주었다. 실제 .mam·데몬·세션 조작 없음, push·merge·PR 상태 변경 없음, purge·scrub 실행 없음.

결론 요약

병합을 막을 결함은 없다. 핵심 변경(암묵적 계획서 로드 제거, --plan-file 명시 재사용, 네 지점 공통 계획 텍스트 검사)은 코드·문서·테스트가 일치하며, 새 테스트 17건 중 13건이 수정 전 소스에서는 실패한다(직접 확인). 비차단 지적 3건(E1–E3)을 남긴다.

1. 암묵적 로드 제거 (항목 1)

확인 결과
Python 러너 runner.py 에서 glob import 가 사라졌고, 남은 glob 사용처는 find_report 의 report-final.md 탐색(:568) 하나뿐이다. .agents/reports/<planner>/plan-*.md 를 읽는 코드가 없다
레거시 셸 러너 run_loop.sh 의 EXISTING_PLAN_FILE/ls -t .../plan-*.md 블록이 통째로 사라지고 log_info "No plan loaded; the Creator plans by itself." 만 남았다
다른 경로 저장소 전체에서 plan-*.md 를 읽는 코드는 없다(승격 쪽 쓰기만 runner.py:1368-1372 에 남는다 — 정상)
planner 자동 선택 resolve_planner_session(run_loop.sh:386)·resolve_planner_session 파이썬 대응은 남아 있으나, 그 결과는 plan 모드의 위임 대상과 승격 경로에만 쓰인다. 자기계획 모드에서 planner 이름으로 파일을 찾는 코드는 없다

2. --plan-file (항목 2) — 실측

검증 경로의 러너를 임시 제어 루트(WORKSPACE_ROOT=<tmp>/ctl)로 직접 돌려 확인했다.

입력 결과
없는 파일 [✗] --plan-file <절대경로>: not a readable file with plan text in it. · exit 1
빈 파일 / 공백만 있는 파일 같은 메시지 · exit 1
디렉터리 같은 메시지 · exit 1 (is_file() 로 걸린다)
읽기 권한 없음(chmod 000) 같은 메시지 · exit 1 (OSError → None)
--plan 과 함께 ERROR: --plan-file cannot be combined with --plan. + 사용 예 2줄 · exit 1
상대경로 제어 루트 기준. 호출 cwd(<tmp>/elsewhere)에 plans/p.md 가 실제로 있어도 러너는 <ctl>/plans/p.md 를 찾고 실패한다 — #25 의 "다른 디렉터리의 동명 파일을 집어오는" 사고를 구조적으로 막는다
공백 있는 경로(my plans/p.md, 상대) 정상 로드: Using the plan given with --plan-file: <ctl>/my plans/p.md (modified ... UTC) (셸을 거치지 않으므로 인용 문제 없음)
절대경로·~ Path.__truediv__ 가 절대경로를 그대로 두고 expanduser() 가 앞서 적용된다(P3d 가 고정)
--review-only 와 함께 경고 후 무시(log_warn), current_plan 을 채우지 않는다
레거시 셸(MAM_LOOP_PY=0) ERROR: --worktree/--base/--review-only/--review-isolation/--remote-review/--plan-file require the Python loop (MAM_LOOP_PY=1) · exit 1 — 동등 구현 대신 명시적 거부이며, 기본 경로(MAM_LOOP_PY=1)는 exec "$_py" -m lib_py.loop "${MAM_LOOP_ARGV[@]}"(run_loop.sh:262)로 원본 argv 를 그대로 넘기므로 공백 경로도 안전하다

읽기가 잡을 하나도 만들기 전(parse_args 단계)에 끝나는 것도 확인했다 — 실패 시 레지스트리에 아무 흔적이 없다.

3. 계획 텍스트 검사 (항목 3)

_plan_text()(runner.py:303-317)가 "읽을 수 있는 일반 파일 + 공백 제외 내용 있음"을 단일 판정으로 만들고, 파이썬 러너의 네 지점이 모두 이를 쓴다: --plan-file(:265), --plan 초기 계획서(:1025), 논의 턴 refine(:1054), 리뷰 에스컬레이션 refine(:1284).

  • 빈 초기 계획서 → log_error 후 return 1. 이때 이미 만든 잡은 release_guard/정리 경로가 담당하고, 자기계획으로의 조용한 격하가 사라졌다.
  • 빈 refine → 경고 + 이전 계획 유지. 그래서 "갱신된 다음 계획서에 입각하여…" 프롬프트가 빈 계획을 싣는 일이 없다.
  • 에스컬레이션 경로([ESCALATE: PLANNER])는 if self.plan_mode and complex_fix(:1275)로 plan 모드에만 걸린다. --plan-file 로 계획을 실은 실행에서 ESCALATE 판정이 나오면 재계획 없이 일반 수정 라운드로 간다 — 샌드박스에서 직접 구동해 확인했다(Planner 위임 0건, 수정 프롬프트는 "리뷰어들이 지적한 다음 피드백…"). SKILL.md:31 의 "수정 라운드에는 리뷰 피드백만" 서술과 일치한다. 다만 이 동작 자체는 E2 로 남긴다.

4. 체크포인트 (항목 4)

.mam/loop-checkpoint.json 의 current_plan 은 쓰기 전용이다. checkpoint.load_checkpoint() 는 정의되어 있으나 runner.py 어디에서도 호출하지 않는다(grep 'checkpoint\.' → save_checkpoint·remove_checkpoint 뿐). 따라서 본 체크아웃 체크포인트에 남아 있는 Issue #6 계획 텍스트가 다음 루프나 재개에서 되살아날 경로는 없다. 계획은 매 실행마다 --plan-file 또는 --plan 으로만 들어온다.

5. 옵션 없을 때의 동작 변화 (항목 5)

  • 자기계획 프롬프트(runner.py:1095)와 승격 경로(:1367 if self.plan_mode and self.current_plan and self.plan_job_id)는 그대로다. 즉 --plan-file 로 실은 계획서는 승격되지 않는다(승격은 plan 모드의 planner 산출물만) — 옳은 구분이다.
  • 바뀐 것은 "planner 리포트 디렉터리에서 최신 plan-*.md 를 끌어오던" 동작 하나뿐이다. 그 외 프롬프트 문구·옵션 기본값·종료 코드는 동일하다.
  • 기존 스위트 정합은 §6 에서 실측했다.

6. 테스트 (항목 6)

6-1. 검증 경로에서의 실행

cd <검증 경로> && PYTHONDONTWRITEBYTECODE=1 <제어 루트 .venv/bin/python> -m pytest -p no:cacheprovider -q -rfEs \
  --basetemp=<검증 경로 밖> tests/test_issue25_plan_reuse.py tests/test_loop_cli.py tests/test_issue5_phase3_loop.py \
  tests/test_issue21_loop_b.py tests/test_issue21_phase2_loop_a.py tests/test_issue21_acceptance.py
→ 2 failed, 108 passed in 220.98s (skipped 0)

실패 2건은 PR 회귀가 아니라 리뷰 워크트리에 .venv 가 없어서 생긴 환경 문제다.

실패 원인 확인
test_loop_cli.py::test_creator_parses_and_reaches_session_check run_loop.sh 를 하위 프로세스로 띄우는데, REPO_ROOT(= 검증 경로)에 .venv 가 없어 _py 가 시스템 python 으로 떨어지고 RuntimeError: Failed to eager load lib_py.loop.autocomplete: No module named 'paho' 로 죽는다(run_loop.sh:253) AGENT_PYTHON_BIN=<제어 루트 .venv/bin/python> 만 주고 둘을 다시 돌리면 2 passed in 0.34s
test_issue5_phase3_loop.py::test_p3_02_run_loop_wrapper_delegates_to_python_default 동일 동일

두 시험 모두 PR 이 건드리지 않은 파일이며, 실패 메시지에 계획서·--plan-file 은 등장하지 않는다.

6-2. 수정 없이는 실패하는가 — 확인함

검증 경로 사본을 만들고 runner.py·run_loop.sh·SKILL.md 세 파일만 base 6fc9194 판으로 되돌려 새 테스트를 돌렸다(검증 경로 자체는 건드리지 않았다).

13 failed, 4 passed in 1.51s
FAILED P1, P2, P3b, P3c, P3d, P5, P9[empty], P9[whitespace], P10, P11, P7, P7b, P8

통과한 4건(P3·P4·P5b·P6)은 옵션 없는 일반 CLI 오류·plan 모드 정상 경로라 수정과 무관한 것들이다. 즉 새 테스트는 이 변경을 정확히 겨냥한다.

6-3. 격리

항목 결과
실제 .mam/jobs 전후 차이 0
실제 .agents/reports 새 항목 없음(테스트는 각자 tmp_path 안에 제어 루트를 만든다 — ctl 픽스처)
검증 경로 tracked 파일 변화 없음(git status --short 클린, HEAD 1e82651 유지)
검증 경로 ignored 내 실행이 .mam/(무시됨)을 만들었다. tracked 파일·커밋에는 영향이 없으며, 지우면 리뷰 트리를 수정하는 셈이라 그대로 두었다 → E3
실행 시간 새 파일 test_issue25_plan_reuse.py 는 전체 17건이 1.5 s 안에 끝난다(브로커·herdr 의존 없음, 모두 stub)

7. 문서 (항목 7)

  • 옛 서술("기존에 승격된 계획서가 있다면 이를 로드")은 .agents·docs 어디에도 남아 있지 않다(전수 grep).
  • SKILL.md 의 사용법 문자열·CLI 표·Workflow 예시·Mermaid 다이어그램이 모두 --plan-file 기준으로 맞춰졌고, "계획서가 비어 있을 때" 절이 네 지점의 판정을 정확히 설명한다.
  • 러너 usage 와 셸 usage 문자열이 동일하다(P7 이 이를 고정한다).
  • 배포 사본: 저장소의 .claude/skills/* 는 ../../.agents/skills/* 심링크라 자동으로 동기화된다. 사용자 홈 ~/.claude/skills 에는 mux 스킬 사본이 없다. 옛 문구를 가진 사본은 발견되지 않았다.

8. 병합 차단 여부 (항목 8)

버전 락스텝 bump 부재(lib.sh:32·eager.py:16·loop SKILL.md 모두 4.7.0, 오케스트레이터가 4.7.1 로 처리 예정) 외에 병합을 막을 결함은 없다.

# 비차단 지적 근거
E1 레거시 셸 러너(MAM_LOOP_PY=0)의 plan 모드 읽기에는 빈 계획서 검사가 없다: CURRENT_PLAN=$(cat "$PLAN_FILE")(run_loop.sh:501). D13/D14 계약은 파이썬 러너에만 성립하는데 SKILL.md 의 "계획서가 비어 있을 때" 절은 이를 명시하지 않는다 후속: 한 줄 거부 또는 문서에 "Python 러너 기준" 명시
E2 --plan-file 로 계획을 실은 실행에서 [ESCALATE: PLANNER] 는 재계획 없이 일반 수정 라운드로 내려간다(runner.py:1275 가 plan_mode 로 게이트). 샌드박스 구동으로 확인(Planner 위임 0건). 이 게이트는 PR 이전부터 있던 것이고 SKILL.md 서술과도 모순되지 않지만, "계획서를 갖고 도는 루프인데 에스컬레이션이 무력화된다"는 점은 문서에 없다 문서 보강 권고
E3 리뷰 격리 워크트리에는 .venv 가 없어, run_loop.sh 를 하위 프로세스로 띄우는 스위트는 AGENT_PYTHON_BIN 없이는 반드시 실패한다(§6-1). 또한 그 워크트리에서 pytest 를 돌리면 무시되는 .mam/ 이 생긴다. PR #32 가 아니라 Issue #21 항목 5 운영 쪽 후속이다 리뷰 브리프의 테스트 명령에 AGENT_PYTHON_BIN=<제어 루트 .venv/bin/python> 를 포함하는 것을 권고
E4 runner.py:1070 의 os.path.getmtime(self.plan_file) 는 파싱 시점과 Phase 1 사이에 파일이 사라지면 예외로 죽는다(메시지 없는 트레이스백). 극히 좁은 경합 사소

9. 교차검증 가이드

RP=/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260921T150212Z-97d845/1
PY=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python
git -C "$RP" rev-parse HEAD HEAD^{tree}      # 1e8265... / 4fd82a... = 브리프의 head·tree
# --plan-file 실패 경로 (임시 제어 루트에서만)
C=$(mktemp -d); mkdir -p "$C/ctl" "$C/elsewhere/plans"; : > "$C/ctl/empty.md"; echo x > "$C/elsewhere/plans/p.md"
(cd "$C/elsewhere" && PYTHONPATH="$RP/.agents/skills" WORKSPACE_ROOT="$C/ctl" MAM_STATE_JSON='{"herdr_sessions":[]}' \
   "$PY" -m lib_py.loop --creator c --task t --plan-file plans/p.md; echo "rc=$?")   # ctl 기준 경로로 실패, rc 1
# 수정 없이는 실패하는지 (사본에서만)
S=$(mktemp -d); rsync -a --exclude .venv --exclude .git --exclude .mam --exclude nats-docker "$RP/" "$S/"
for f in .agents/skills/lib_py/loop/runner.py .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \
         .agents/skills/multi-agent-mux-loop/SKILL.md; do git -C "$RP" show 6fc9194:"$f" > "$S/$f"; done
(cd "$S" && PYTHONDONTWRITEBYTECODE=1 "$PY" -m pytest -q -p no:cacheprovider --basetemp=/tmp/bt tests/test_issue25_plan_reuse.py)  # 13 failed
# 실패 2건이 환경 문제임을 확인
(cd "$RP" && AGENT_PYTHON_BIN="$PY" PYTHONDONTWRITEBYTECODE=1 "$PY" -m pytest -q -p no:cacheprovider --basetemp=/tmp/bt2 \
   tests/test_loop_cli.py::test_creator_parses_and_reaches_session_check \
   tests/test_issue5_phase3_loop.py::test_p3_02_run_loop_wrapper_delegates_to_python_default)   # 2 passed

[VERDICT: PASS]