Merge-workspace review loop (--all-reviewer): round 1 (@8241bd5) claude grok PASS, agy NOT PASS (two static wrapper-literal tests did not follow _decided/_exit_for), codex NOT PASS (R1: a yes/no decision that could not run read as "no"); fixed in541c3feand65b78b6. Round 2 (@65b78b6) 4/4 PASS. Full suite at65b78b6on macOS / Python 3.9 / herdr 0.9.3: 1730 passed / 0 failed / 56 skipped; 18 of the skips were the uninitialized nats-docker submodule in the fresh worktree, and with it tests/test_deploy_freshness.py is 31 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
4.9 KiB
4.9 KiB
리뷰 리포트 — Job f0f131cd (Issue #17 후속 2회차 델타, 8241bd5..65b78b6)
- 리뷰어: planner-reviewer-claude-01 (claude)
- 리뷰 identity: loop-20261007T224638Z-cf1efb / base
56d0bfd/ head65b78b6/ treeb805d9f— 검증 경로(…/2)에서git rev-parse HEAD 'HEAD^{tree}'일치, 미커밋 변경 없음 - 범위: 커밋 2개 — 541c3fe(agy 지적: 동결 리터럴을
_decided/_exit_for로), 65b78b6(codex R1: 실패한 yes/no 판정은 "no" 가 아니다). wrapper.py +24/−4, 래퍼 +18/−4, 테스트 3개. 버전·VERSIONS·문서 변경 없음(브리프대로).
결론 요약
codex R1 이 닫혔고, 그 수정이 1회차에 내가 남긴 N1(stall-watch 판정 실패 시 조용히 감시 생략)도 함께 닫는다. 정상 경로(yes/no 두 분기 모두)의 동작은 불변. 새 테스트 15건이 1회차 head 코드에서 실제로 실패하며, 8건은 codex 가 지적한 바로 그 증상(rc 0)으로 실패한다. 차단 결함 없음.
1. 지적 종결
| 지적 | 수정 | 확인 |
|---|---|---|
codex R1: _decide subscriber-up/stall-watch 를 rc 로 읽어, 판정 실패(rc 1·127, 빈 답)가 "no" 와 구분되지 않음. subscriber-up 에서는 rc 0 으로 끝난 구독자가 "not up" → "died early" 경로 → sub-status 0 = completed → submit 이 0 으로 종료 |
wrapper.py:8-17, :335-340, :344-353 — 두 yes/no 서브커맨드에 --print(yes/no 출력 + rc 0). 래퍼 :121-131 _decided_yes 가 _decided 로 답을 받아 yes/no 외에는 _decision_failed → exit 1. 호출 3곳(:396, :568, :730) 교체 |
1회차 head(8241bd5) 래퍼+wrapper.py 를 넣은 사본에서 test_f_a_failed_yes_no_decision_ends_the_submit_with_1 9건 모두 실패 — subscriber-up 6건은 assert 0 == 1(codex 의 증상 그대로), stall-watch 3건은 오류 문구 없이 정상 종료. 현재 코드에서는 9건 모두 rc 1 + "could not decide" |
claude N1(1회차): stall-watch 판정 실패가 || return 0 으로 조용히 감시만 끔 |
위와 같음 — "no" 는 여전히 return 0(감시 안 함), 실패는 exit 1 |
stall-watch-direct-{error,gone,empty} 통과 |
agy: test_issue5_phase1_delegation·test_tier1_unit 의 동결 리터럴이 옛 _decide sub-status/exit "$(_decide exit-code 문자열을 가리킴 |
_decided job_status sub-status "$sub_rc", _exit_for "$job_status" 로 갱신 |
두 파일 통과(아래) |
2. 정상 경로 불변
- rc 형식은 그대로 남아 있다(
--print없이 subscriber-up 1 → rc 1 확인; test_u_yes_no_print 가 두 형식을 함께 고정). - test_f_yes_and_no_keep_their_branches(direct·loop × up/not-up): "yes" 는 진행, "no" 는 "subscriber died early" 경로로 가고 direct 의 종료 코드는 0/1 — 옛 코드에서도 통과(4 passed)하고 새 코드에서도 통과한다 = 분기 결과 불변. 옛·새 비교 테스트(test_c_*) 전부 통과.
_decided_yes는if …; then(:396, :568)과… || return 0(:730) 안에서 직접 호출되며 서브셸이 아니므로exit 1이 래퍼를 끝낸다.--print를 위치 인자 뒤에 붙여도 argparse 가 받는다(3.9 에서 직접 확인:stall-watch remote:r claude --print→ stderr 진단 + stdoutno, rc 0).stall_watch의 진단 메시지는 전과 같이 stderr 로만 나가 stdout 의 한 줄 답을 더럽히지 않는다.
3. 정적 가드
test_s_no_printed_decision_is_read_raw 가 _decide (subscriber-up|stall-watch) 의 rc 읽기 재유입을 막고 _decided_yes 호출 수(2+1)를 고정한다. 옛 코드에서 실패, 새 코드에서 통과.
4. 이식성
/bin/bash -n(3.2.57) 통과. 추가된 bash 는 case·local·함수 호출뿐. wrapper.py 변경은 argparse store_true 와 print.
5. 테스트 실행 (검증 경로 …/2, 워크트리 .venv Python 3.9.6, env -i 로 HERDR_·MAM_·MQTT_*·WORKSPACE_ROOT 제거)
| 실행 | 결과 |
|---|---|
| test_issue45_restore, test_issue45_restore_unit, test_issue17_item4_decisions, test_issue33_budget, test_issue5_phase1_delegation, test_tier1_unit | 461 passed / 0 failed / 0 skipped (537 s; 실서버 restore 5건 포함, herdr 0.9.3) |
| 새 테스트 19건, 1회차 head 코드 사본 | 15 failed / 4 passed (실패 = yes/no 실패 9 + --print 단위 5 + 정적 가드; 통과 4 = 분기 불변 테스트, 의도대로) |
격리: 실행 전후 herdr 프로세스 차이는 오케스트레이터의 agent wait pid 교체뿐, /private/tmp/m45* 없음, ~/.config/herdr sessions·config.toml 불변, 실제 .mam/jobs 항목 수 불변. |
6. 비차단
- 1회차 N2(
_env가 상속 WORKSPACE_ROOT 를 비우지 않아_run_both4곳이 에이전트 pane 의 워크스페이스로 lib.sh 를 초기화할 수 있음)와 N3(herdr_restore.py docstring 줄바꿈)는 이 델타에서 다루지 않았다. 둘 다 이 PR 이 만든 문제가 아니며 그대로 비차단. - 새 관찰 없음.
[VERDICT: PASS]