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
8.1 KiB
8.1 KiB
PR #58 (fix/issue-57) 병합 전 2차 리뷰 (job b48be8a6)
- 리뷰어: planner-reviewer-claude-01 (Reviewer)
- 리뷰 identity: loop-20261015T123502Z-1b9df6 / base
e32c9574.. heada7cb90e6(커밋 15개; 1차 이후 대응 4개:75c0042F1,151af99F2,1c598bbR1,a7cb90eR2) / tree96c5c5f8 - 검증 경로:
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261015T123502Z-1b9df6/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 또는/tmp/jb48-r2(종료 후 삭제)의 임시 워크스페이스·cwd 에서만 했다. 수정 전 대조군은git archive HEAD복사본에 제품 파일만 되돌려 만들었다. 실제.mam·.mam.env·모니터·herdr 서버·에이전트 홈 미접촉.
1. 1차 지적 4건 — 재현을 다시 돌린 결과
| 지적 | 1차 재현 (63d8164) |
head (a7cb90e) |
수정 전 코드에서 회귀 테스트 |
|---|---|---|---|
| F1 paho 없는 폴백에서 control_plane·status 실패 | AGENT_PYTHON_BIN=/usr/bin/python3 로 mam_control_plane_json rc=1 ModuleNotFoundError: paho; get_job_status 예외 |
rc=0, 바인딩 JSON 에 로그 루트 포함; status.sh 의 get_job_status 두 복사본 모두 ('abc12345', 'unknown') |
test_control_plane_binding_works_without_paho, test_status_works_without_paho[json/text] 실패(paho 미존재로 control_plane·service import 실패) |
F2 래퍼 logs 기본 경로가 cwd 상대 |
WORKSPACE_ROOT=ws, cwd=elsewhere 에서 logs --list 가 잡을 못 찾음 |
logs --list 가 ws 의 잡을 나열, logs <id> 에 registered 이벤트, --registry-dir 를 주면 그 레지스트리를 따름, elsewhere 에 아무것도 안 생김 |
test_logs_without_registry_dir_follows_the_workspace_from_any_cwd 실패((no logged jobs under …)) |
| R1 디스크 폴백이 이후 디스패치의 terminal 을 이전 시도로 오인 | (codex 지적) | 감사 상태 채택 직전에 레코드를 다시 읽어 (dispatch_token, attempt_id, transition_seq) 가 처음 읽은 것과 같을 때만 채택 |
test_disk_fallback_rejects_a_terminal_that_belongs_to_a_later_dispatch 실패(B's completion was attributed to A) |
| R2 jobs 디렉터리가 심볼릭 링크일 때 submit 과 logs 불일치 | (codex 지적) | /tmp/jb48-r2/alias/jobs -> actual/jobs 로 submit --dry-run: 출력 경로·감사 로그 모두 actual/delegate_job_logs/<id>; logs --registry-dir alias/jobs 와 registry.py --registry-dir actual/jobs logs --list 가 같은 잡을 찾음 |
test_a_symlinked_registry_dir_resolves_to_one_audit_log 는 63d8164 에서는 logroot 부재로 ImportError 라 증거가 안 되므로, a7cb90e^ (logroot 있음, abspath 규칙) 로 다시 돌려 실패(…/alias/… ≠ …/actual/delegate_job_logs) 확인 |
2. 수정이 새 문제를 만들지 않았는가
F1 (모듈 이동)
lib_py/delegation/logroot.py는 stdlib(os,typing)만 쓴다.mqtt_common이DEFAULT_REGISTRY_DIR·_workspace_mam_dir·default_registry_dir·get_logs_dir·sibling_logs_dir를 그대로 재노출하므로 기존 import 경로(mqtt_common.get_logs_dir등)는 불변이다.from lib_py.delegation.logroot import …는 패키지__init__을 거쳐daemon·service·registry·publish_event·publisher까지 로드한다. 이 Mac 의/usr/bin/python3로 직접 import 해 보니 12 개lib_py모듈이 모두 올라오고paho는 로드되지 않았다. 직접 paho 를 import 하는 곳은mqtt_common·publisher둘뿐이고 둘 다try/except ImportError로 감쌌으며from __future__ import annotations라-> mqtt.Client주석도 import 시 평가되지 않는다. 실제로 브로커가 필요하면make_client가 명확한ImportError를 낸다.- eager import:
logroot는eager.CORE_MODULES에 없지만 목록에 있는mqtt_common이 import 시점에 로드하므로 동결 런타임에 함께 들어간다(아래 비차단 메모).
F2: local -a reg=() + ${reg[@]+"${reg[@]}"} 를 /bin/bash -u 로 직접 확인 — 빈 배열이면 인자 없음, 주어지면 --registry-dir <dir>. 래퍼의 다른 subcommand 는 바뀌지 않았다.
R1 (재읽기)
- 경합: B 의 terminal 감사 상태는 재디스패치(레코드 토큰·seq 변경)가 커밋된 뒤에만 생긴다. 그러므로 감사 읽기에서 B 의 terminal 이 보였다면 레코드는 이미 그 전에 바뀌었고, 그 뒤의 재읽기가 반드시 차이를 본다. 재읽기 이후의 변경은 이미 읽은 감사 상태와 무관하다.
- 같은 시도 안에서 정상 전이(A running → A completed)가 두 읽기 사이에 끼면 seq 가 달라져 "deferred" 로 한 라운드 늦어질 뿐이고, 다음 라운드는 레코드 자체의 terminal 을 읽는다.
- 성능: 감사 상태가 terminal 이고 stale 이 아닐 때만 레코드 하나를 더 읽는다. 무시할 만하다.
R2 (realpath)
- 기본 경로:
WORKSPACE_ROOT=/tmp/...(macOS 에서/tmp → /private/tmp)일 때get_logs_dir()은 이제/private/tmp/.../.mam/delegate_job_logs문자열을 돌려준다. 물리적으로는 이전과 같은 디렉터리임을 확인했다. 결과를 문자열로 비교하는 곳은control_plane.forwarded_keys(이제 양쪽 모두_abs=realpath)와 루프 러너의 제외 목록(runner.py가d.resolve().relative_to(root.resolve()))뿐이라 영향이 없다. forwarded_keys탐침:/tmp·/private/tmp혼합 두 방향, 후행/, 심볼릭 링크 alias 경유 모두 형제로 판정되어 전파하지 않는다. 비형제만 전파한다. 1차에서 비정규 입력이 "전파" 쪽으로 어긋나던 비대칭도 사라졌다.from_env({"WORKSPACE_ROOT": "/tmp/..."})의 기본 바인딩은defaults()와 같아서, 평소 실행은 pane 에 아무것도 전파하지 않는다.- 레지스트리 디렉터리 자체가 심볼릭 링크인 경우에만 감사 로그가 링크 대상 옆으로 간다. 이는 R2 가 의도한 동작이며, 래퍼(
pwd -P)와 바인딩(realpath)이 이미 그렇게 보고 있었다.
3. 이 Mac 정합성
- 변경 py 15 개:
ast.parse(feature_version=(3,9))와/usr/bin/python3 -m py_compile모두 통과. 3.10+ 문법·API 없음. lib.sh·래퍼·status.sh:/bin/bash -n(3.2) 통과.
4. 테스트 (head, 검증 경로)
| 묶음 | 결과 |
|---|---|
| A: issue57·15·21_acceptance·21_worktree_prereqs·tier1·20·17_item4·17_workspace_root_isolation·36·14 | 475 passed, 2 skipped |
| B: issue18·6_fail_closed·34·33·5_phase1/2a/2b/4_runtime·tier2·12·23_24·21_phase2·b7 | 493 passed |
5_phase4_runtime 의 eager 모듈 검사도 통과했다. 어느 실행도 검증 경로에 .mam 을 남기지 않았고 #55 가드 오탐도 나오지 않았다.
5. 비차단 메모
lib_py.delegation.logroot를eager.CORE_MODULES에 명시하면 의도가 분명해진다. 지금도mqtt_common을 통해 로드되므로 기능 문제는 없다.- paho 없이
publisher의 브로커 경로를 직접 타면make_client의ImportError가 먼저 나는 구조다. 다른 진입점이mqtt.를 먼저 건드리면AttributeError: 'NoneType'로 보일 수 있지만, 브로커가 필요한 경로는 원래 paho 가 필수다.
결론
1차 지적 4건(F1·F2·R1·R2)이 모두 해소됐다. 1차 재현을 다시 돌려 확인했고, 각 회귀 테스트가 수정 전 코드에서 의도한 이유로 실패한다(R2 는 직전 부모 커밋 기준). 모듈 이동·재읽기·realpath 는 기본 경로, 바인딩·전파, 기존 import 경로에 새 문제를 만들지 않으며, 이 Mac 의 Python 3.9·bash 3.2 에서 정합하다.
[VERDICT: PASS]