Files
multi-agent-mux/.agents/reports/planner-reviewer-codex-01/report-2e264c0c.md
T
GodopuandClaude Fable 5.1 c3e6a7e96d release(v4.7.3): bump framework and eight skills to 4.7.3 in lockstep; archive PR #37 merge-side reviews
Issue #36: delegated prompts travel by file, a delegation that fails ends
the loop with rc 1. VERSIONS.md carries the entry; the merge-side reports
(round 1 @59be1c8 and round 2 @708262a with identity sidecars, base
59be1c8 -> head 708262a, 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-24 18:10:28 +09:00

9.1 KiB
Raw Blame History

PR #37 독립 리뷰

  • Job: 2e264c0c; reviewer: planner-reviewer-codex-01
  • Loop: loop-20260924T080014Z-2b3df0
  • Base: 1debc7d92444385b9200e821efa0ff0f6d82023e
  • Head: 59be1c89209a232059aec0c5aeecc2329929cb49
  • Tree: a009c9062d9818f343ac57e6c72cda932887fab7
  • 검증 경로: /var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20260924T080014Z-2b3df0/1

파일 전달의 정상 경로와 기존 계약은 검증됐지만, Python 러너의 파일 쓰기 실패 정리 누락과 새 테스트의 macOS 실패가 남아 있다. 아래 두 항목을 수정한 뒤 재검토가 필요하다. 계획된 4.7.3 버전 변경과 별개의 지적이다.

R1 — P2: Python 러너의 임시 파일 쓰기 실패가 정리 범위 밖에 있음

근거: .agents/skills/lib_py/loop/runner.py:472–:477, :490–:502.

NamedTemporaryFile(delete=False) 생성과 fh.write() 및 context manager 종료 시 flush/close가 try/except/finally보다 먼저 실행된다. 따라서 쓰기 또는 flush가 실패하면 OSError가 delegate_job() 밖으로 전파되고, 뒤의 os.unlink(prompt_file)에 도달하지 않는다. 특히 경로 변수도 쓰기 다음에 지정되어 있어 단순히 try 위치만 바꾸는 것으로는 충분하지 않다. 새 S9는 셸 러너의 부분 파일 정리만 검사하므로 이 Python 경로를 검출하지 못한다.

재현 절차:

  1. 독립 Python subprocess에서 WORKSPACE_ROOT, HOME, 로그 경로를 새 /tmp 샌드박스로 설정하고, 검증 트리의 LoopRunner(['--creator', 'c', '--task', 't'])를 생성한다.
  2. 그 subprocess에서만 signal.signal(signal.SIGXFSZ, signal.SIG_IGN)과 resource.setrlimit(resource.RLIMIT_FSIZE, (1024, 1024))를 적용한다.
  3. runner.delegate_job('c', 'claude', 'Worker', 'x' * 20000)를 호출하고 예외와 .mam/prompt-*를 확인한다. 실제 wrapper/에이전트가 실행되기 전에 파일 쓰기에서 실패한다.

기대: 원인을 기록하는 정상적인 제출 실패 반환과 임시 파일 정리.

실제: [Errno 27] File too large가 처리되지 않은 OSError로 전파됐고, .mam/prompt-loop-…md가 1,024바이트, 권한 0600으로 남았다. 실제 LoopRunner.delegate_job()으로 재현했으며, 오류를 흉내 내는 subprocess mock은 사용하지 않았다. 생성/쓰기 실패를 처리해야 한다는 이번 브리프의 요구를 충족하지 못한다.

수정 방향: 임시 파일 경로를 미리 초기화하고, 디렉터리 생성·파일 생성·쓰기·close·wrapper 실행을 정리 가능한 하나의 try/finally 범위에 둔다. 생성된 파일의 이름은 write() 전에 저장한다. 파일 준비 실패도 원인이 드러나는 실패 제출 결과로 돌려주고, 첫 시도와 busy 재시도에서 같은 규칙을 적용한다. 메시지는 wrapper 실행 실패와 파일 준비 실패를 구분하는 것이 정확하다.

대안 검증: 검증 트리에서 추출한 동일 _run_cmd를 임시 harness에서만 변경해, 위 작업들을 기존 예외/정리 범위 안으로 옮기고 경로를 쓰기 전에 저장했다. 같은 실제 파일 크기 제한에서 원본은 예외와 1,024바이트 파일을 남겼지만, 후보는 rc 126 실패 결과와 빈 leftovers 목록을 반환했다. 제품 코드는 수정하지 않았다. 이 상황을 Python 러너의 회귀 테스트에도 추가해야 한다.

R2 — P2: 새 S9가 macOS에서 플랫폼별 오류 문구 때문에 실패함

근거: tests/test_issue36_prompt_transport.py:602.

재현: 브리프의 인터프리터와 격리 옵션으로 tests/test_issue36_prompt_transport.py::test_s9_shell_loop_removes_a_partial_prompt_file_and_says_why를 실행한다.

기대: 실제 RLIMIT_FSIZE 쓰기 실패, 비정상 종료, 부분 파일 제거, 프로젝트가 출력하는 원인 메시지를 검증하며 지원 플랫폼에서 통과해야 한다.

실제: 전체 선택 실행과 S9 단독 재실행 모두 assert "File too large" in result.stderr에서 실패했다. 이 macOS 실행 환경의 bash stderr에는 그 문구가 없었다. 프로젝트 자체의 delegate_job_safe: cannot write the prompt file ... 메시지는 출력됐다.

수정 방향 및 검증: 플랫폼별 bash/OS 오류 문자열을 필수 계약으로 삼지 말고, 비정상 반환 코드·부분 파일 없음·프로젝트의 고유 오류 메시지를 유지해서 검사한다. 임시 namespace에서 해당 영어 문자열 assertion 한 줄만 제외한 동일 S9를 새 샌드박스에 실행했으며, 나머지 실제 쓰기 실패와 정리/진단 assertion은 모두 통과했다. 테스트 전체를 skip하거나 정리 assertion을 제거할 필요는 없다.

나머지 검토 결과

  • 전달 경로: --prompt 잔여 호출자를 직접 검색했다. 옛 셸 러너의 11개 내부 호출은 shell 함수 delegate_job_safe로 들어가며, 이 함수가 exec 전에 파일로 변환한다. Python 러너, lib.sh의 Python/legacy 제출 도우미, delegation CLI, wrapper의 registry register/update 경로는 파일 전달을 사용한다. 명시적 --prompt CLI 호환 경로는 남아 있다.
  • 경로/크기: 실제 이 호스트의 SC_ARG_MAX보다 큰 1,114,112바이트 프롬프트를 argv로 전달하면 OSError(errno=7)가 발생했다. 같은 내용은 상대경로의 공백 포함 파일명, 다른 --workdir, 공백 포함 registry 경로 조합으로 실제 wrapper/registry를 거쳐 원문 그대로 등록됐다. Linux 단일 인자 131,072바이트 상한을 macOS에서 재현했다고 주장하는 것이 아니다.
  • 파일 읽기 시점: wrapper가 registry를 동기 실행하고 registry가 그 호출 중 파일 내용을 읽으므로, 호출자 쪽 정상 반환 후 임시 파일 삭제와 충돌하지 않는다. 정상 완료/exec OSError의 정리 테스트는 통과했다. R1은 그보다 앞선 쓰기 실패에 한정된다.
  • 실패 처리: exec 실패와 busy 재시도의 rc 126 처리, 재심/중재/순응 미등록 시 비정상 종료 테스트가 통과했다. 별도 실제 루프 subprocess에서 fake agent가 순응 잡을 등록한 뒤 error 상태로 종료하도록 했을 때도 rc 1로 끝났고 다음 리뷰 라운드/성공 승격으로 진행하지 않았다. 구현·일반 수정 잡의 실패를 중단하는 기존 정책과 일관된 강화다. 다만 이때도 “Failed to register Creator compliance job”이라고 출력하는 문구는 등록 실패와 실행 실패를 구분하지 못한다(비차단 진단 개선 사항).
  • diff 제한: 수집기의 바이트/줄 제한값은 바뀌지 않았고 증거 절단 표시도 유지된다. L15는 잘린 증거를 받는 diff-only 원격 리뷰어를 거부하고 로컬 리뷰는 허용하는 기존 규칙을 검증했다.
  • create 온보딩: S7 계열과 새 S8이 통과했다. brief는 job registry 아래 절대경로로 만들어지고, 주입문은 과제 본문 대신 그 경로와 기존 dispatch-token/started 명령을 포함한다. 임시 제출 파일과 에이전트가 이후 읽을 영속 brief는 구분된다.
  • 하위 호환: registry CLI 계약 스냅샷, shim help 캡처, loop CLI, worktree 문맥, Issue #14 acknowledgement 선택 테스트가 통과했다. --prompt는 계속 사용할 수 있다. 대표 역검증으로 샌드박스의 wrapper만 base 버전으로 교체하면 새 S1은 unknown option: --prompt-file로 실패했다. PR이 언급한 13건 전체의 과거 버전 실패 수를 재측정한 것은 아니다.
  • 문서: delegate-job skill은 파일 옵션과 Linux argv 제한을 설명하고, loop skill은 위임 실패의 비정상 종료 및 pipefail 사용을 설명한다. 검증 트리에 .claude/skills 배포 사본은 없었다. 4.7.3 락스텝 bump는 브리프대로 오케스트레이터 후속 작업이다.

실행 증거

모든 pytest 실행은 검증 경로에서 브리프의 절대 Python 경로를 AGENT_PYTHON_BIN과 실행 명령에 함께 지정하고, PYTHONDONTWRITEBYTECODE=1, -p no:cacheprovider, 검증 경로 밖의 고유 --basetemp를 사용했다.

  1. test_issue36_prompt_transport.py, test_issue5_phase1_delegation.py, test_loop_cli.py: 95 passed, 1 failed, 1 skipped — 91.15초. 실패는 R2의 S9이며, skip은 Linux 전용 MAX_ARG_STRLEN 음성 대조군이다.
  2. test_issue23_24_startup_path.py, test_issue21_loop_b.py, test_issue21_worktree_prereqs.py, test_issue14_delivery_ack.py에서 s7 or l15 or t2_5 or wrapper_honours_registry_ack 선택: 19 passed, 132 deselected — 63.39초.
  3. S9 단독 확인: 동일 문구 assertion에서 실패. 위 결과에 중복 합산하지 않았다.
  4. git diff --check, 변경 셸 파일 5개의 개별 bash -n, 변경 Python 파일 4개의 AST 구문 검사 통과.

HEAD/tree가 브리프와 일치했고 검증 후 git 작업 트리도 깨끗했다. 제품 코드, creator 워크트리, 실제 herdr 서버/에이전트 세션을 수정하지 않았다. 제안 검증과 역검증은 임시 사본/독립 subprocess에 한정했다. 두 지적은 국소 수정으로 해결 가능하므로 재설계 에스컬레이션은 필요하지 않다.

[VERDICT: NOT PASS]