PR #48 already carries the 4.8.5 lockstep bump. Merge-workspace review: round 1 (@35935f9) claude agy grok NOT PASS (test portability: tomllib on Python 3.9, /proc and /tmp on macOS), codex no verdict (model at capacity, budget spent); fixed in36f0b8dande8d9151. Round 2 (@e8d9151) codex NOT PASS (unrunnable config check passed an unchecked merge, odd pids closed a pane, nan dropped the user's settings); fixed inab93776. Round 3 (@ab93776) 4/4 PASS. Full suite atab93776on macOS / Python 3.9: 1456 passed / 0 failed / 38 skipped. Reports and the round-3 identity sidecars go to .agents/reports/<session>/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
124 lines
12 KiB
Markdown
124 lines
12 KiB
Markdown
# PR #48 — Codex review, job 714c5189
|
||
|
||
## 판정: NOT PASS
|
||
|
||
테스트 이식성 수정은 확인했다. 다만 현재 제품 코드에서 아래 R1–R3을 재현했다. 기존 스위트가 통과해도 설정 검증 실패 시 복원이 다시 켜질 수 있고, 비정상 프로세스 응답에서 pane을 닫으며, 유효한 NaN 설정에서 다른 설정을 유실한다. 국소 수정과 회귀 테스트로 해결 가능한 범위이므로 재계획 escalation은 필요하지 않다.
|
||
|
||
## 검증 identity 및 범위
|
||
|
||
- loop: `loop-20261005T081844Z-e92124`
|
||
- base: `c5c7eff6f758c76ac37a138dbc3c301f3f7e10cf`
|
||
- head: `e8d9151226d8139c7b5688e83d035a3a747aeb2d`
|
||
- tree: `8b39188453ff8a7263aa4400792fabc99188dad6`
|
||
- 검증 cwd: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T081844Z-e92124/1`
|
||
- Codex는 PR 전체 `c5c7eff..e8d9151`을 검토했다. 두 후속 커밋은 테스트와 문서만 변경했으며 R1–R3 제품 코드는 이전 head와 동일하다.
|
||
- 검증 전후 HEAD/tree 동일, `git status --porcelain` 출력 없음. 제품 코드·테스트·문서는 수정하지 않았다.
|
||
|
||
## 차단 사항
|
||
|
||
### R1 — P1: config check 실행 실패를 성공으로 반환해 스키마 오류 파일을 서버에 넘긴다
|
||
|
||
**근거:** `.agents/skills/lib_py/herdr_config.py:235–237`은 `OSError` 또는 `subprocess.SubprocessError`를 받으면 경고 후 `True`를 반환한다. 호출부 `:265–266`은 해당 파일 경로를 성공 결과로 반환하고, `.agents/skills/lib.sh:255–256`은 그 경로를 서버의 `HERDR_CONFIG_PATH`로 설정한다. `VERSIONS.md:49`의 “config check를 통과해야 한다” 계약과 다르다.
|
||
|
||
**재현:**
|
||
|
||
1. Python 3.11에서 임시 원본 파일에 다음을 쓴다.
|
||
```toml
|
||
[ui]
|
||
confirm_close = "invalid-bool"
|
||
```
|
||
2. `herdr_config.herdr_rejects`가 `subprocess.TimeoutExpired(["herdr", "config", "check"], 30)`를 던지도록 주입하고 `write_server_config({"HERDR_CONFIG_PATH": source}, herdr="/review/fake-herdr")`를 호출한다.
|
||
3. 반환된 경로의 내용을 확인한다. 별도로 버리는 HOME/XDG/세션에서 실제 herdr의 `config check`로 그 내용을 검사한다.
|
||
|
||
**기대:** 검증하지 못한 병합 파일을 “사용 가능한 설정”으로 반환하지 않아야 한다. 최소 설정으로의 안전한 전환을 시도하고, 그것도 검증 불가하면 실패를 명시해야 한다.
|
||
|
||
**실제:** 유효한 경로와 “the config is unchecked” 경고를 반환하며, 파일에는 `confirm_close = "invalid-bool"`이 남는다. 동일 내용의 실제 herdr 검사는 exit 1, `invalid type ... expected a boolean; using defaults`를 반환했다. 이 파일로 서버가 시작하면 파일 전체가 거부되어 `resume_agents_on_restore=false`도 적용되지 않는다. 검사 실행 timeout을 주입한 재현이며, 실제 운영 서버에서 timeout을 유발하지 않았다.
|
||
|
||
**수정 방향:** 예외를 성공으로 취급하지 말고 명시적 검사 실패로 처리한다. 최소 설정의 검증에도 실패하면 경로를 반환하지 않도록 하고, 호출부의 기존 실패 경고와 연결한다. timeout/OSError 회귀 검사를 추가한다. `:178`의 알 수 없는 nonzero 종료도 무조건 허용하지 말고, 파일 전체를 버리지 않는 것으로 확인된 경고와 검사 자체 실패를 구별하는 편이 안전하다.
|
||
|
||
### R2 — P2: 비정상 PID 값의 단순 동등 비교로 pane 닫기를 허용한다
|
||
|
||
**근거:** `.agents/skills/lib_py/herdr_restore.py:44–47`은 `shell_pid is not None` 및 전경 그룹과의 동등 여부만 검사한다. `:63–65`는 이 결과만으로 `pane close`를 호출한다.
|
||
|
||
**재현:**
|
||
|
||
1. `_herdr`를 stub으로 바꾸고 `pane list`가 아래 pane을 반환하게 한다.
|
||
```python
|
||
{"pane_id": "probe-pane", "agent": None,
|
||
"agent_session": {"value": "probe-id"}}
|
||
```
|
||
2. `pane process-info`가 `{"process_info": {"shell_pid": 0, "foreground_process_group_id": 0}}`을 반환하게 한다.
|
||
3. `close_restored_shells("probe-id", session="m45-review-probe")`를 호출하고 stub 호출 이력을 검사한다. 같은 검사를 `False`, 빈 문자열, `-1`로 반복한다.
|
||
|
||
**기대:** 유효한 셸 PID/전경 프로세스 그룹인지 입증하지 못한 응답은 pane을 그대로 두어야 한다.
|
||
|
||
**실제:** 네 경우 모두 `("pane", "close", "probe-pane")`를 호출하고 `["probe-pane"]`를 반환했다. 실제 pane은 닫지 않았으며 응답 주입으로 닫기 요청을 확인했다. 현재 실제 herdr가 이 값을 반환한다고 주장하는 것은 아니다. 브리프가 명시한 “응답이 이상할 때 fail-safe” 조건을 위반하는 재현이다.
|
||
|
||
**수정 방향:** 두 값이 bool이 아닌 양의 정수인지 검사한 뒤 동등 여부를 비교한다. 누락·null·0·음수·문자열·bool 응답에서 닫기 요청이 없는지 회귀 검사한다.
|
||
|
||
### R3 — P2: 유효한 TOML NaN을 불일치로 판단해 무관한 사용자 설정을 버린다
|
||
|
||
**근거:** `.agents/skills/lib_py/herdr_config.py:159`의 일반 dict 동등 비교에서 재파싱된 `nan`은 원래 `nan`과 같지 않다. 따라서 `:162–163`이 전체 설정을 MINIMAL로 교체한다. `:112–114`에는 NaN 직렬화 지원까지 있지만 동등 검사가 이를 거부한다.
|
||
|
||
**재현:**
|
||
|
||
```python
|
||
body, warnings = herdr_config.server_config_text(
|
||
"[ui]\nconfirm_close = false\n[custom]\nvalue = nan\n"
|
||
)
|
||
```
|
||
|
||
Python 3.11에서 `tomllib.loads(body)`의 `ui.confirm_close`를 확인한다.
|
||
|
||
**기대:** 원본 설정과 복원 키만 false로 바꾼 결과가 유지되어야 한다. NaN 값의 의미상 동등성도 처리해야 한다.
|
||
|
||
**실제:** 출력은 `[session]\nresume_agents_on_restore = false\n`뿐이다. `ui.confirm_close=false`가 사라지고 “could not rewrite ... faithfully” 경고가 나온다. 버리는 환경의 실제 herdr 검사에서 원본은 unknown `[custom]` 섹션 경고만 냈으며 `parse error/using defaults`는 없었다. 즉 추가 섹션을 무시하는 것과 정상 UI 설정까지 버리는 것은 다르다. Python 3.11에서도 발생하므로 사용자가 승인한 Python 3.9 최소 설정 폴백과 별개의 문제다.
|
||
|
||
**수정 방향:** 중첩 dict/list의 구조 및 값 비교 시 두 NaN을 의미상 동등하게 취급한다. 최상위·중첩·배열 NaN을 포함해 무관한 유효 설정이 유지되는 회귀 검사를 추가한다.
|
||
|
||
## 실행 결과
|
||
|
||
| 검증 | 결과 |
|
||
| --- | --- |
|
||
| 지정 Python 3.9, 브리프의 6개 파일 + `tests/test_issue10_13_purge.py` | **175 passed, 30 skipped**, 226.24초 |
|
||
| 위 결과 중 purge 파일 | **23 passed**; 실패 없음 |
|
||
| Python 3.11, `tests/test_issue45_herdr_config.py` | **51 passed**, 0.42초 |
|
||
| 현재 head의 독립 경계 탐침: 검사 timeout 1 + 비정상 PID 4 + NaN 1 | **6 failed**, 0.03초; R1–R3 재현 |
|
||
| `git diff --check c5c7eff..HEAD` | 통과 |
|
||
| 변경 bash 두 파일 `bash -n`, 신규 Python 두 모듈 AST parse | 통과 |
|
||
| 검증 후 작업 트리 / 실제 테스트 서버 정리 | clean / 해당 runner의 서버·세션 잔류 없음 |
|
||
|
||
30 skips 중 29건은 tomllib 미설치 환경에서 의도된 설정 병합 검사 skip이다. 나머지 1건은 기존 checkout 의존 검사다. 따라서 Python 3.9 결과만으로 3.11 병합 기능이 검증됐다고 간주하지 않았고, 3.11의 51건을 따로 실행했다.
|
||
|
||
- 선택 테스트 로그: `/tmp/714c5189-selected.log`
|
||
- Python 3.11 설정 로그: `/tmp/714c5189-config311.log`
|
||
- 독립 탐침: `/tmp/714c5189_extra_probes.py`
|
||
- 독립 탐침 로그: `/tmp/714c5189-probes.log`
|
||
|
||
독립 탐침 재실행(위 검증 cwd에서):
|
||
```sh
|
||
PYTHONDONTWRITEBYTECODE=1 /tmp/270e8aaa-py311/bin/python -m pytest -p no:cacheprovider -q --basetemp=/tmp/714c5189-probes-rerun /tmp/714c5189_extra_probes.py
|
||
```
|
||
이 탐침은 실제 herdr를 실행하거나 실제 pane을 닫지 않는다. 임시 파일과 stub만 사용한다.
|
||
|
||
## 브리프 1–7 확인
|
||
|
||
1. **병합/충돌:** 기본 TOML 표기, 기존 session 키, 원본 없음·깨짐, 생성 파일의 원본 추적, 원본/외부 파일 충돌 및 폴백 검사는 3.11 스위트에서 통과했다. 원본 파일은 유지되고 생성 파일이 원본 옆에 남는다. 충돌 시 원본별 해시 이름, 쓰기 불가 시 .mam 폴백과 상대 경로 의미 변경 경고가 있다. 단 R1/R3 때문에 전체 무손실·검사 계약은 통과로 볼 수 없다.
|
||
2. **기동 경로:** `lib.sh:193–212,241–261`의 named-session headless bootstrap에 적용된다. create/resume도 이 shim을 이용한다. default 서버, MAM_NO_AUTOSTART 조회 경로, 이미 실행 중인 서버, 사용자가 직접 attach로 띄운 서버에는 새 설정을 적용하지 않는다. create SKILL.md:119–120 및 VERSIONS.md:50의 다음 기동/직접 기동 예외 설명과 부합한다. 이미 떠 있는 서버를 재설정하는 기능은 추가하지 않았다.
|
||
3. **pane 정리:** 정상 응답의 다른 session id, 사용자 pane, live agent, 셸 이외 전경 프로세스 보호와 create→crash→resume 정리는 실서버 테스트에서 통과했다. 다만 비정상 응답은 R2처럼 닫히므로 fail-safe 항목은 미충족이다.
|
||
4. **실서버 테스트/격리:** 후속 수정의 resolve된 임시 HOME 및 macOS ps 환경 조회로 두 A 테스트와 B 테스트가 통과했다. A의 마지막 양성 대조군도 실행돼 일반 서버에서 같은 저장 레이아웃의 agent resume을 확인했다. 실행 wrapper에서 HERDR_*를 모두 제거하고 버리는 HOME/XDG_CONFIG_HOME을 사용했다. 실제 테스트는 uuid4 기반 `mamt45-66197-*` 세션만 사용했으며, 종료 후 해당 서버 프로세스 및 세션을 담은 임시 루트가 없음을 확인했다. 사용자 서버/설정/소켓 및 .mam.env를 사용하거나 변경하지 않았다.
|
||
5. **문서/버전/#42:** v4.8.5 bump는 승인 범위이며 일관성 검사를 통과했다. Python 3.9에서 다른 설정을 보존하지 않는 동작은 새 문서에 명시됐고 사용자의 문서화 결정을 차단 사유로 삼지 않았다. 인벤토리는 create 문서의 start/end 줄 번호와 removed 항목의 근거 줄 번호만 변경됐고 class/hash 재분류는 없다. 검사 통과 필수라는 문서와 R1 코드의 불일치는 수정해야 한다.
|
||
6. **purge 12건 기준 실패:** 지정 Python 3.9에서는 23건 모두 통과했다. 이전 head의 추가 Python 3.11 실행에서는 같은 호스트에서도 12건이 실패했다. PR에 purge 코드/테스트 변경은 없으며, PR 모듈을 전혀 import하지 않은 SQLite 최소 재현으로 원인을 분리했다: DB 옆에 가짜 WAL/SHM을 만든 뒤 열어 SELECT하면 Python 3.9.6/SQLite 3.51.0은 두 파일을 보존하고, Python 3.11.15/SQLite 3.53.3은 둘 다 제거한다. 기존 `agy.py:74–76`의 DB 검사와 `tests/test_issue10_13_purge.py:83–111` fixture 가정의 환경 차이다. “Linux에서만 발생”으로 단정하면 부정확하지만 이 PR이 도입한 회귀라는 근거는 없다.
|
||
7. **Python 경계:** 새 설정 정책은 herdr_config.py, pane 판정은 herdr_restore.py에 있다. resume bash는 호출과 경고만 추가하며, lib.sh는 Python 생성 결과를 서버 환경으로 전달한다. 신규 비즈니스 판정을 bash에 추가한 것으로 보지 않는다.
|
||
|
||
## 후속 커밋으로 해결된 사항
|
||
|
||
| 이전 이식성 문제 | 현재 상태 |
|
||
| --- | --- |
|
||
| Python 3.9에서 무조건 tomllib import로 수집 중단 | 해결: 선택 import와 해당 29건 skip, 3.11에서 별도 51건 통과 |
|
||
| macOS에 없는 /proc 환경 조회 | 해결: ps -E 대체 경로로 실제 A 검사 및 양성 대조 통과 |
|
||
| /tmp와 /private/tmp 문자열 차이 | 해결: 임시 루트 resolve 후 B 검사 통과 |
|
||
| issue9 테스트의 실행 Python과 bootstrap Python 차이 | 해결: PATH python3 기준 검사 및 폴백 경고 검증 통과 |
|
||
|
||
[VERDICT: NOT PASS]
|