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
9.0 KiB
9.0 KiB
PR #58 (fix/issue-57) 병합 전 리뷰 — 이 Mac 정합성 중심 (job b9c7522d)
- 리뷰어: planner-reviewer-claude-01 (Reviewer)
- 리뷰 identity: loop-20261014T152849Z-f1a415 / base
e32c9574.. head63d81646(커밋 11개, 21 파일) / treee8f162f7 - 검증 경로:
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261014T152849Z-f1a415/1— HEAD·tree 일치,git status --porcelain0 줄,.mam없음(시작·종료 모두). 본 체크아웃 HEAD e32c957(main) 변동 없음. - 환경: macOS,
/usr/bin/python33.9.6(paho 없음 — 이 Mac 의 PATHpython3도 이것), 체크아웃.venv3.9.6(paho 2.1.0), PATHbash=/bin/bash3.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> PATHpython3. README 도 이 폴백을 문서화한다. 이 Mac 의 PATHpython3은/usr/bin/python33.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-jobcmd_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 status0 줄). #55 가드 오탐은 이번 실행에서 나오지 않았다.
결론
규칙의 일원화, 쓰기·읽기 지점, macOS 경로 정규화, 디스크 폴백의 재디스패치 안전성, bash 3.2·Python 3.9 이식성, 테스트의 회귀 탐지는 모두 맞다. 그러나 (F1) 경로 헬퍼를 paho 를 끌어오는 모듈에서 import 해 이 Mac 의 문서화된 python3 폴백에서 create/resume 바인딩과 status 가 깨지고, (F2) 래퍼 logs 의 기본 경로가 cwd 상대로 바뀌었다. 둘 다 base 대비 재현됐고 작은 수정으로 끝난다.
[VERDICT: NOT PASS]