Files
multi-agent-mux/.agents/reports/planner-reviewer-claude-01/report-b9c7522d.md
T
GodopuandClaude Opus 5.5 7cf728e9f8 release(v4.9.4): the audit log follows a moved registry (Issue #57, PR #58) - archive the review reports and bump the lockstep version
Review loop (--review-only, --all-reviewer, focused on this Mac:
macOS, /usr/bin/python3 3.9.6, bash 3.2): round 1 (@63d8164) agy and
grok PASS, claude and codex NOT PASS (F1 paho via the /usr/bin/python3
fallback, F2 logs default path, R1 disk-fallback redispatch
interleaving, R2 symlinked registry), sent as a PR comment and fixed by
the author in four commits; round 2 (@a7cb90e) 4/4 PASS.

Full suite on macOS / Python 3.9.6 @a7cb90e: 1867 passed, 56 skipped,
1 error (the known #55 false positive: WAL/SHM of an agy conversation
running in another workspace). Real herdr panes and ~/.claude/projects
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
2026-10-15 22:52:04 +09:00

9.0 KiB

PR #58 (fix/issue-57) 병합 전 리뷰 — 이 Mac 정합성 중심 (job b9c7522d)

  • 리뷰어: planner-reviewer-claude-01 (Reviewer)
  • 리뷰 identity: loop-20261014T152849Z-f1a415 / base e32c9574 .. head 63d81646 (커밋 11개, 21 파일) / tree e8f162f7
  • 검증 경로: /var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261014T152849Z-f1a415/1 — HEAD·tree 일치, git status --porcelain 0 줄, .mam 없음(시작·종료 모두). 본 체크아웃 HEAD e32c957(main) 변동 없음.
  • 환경: macOS, /usr/bin/python3 3.9.6(paho 없음 — 이 Mac 의 PATH python3 도 이것), 체크아웃 .venv 3.9.6(paho 2.1.0), PATH bash = /bin/bash 3.2.57.
  • 실행 안전: 모든 테스트·탐침은 env -i(PATH/HOME/LANG/TMPDIR 만, 필요한 값만 명시)로 브리프의 -u 목록 전부를 비웠고 basetemp 는 scratchpad 아래(이름에 "shim" 없음). 수동 재현은 scratchpad 의 임시 워크스페이스·cwd 에서만 했고, base 비교는 git archive e32c957 복사본으로 했다. 실제 .mam·.mam.env·모니터·herdr 서버·에이전트 홈 미접촉.

1. 결과 요약

항목 결과
Python 3.9 문법(ast.parse(feature_version=(3,9)), 변경 py 13 개)·py_compile 통과. 3.10+ 문법·API(match, X | None, 내장 제네릭 주석, removeprefix, tomllib, zip(strict=)) 사용 없음
bash 3.2 bash -n(lib.sh, 래퍼, worktree_prepare, reconcile, status) 통과. 래퍼의 LOGS_DIR_ABS="" 전역 초기화, var=$(…) || {…} 실패 처리, env_add+=(…) 모두 3.2 set -u 안전
head 관련 모듈 A: issue57·15·20·21_acceptance·21_worktree_prereqs·tier1·17_item4·17_workspace_root_isolation·b7·36 477 passed, 2 skipped
head 관련 모듈 B: issue14·18·6_fail_closed·34·33·5_phase1/2a/2b·tier2·12·23_24·21_phase2·10_13_purge 503 passed(알려진 실패 #28 의 test_issue10_13_purge 도 이 Mac 에서 통과)
수정 전 대조군(제품 파일만 base 로 되돌린 복사본) + 새 test_issue57_logs_follow_registry.py 15 failed, 6 errors, 1 passed — 회귀를 실제로 잡는다
이전 라운드(ee7cef33) 차단 지적: test_issue15…:64, test_tier1_unit…:512 두 곳 모두 monkeypatch.delenv("MAM_JOBS_DIR") 추가로 해소, 통과

2. 맞게 된 부분

  • 규칙은 한 곳: mqtt_common.sibling_logs_dir(순수) + get_logs_dir(registry_dir)(DELEGATE_JOB_LOGS_DIR > 레지스트리 형제). 쓰기·읽기 지점(register·pick·legacy/engine 미러·conflict·publish·subscriber·remote·reconcile·runner·lib.sh·래퍼)이 모두 이 함수를 거친다. lib.sh 의 bash 기본값 사본은 delegate_logs_dir 로 대체됐고, 해석 실패 시 감사 확인만 건너뛰고 events.log 증거는 유지한다.
  • macOS 경로: control_plane._abs 는 realpath(expanduser()) 라 from_env·행 기록이 모두 /private/tmp·/private/var 로 정규화된다. forwarded_keys 를 직접 탐침: 형제(정규화 후 동일) → 전파 안 함, /tmp 와 /private/tmp 가 섞인 비정규 입력·후행 /·비형제 → 전파. 어긋날 때는 늘 "전파" 쪽(기존 동작)이라 안전하다.
  • 디스크 폴백의 재디스패치 안전성: job_subscriber._check_disk_fallback 은 이제 status.json 의 status 필드를 읽고(이전엔 dict 를 비교해 아무것도 맞지 않았음), 버전 있는 레코드는 감사 transition_seq 가 같거나 새로울 때만 채택한다. 이전 시도의 terminal 감사 상태가 새 시도를 끝내지 못하며(test_old_audit_terminal_does_not_complete_the_redispatched_attempt), 손상된 seq 는 stale 로 처리한다. 버전 없는 레코드·레코드 없음은 기존 규칙.
  • 래퍼: 16 곳의 ${DELEGATE_JOB_LOGS_DIR:-$WORKDIR/delegate_job_logs} 제거, 해석 실패 시 fail-closed(exit 1), 해석값을 export 해 자식과 주입 명령이 같은 루트를 쓴다.
  • worktree_prepare / control_plane: 호출자가 명시한 DELEGATE_JOB_LOGS_DIR 만 넘기고, 유도 가능한 형제 루트는 행에만 기록한다. 기존 행 중 레지스트리를 옮겼지만 로그가 워크스페이스에 있던 것은 비형제라 계속 전파되어 resume 짝이 유지된다.

3. 차단 지적

F1. 경로 헬퍼 하나 때문에 create/resume 바인딩과 status 가 paho 를 요구하게 됐다 — 이 Mac 의 문서화된 폴백에서 회귀.

  • lib_py/control_plane.py:28 이 from lib_py.delegation import mqtt_common 을 새로 하고(base 의 control_plane 은 stdlib 만), status.sh:171,383 이 get_job_status 안에서 from lib_py.delegation.mqtt_common import sibling_logs_dir 를 무조건 한다. mqtt_common.py:33 은 모듈 최상단에서 import paho.mqtt.client 다.
  • 두 경로의 인터프리터는 _delegate_py_bin(lib.sh:2111-2130): AGENT_PYTHON_BIN > 활성 venv > 스킬 위쪽 가장 가까운 .venv > PATH python3. README 도 이 폴백을 문서화한다. 이 Mac 의 PATH python3 은 /usr/bin/python3 3.9.6 이고 paho 가 없다(install.sh 는 시스템 python3 에 PyYAML 만 요구).
  • 재현(임시 워크스페이스, herdr 미호출):
    AGENT_PYTHON_BIN=/usr/bin/python3 로 lib.sh source 후 mam_control_plane_json <wt>
      base: rc=0
      head: rc=1  ModuleNotFoundError: No module named 'paho'
    status.sh 의 get_job_status 를 그대로 떼어 /usr/bin/python3 로 job 이 있는 행에 실행
      base: ('abc12345', 'unknown')
      head: ModuleNotFoundError: No module named 'paho'
    
  • 영향: 그 폴백에서 create_session.sh:232-238·resume_session.sh:100-106 은 워크트리 대상이면 바인딩 계산 실패로 exit 2(거부), 제어 루트면 control_plane 없이 기록한다. 읽기 전용 status.sh 는 위임 잡이 걸린 행이 하나라도 있으면 heredoc 이 예외로 죽는다. 둘 다 base 에서는 paho 없이 동작했다.
  • 수정(작음): sibling_logs_dir 를 paho 를 import 하지 않는 모듈(예: lib_py/paths.py 또는 새 lib_py/delegation/logroot.py)로 옮기고 mqtt_common 은 그것을 재노출, control_plane·status.sh 는 그 모듈에서 import. (또는 mqtt_common 의 paho import 를 지연.) 회귀 테스트로 "paho 없는 인터프리터에서 control_plane json·get_job_status 가 동작" 을 두면 좋다.

F2. 래퍼 logs 의 기본 경로가 바뀌었다 (레지스트리를 옮기지 않은 평소 실행).

  • multi-agent-mux-delegate-job cmd_logs 는 이제 항상 --registry-dir "$reg" 를 넘기고 reg 의 기본은 REGISTRY_DIR_DEFAULT="${MAM_JOBS_DIR:-.mam/jobs}"(:78) — cwd 상대다. base 는 --registry-dir 없이 registry.py logs 를 불러 get_logs_dir() 이 $WORKSPACE_ROOT/.mam/delegate_job_logs 를 썼다(registry.py 자체의 기본 default=default_registry_dir()(:370) 도 WORKSPACE_ROOT 를 존중한다).
  • 재현(임시 디렉터리, WORKSPACE_ROOT=ws 에 잡 하나 등록, cwd=elsewhere):
    base: logs --list → 그 잡을 나열
    head: logs --list → "(no logged jobs under <elsewhere>/.mam/delegate_job_logs)"
    
  • 수정(작음): --registry-dir 가 주어졌을 때만 넘긴다(이전 라운드 구현처럼 local reg=() 배열 + ${reg[@]+"${reg[@]}"}). 그러면 registry.py 의 기본(MAM_JOBS_DIR > WORKSPACE_ROOT > cwd)이 그대로 쓰여 기본 경로가 불변이다. "cwd≠워크스페이스에서 logs" 테스트 한 건을 권한다.

두 지적 모두 한두 줄 수정이며 설계 변경은 필요 없다.

4. 비차단 메모

  • docs/design/worktree-creator-model.md 의 status 행(“MAM_JOBS_DIR·행 mam_jobs_dir·감사 로그(DELEGATE_JOB_LOGS_DIR·행 delegate_job_logs_dir)·기존 후보 순 조회”)에 새로 추가된 "레지스트리 옆 감사 로그" 후보가 빠져 있다.
  • control_plane.forwarded_keys 는 로그 쪽 값을 정규화 없이 비교한다. 실제 흐름에서는 양쪽이 이미 realpath 라 문제없고 어긋나도 전파 쪽이라 안전하지만, 양쪽에 _abs 를 걸면 의도가 분명해진다.
  • cmd_logs 의 --registry-dir 뒤 인자가 없으면 set -u 로 "unbound variable" 종료 — fail-closed 이므로 무방.
  • 수정 전 대조군의 6 errors 는 base 코드가 테스트가 기대하지 않는 위치에 쓰면서 생긴 setup/teardown 오류로, head 에서는 없다.

5. 잔류물

  • 복사본·basetemp·탐침 디렉터리는 모두 scratchpad 아래. 검증 경로는 깨끗(.mam 없음, git status 0 줄). #55 가드 오탐은 이번 실행에서 나오지 않았다.

결론

규칙의 일원화, 쓰기·읽기 지점, macOS 경로 정규화, 디스크 폴백의 재디스패치 안전성, bash 3.2·Python 3.9 이식성, 테스트의 회귀 탐지는 모두 맞다. 그러나 (F1) 경로 헬퍼를 paho 를 끌어오는 모듈에서 import 해 이 Mac 의 문서화된 python3 폴백에서 create/resume 바인딩과 status 가 깨지고, (F2) 래퍼 logs 의 기본 경로가 cwd 상대로 바뀌었다. 둘 다 base 대비 재현됐고 작은 수정으로 끝난다.

[VERDICT: NOT PASS]