리뷰 루프가 리뷰 위임 직전에 죽는다. 그런데 종료 코드는 0 이라 정상 완료처럼 보인다.
File ".../subprocess.py", line 1990, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
OSError: [Errno 7] Argument list too long: 'bash'
로그의 마지막 줄은 Requesting code review from Reviewer '<name>'... 이고 그 뒤에 아무 오류 표시가 없다. 루프가 여러 회차를 도는 경우 1회차는 멀쩡히 끝나고 creator 가 수정 커밋을 올린 뒤 2회차에서 조용히 멈춘다 — diff 가 그 사이에 커졌기 때문이다.
원인
lib_py/loop/runner.py 가 수집한 diff 를 리뷰 프롬프트 문자열에 그대로 넣고(r_prompt = f"... 변경분:\n{changes_diff}"), 그 프롬프트를 bash 에 단일 argv 원소로 넘긴다. 리눅스의 단일 인자 상한 MAX_ARG_STRLEN 은 128 KiB = 131,072 바이트로 고정이며 ARG_MAX(여기서는 2,097,152) 와 별개다.
수집기 diff_collect.sh 는 자체 상한을 가지고 있지만 그 값이 커널 상한보다 높다.
getconf ARG_MAX # 2097152 — 이 값이 아니다# MAX_ARG_STRLEN = 32 * PAGE_SIZE = 131072, 커널 상수
영향
커밋 10여 개 규모의 브랜치는 리뷰 루프로 리뷰할 수 없다. 이 저장소에서 실제로 도달한 크기다.
실패가 성공처럼 보인다. 종료 코드 0 에 오류 요약도 없어서, 리뷰 판정이 하나도 수집되지 않았는데 루프가 끝난 것으로 오인하기 쉽다.
같은 경로를 쓰는 self-review 와 원격 diff-only 리뷰도 같은 상한에 걸린다.
수정 방향
프롬프트를 argv 로 넘기지 않는다. stdin 이나 임시 파일로 전달한다. 상한 자체가 사라지므로 이게 근본 수정이다.
그게 어렵다면 최소한 수집기 상한을 커널 상한 아래로 내리고(MAM_DIFF_MAX_BYTES 기본값을 131072 미만으로), 잘림 표시가 붙은 diff 로 리뷰를 위임할지 거부할지 명시적으로 결정한다. 다만 잘린 diff 는 증거로서 약하므로 1번이 낫다.
어느 쪽이든 이 실패가 종료 코드 0 으로 끝나지 않게 한다. 위임이 실패했는데 루프가 성공처럼 끝나는 것이 가장 위험한 부분이다.
우회
리뷰 범위를 델타로 줄인다. --base <직전에 리뷰된 head> 로 브랜치 전체가 아니라 그 이후 변경만 리뷰한다. PR #35 의 4·5차 리뷰를 이 방법으로 수행했다.
## 증상
리뷰 루프가 리뷰 위임 직전에 죽는다. 그런데 종료 코드는 0 이라 정상 완료처럼 보인다.
```
File ".../subprocess.py", line 1990, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
OSError: [Errno 7] Argument list too long: 'bash'
```
로그의 마지막 줄은 `Requesting code review from Reviewer '<name>'...` 이고 그 뒤에 아무 오류 표시가 없다. 루프가 여러 회차를 도는 경우 1회차는 멀쩡히 끝나고 creator 가 수정 커밋을 올린 뒤 2회차에서 조용히 멈춘다 — diff 가 그 사이에 커졌기 때문이다.
## 원인
`lib_py/loop/runner.py` 가 수집한 diff 를 리뷰 프롬프트 문자열에 그대로 넣고(`r_prompt = f"... 변경분:\n{changes_diff}"`), 그 프롬프트를 `bash` 에 **단일 argv 원소**로 넘긴다. 리눅스의 단일 인자 상한 `MAX_ARG_STRLEN` 은 128 KiB = 131,072 바이트로 고정이며 `ARG_MAX`(여기서는 2,097,152) 와 별개다.
수집기 `diff_collect.sh` 는 자체 상한을 가지고 있지만 그 값이 커널 상한보다 높다.
```
MAM_DIFF_MAX_BYTES="${MAM_DIFF_MAX_BYTES:-200000}"
MAM_DIFF_MAX_LINES="${MAM_DIFF_MAX_LINES:-4000}"
```
그래서 131,072 에서 200,000 사이의 diff 는 수집기의 잘림 검사를 통과한 뒤 위임 단계에서 죽는다. 이 구간이 사각지대다.
## 재현
2026-09-22, 브랜치 `fix/issue-23-24`, 리뷰어 5명 `--review-only` 루프.
| 범위 | `git diff \| wc -c` | 결과 |
|---|---:|---|
| `1e78dae..4805fd4` | 115,643 | 정상 위임, 5명 판정 수신 |
| `1e78dae..087eaca` | 133,235 | `OSError: [Errno 7]`, 루프 중단, 종료 코드 0 |
```sh
getconf ARG_MAX # 2097152 — 이 값이 아니다
# MAX_ARG_STRLEN = 32 * PAGE_SIZE = 131072, 커널 상수
```
## 영향
- 커밋 10여 개 규모의 브랜치는 리뷰 루프로 리뷰할 수 없다. 이 저장소에서 실제로 도달한 크기다.
- 실패가 성공처럼 보인다. 종료 코드 0 에 오류 요약도 없어서, 리뷰 판정이 하나도 수집되지 않았는데 루프가 끝난 것으로 오인하기 쉽다.
- 같은 경로를 쓰는 self-review 와 원격 diff-only 리뷰도 같은 상한에 걸린다.
## 수정 방향
1. **프롬프트를 argv 로 넘기지 않는다.** stdin 이나 임시 파일로 전달한다. 상한 자체가 사라지므로 이게 근본 수정이다.
2. 그게 어렵다면 최소한 **수집기 상한을 커널 상한 아래로** 내리고(`MAM_DIFF_MAX_BYTES` 기본값을 131072 미만으로), 잘림 표시가 붙은 diff 로 리뷰를 위임할지 거부할지 명시적으로 결정한다. 다만 잘린 diff 는 증거로서 약하므로 1번이 낫다.
3. 어느 쪽이든 **이 실패가 종료 코드 0 으로 끝나지 않게** 한다. 위임이 실패했는데 루프가 성공처럼 끝나는 것이 가장 위험한 부분이다.
## 우회
리뷰 범위를 델타로 줄인다. `--base <직전에 리뷰된 head>` 로 브랜치 전체가 아니라 그 이후 변경만 리뷰한다. PR #35 의 4·5차 리뷰를 이 방법으로 수행했다.
원인. 러너가 수집한 diff 를 리뷰 프롬프트에 그대로 넣고, 그 프롬프트를 bash 에 단일 argv 원소로 넘겼다. 리눅스의 단일 인자 상한은 131,072 바이트인데 수집기 상한은 200,000 바이트라, 그 사이 크기가 걸러지지 않은 채 exec 단계에서 죽었다.
수정. 프롬프트가 지나는 경로 전체에서 긴 본문을 argv 로 넘기지 않는다. 파이썬 러너, delegate-job 래퍼, 등록부, 옛 셸 러너(MAM_LOOP_PY=0), lib.sh 제출 도우미가 모두 --prompt-file 로 넘긴다. 에이전트 pane 에는 brief 파일 경로만 담은 짧은 안내문이 주입된다. create_session 도 이제 같은 방식이다. 기존 --prompt 는 그대로 받는다.
종료 코드. 래퍼를 실행할 수 없거나 잡 등록이 실패하면 루프는 원인을 남기고 종료 코드 1 로 끝난다. 판정을 하나도 수집하지 못한 루프가 성공처럼 보이는 일은 없다. 이번 작업의 델타 리뷰 라운드에서 codex 가 NOT PASS 를 냈을 때, 루프가 실제로 종료 코드 1 로 끝나는 것을 확인했다.
수집기 상한.MAM_DIFF_MAX_BYTES 는 이제 리뷰어에게 보여줄 증거의 크기만 제한한다.
검증
항목
결과
전체 스위트 59be1c8
1111 통과 / 12 실패 / 19 건너뜀
리뷰어 5명
전원 PASS
이슈 테스트
22 통과
실패 12건은 이 호스트의 기존 기준선 실패다. 새 테스트는 131,072 바이트를 넘는 실제 프롬프트로 실제 exec 경로를 통과시키며, 수정 전 코드에서는 13건 중 11건이 실패한다.
리뷰 도중 codex 가 strace 로 실제 경로를 추적해, create_session 이 주입 안내문에 본문을 다시 붙여 execve 가 E2BIG 로 실패하는 경로를 찾아냈다. 이 지적까지 고친 뒤 codex 가 제출한 재현 자산을 수정 없이 돌려 통과를 확인했다.
PR #37 로 해결했다. 브랜치 `fix/issue-36`, head `59be1c8`.
**원인.** 러너가 수집한 diff 를 리뷰 프롬프트에 그대로 넣고, 그 프롬프트를 `bash` 에 단일 argv 원소로 넘겼다. 리눅스의 단일 인자 상한은 131,072 바이트인데 수집기 상한은 200,000 바이트라, 그 사이 크기가 걸러지지 않은 채 exec 단계에서 죽었다.
**수정.** 프롬프트가 지나는 경로 전체에서 긴 본문을 argv 로 넘기지 않는다. 파이썬 러너, delegate-job 래퍼, 등록부, 옛 셸 러너(`MAM_LOOP_PY=0`), lib.sh 제출 도우미가 모두 `--prompt-file` 로 넘긴다. 에이전트 pane 에는 brief 파일 경로만 담은 짧은 안내문이 주입된다. create_session 도 이제 같은 방식이다. 기존 `--prompt` 는 그대로 받는다.
**종료 코드.** 래퍼를 실행할 수 없거나 잡 등록이 실패하면 루프는 원인을 남기고 종료 코드 1 로 끝난다. 판정을 하나도 수집하지 못한 루프가 성공처럼 보이는 일은 없다. 이번 작업의 델타 리뷰 라운드에서 codex 가 NOT PASS 를 냈을 때, 루프가 실제로 종료 코드 1 로 끝나는 것을 확인했다.
**수집기 상한.** `MAM_DIFF_MAX_BYTES` 는 이제 리뷰어에게 보여줄 증거의 크기만 제한한다.
## 검증
| 항목 | 결과 |
|---|---|
| 전체 스위트 `59be1c8` | 1111 통과 / 12 실패 / 19 건너뜀 |
| 리뷰어 5명 | 전원 PASS |
| 이슈 테스트 | 22 통과 |
실패 12건은 이 호스트의 기존 기준선 실패다. 새 테스트는 131,072 바이트를 넘는 실제 프롬프트로 실제 exec 경로를 통과시키며, 수정 전 코드에서는 13건 중 11건이 실패한다.
리뷰 도중 codex 가 `strace` 로 실제 경로를 추적해, create_session 이 주입 안내문에 본문을 다시 붙여 `execve` 가 `E2BIG` 로 실패하는 경로를 찾아냈다. 이 지적까지 고친 뒤 codex 가 제출한 재현 자산을 수정 없이 돌려 통과를 확인했다.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
증상
리뷰 루프가 리뷰 위임 직전에 죽는다. 그런데 종료 코드는 0 이라 정상 완료처럼 보인다.
로그의 마지막 줄은
Requesting code review from Reviewer '<name>'...이고 그 뒤에 아무 오류 표시가 없다. 루프가 여러 회차를 도는 경우 1회차는 멀쩡히 끝나고 creator 가 수정 커밋을 올린 뒤 2회차에서 조용히 멈춘다 — diff 가 그 사이에 커졌기 때문이다.원인
lib_py/loop/runner.py가 수집한 diff 를 리뷰 프롬프트 문자열에 그대로 넣고(r_prompt = f"... 변경분:\n{changes_diff}"), 그 프롬프트를bash에 단일 argv 원소로 넘긴다. 리눅스의 단일 인자 상한MAX_ARG_STRLEN은 128 KiB = 131,072 바이트로 고정이며ARG_MAX(여기서는 2,097,152) 와 별개다.수집기
diff_collect.sh는 자체 상한을 가지고 있지만 그 값이 커널 상한보다 높다.그래서 131,072 에서 200,000 사이의 diff 는 수집기의 잘림 검사를 통과한 뒤 위임 단계에서 죽는다. 이 구간이 사각지대다.
재현
2026-09-22, 브랜치
fix/issue-23-24, 리뷰어 5명--review-only루프.git diff | wc -c1e78dae..4805fd41e78dae..087eacaOSError: [Errno 7], 루프 중단, 종료 코드 0영향
수정 방향
MAM_DIFF_MAX_BYTES기본값을 131072 미만으로), 잘림 표시가 붙은 diff 로 리뷰를 위임할지 거부할지 명시적으로 결정한다. 다만 잘린 diff 는 증거로서 약하므로 1번이 낫다.우회
리뷰 범위를 델타로 줄인다.
--base <직전에 리뷰된 head>로 브랜치 전체가 아니라 그 이후 변경만 리뷰한다. PR #35 의 4·5차 리뷰를 이 방법으로 수행했다.PR #37 로 해결했다. 브랜치
fix/issue-36, head59be1c8.원인. 러너가 수집한 diff 를 리뷰 프롬프트에 그대로 넣고, 그 프롬프트를
bash에 단일 argv 원소로 넘겼다. 리눅스의 단일 인자 상한은 131,072 바이트인데 수집기 상한은 200,000 바이트라, 그 사이 크기가 걸러지지 않은 채 exec 단계에서 죽었다.수정. 프롬프트가 지나는 경로 전체에서 긴 본문을 argv 로 넘기지 않는다. 파이썬 러너, delegate-job 래퍼, 등록부, 옛 셸 러너(
MAM_LOOP_PY=0), lib.sh 제출 도우미가 모두--prompt-file로 넘긴다. 에이전트 pane 에는 brief 파일 경로만 담은 짧은 안내문이 주입된다. create_session 도 이제 같은 방식이다. 기존--prompt는 그대로 받는다.종료 코드. 래퍼를 실행할 수 없거나 잡 등록이 실패하면 루프는 원인을 남기고 종료 코드 1 로 끝난다. 판정을 하나도 수집하지 못한 루프가 성공처럼 보이는 일은 없다. 이번 작업의 델타 리뷰 라운드에서 codex 가 NOT PASS 를 냈을 때, 루프가 실제로 종료 코드 1 로 끝나는 것을 확인했다.
수집기 상한.
MAM_DIFF_MAX_BYTES는 이제 리뷰어에게 보여줄 증거의 크기만 제한한다.검증
59be1c8실패 12건은 이 호스트의 기존 기준선 실패다. 새 테스트는 131,072 바이트를 넘는 실제 프롬프트로 실제 exec 경로를 통과시키며, 수정 전 코드에서는 13건 중 11건이 실패한다.
리뷰 도중 codex 가
strace로 실제 경로를 추적해, create_session 이 주입 안내문에 본문을 다시 붙여execve가E2BIG로 실패하는 경로를 찾아냈다. 이 지적까지 고친 뒤 codex 가 제출한 재현 자산을 수정 없이 돌려 통과를 확인했다.