docs(v4.8.5): archive PR #48 reviews (three rounds) and record the merge-workspace review
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
This commit is contained in:
1 parent
ab93776d6d
commit
bf8a583afb
16 files changed
+898
No files matched your search
@@ -0,0 +1,30 @@
|
||||
# 리뷰 리포트 — Job 3a93c30b (PR #48 2회차, 35935f9..e8d9151)
|
||||
|
||||
- 리뷰어: planner-reviewer-claude-01 (claude)
|
||||
- 리뷰 identity: loop-20261005T081844Z-e92124 / base c5c7eff / head e8d9151 / tree 8b39188 — 검증 경로에서 `git rev-parse HEAD 'HEAD^{tree}'` 일치, 미커밋 변경 없음
|
||||
- 범위: 커밋 36f0b8d, e8d9151. `git diff --stat 35935f9 e8d9151`: create SKILL.md 1줄, VERSIONS.md, 테스트 3개. lib.sh·lib_py·스크립트 변경 없음(제품 코드 불변).
|
||||
|
||||
## 결론 요약
|
||||
1회차의 차단 3건(B1~B3)과 문서 지적(D1)이 모두 닫혔다. 이 macOS 호스트(Python 3.9 venv)에서 지정 6개 파일이 실패 없이 통과하고, 실제 herdr 서버 테스트 5개가 skip 없이 돈다. 차단 결함 없음.
|
||||
|
||||
## 1. 1회차 지적 종결
|
||||
| 지적 (1회차) | 수정 | 확인 |
|
||||
|---|---|---|
|
||||
| B1 test_issue45_herdr_config.py:16 최상단 `import tomllib` → 3.9 에서 수집 중단 | try/except 로 선택적 import(:18-21), 결과를 파싱하거나 설정 유지를 기대하는 테스트만 `needs_tomllib` skip(:36) | 3.9: 수집 정상, tomllib 의존 검사만 skip. Python 3.11(Homebrew + PYTHONPATH): **51 passed**. 3.9 에서 남는 22건 중 test_without_tomllib_restore_is_still_off(:91-96)가 tomllib 없을 때 MINIMAL + "no tomllib" 경고를 직접 확인하므로 3.9 경로도 검사된다 |
|
||||
| B2 test_issue45_restore.py `/proc` 와 `/tmp` | 루트 `.resolve()`(:175), 서버 환경은 `/proc` 가 있으면 거기서, 없으면 `ps -E -ww`(:160-169, :239-240) | test_issue45_restore.py 5개 모두 **실행·통과**(skip 아님). 1회차에 내가 스크래치 사본으로 확인한 것과 같은 두 수정이며, 그때 A·B 수정을 끄면 각각 실패함(A: `--resume <id>`, B: pane 2개)을 확인했다. 제품 코드가 그 뒤 바뀌지 않았으므로 대조 결과는 그대로 유효하다 |
|
||||
| B3 test_issue9_server_env.py:157 tomllib, bootstrap 의 python3 가정 | bootstrap 과 같은 PATH 의 `python3` 로 결과를 파싱, tomllib 이 없으면 키 하나짜리 파일 + `confirm_close` 없음 + 경고 "no tomllib" 를 기대(:169-183) | 통과. 이 호스트에서는 else 분기(3.9)가 실행된다. `subprocess.run(["python3"…], env=env)` 는 POSIX 에서 env 의 PATH 로 실행 파일을 찾으므로 bootstrap 과 같은 인터프리터다. 경고 문구는 herdr_config.py:147 와 일치 |
|
||||
| D1 (비차단) python3 < 3.11 이면 사용자 설정 없이 뜬다는 문서 부재 | create SKILL.md:120 와 VERSIONS 4.8.5 항목 1, 품질 검증 항목에 추가 | 내용은 코드(lib.sh:241 의 bare `python3`, herdr_config.py:146-147)와 일치 |
|
||||
|
||||
## 2. 테스트 실행
|
||||
| 실행 | 결과 |
|
||||
|---|---|
|
||||
| 지정 6개 파일 (pr-48 .venv, Python 3.9.6) | **152 passed / 30 skipped / 0 failed** (216 s). skip = tomllib 필요 29건 + .claude/skills 1건(워크트리 정상). test_issue45_restore 는 skip 0 |
|
||||
| tests/test_issue45_herdr_config.py (python3.11) | **51 passed** |
|
||||
- 격리: 실행 전후 herdr 프로세스는 기존 `multi-agent-mux` 서버·attach 클라이언트와 오케스트레이터의 `agent wait` 뿐(잔류 서버 없음), `/private/tmp/m45*` 없음, `~/.config/herdr` 목록·config.toml md5·sessions 목록 불변. MQTT_* unset, .mam.env 미export. 실제 .mam/jobs 에 새 항목 없음.
|
||||
|
||||
## 3. 비차단 관찰
|
||||
- **N1 VERSIONS.md 4.8.5 항목 1 문장 흐름**: 새 문장이 "…경고가 남는다:" 로 끝나고 바로 뒤에 원래 있던 "출력이 원본 자신(별칭 포함)이거나 … 원본별 이름을 쓴다" 가 이어져, 출력 이름 규칙이 Python 버전 설명의 결과처럼 읽힌다. 원래는 "사용자 파일은 고치지 않는다:" 뒤에 붙던 설명이다. 새 문장을 항목 끝으로 옮기면 된다.
|
||||
- **N2 `_process_env` 의 macOS 분기**(test_issue45_restore.py:166-169)는 `ps -E` 출력을 공백으로 나누므로 공백이 든 경로는 맞추지 못한다. 테스트 루트가 `/private/tmp/m45…` 로 고정이라 지금은 영향 없다.
|
||||
- **N3 tomli 백포트 대신 문서화한 결정**에 대한 의견: 복원은 어느 경우에도 꺼지므로(#45 의 핵심) 안전 측면에서 문제없고, 이 호스트의 herdr 설정이 키 하나뿐이라는 사정에서 합리적이다. 나중에 사용자 설정이 커지면, 새 의존성 없이 PATH 에서 3.11+ 인터프리터를 찾는 방법도 고려할 만하다.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,76 @@
|
||||
# 리뷰 리포트 — Job 416d9768 (PR #48, fix/issue-45)
|
||||
|
||||
- 리뷰어: planner-reviewer-claude-01 (claude)
|
||||
- 리뷰 identity: loop-20261005T064728Z-a4e9c8 / base c5c7eff / head 35935f9 / tree dff9db1 — 검증 경로에서 `git rev-parse HEAD 'HEAD^{tree}'` 일치, 미커밋 변경 없음
|
||||
|
||||
## 결론 요약
|
||||
제품 동작(A: MAM 서버 복원 꺼짐, B: 자기 세션의 빈 셸만 닫기)은 이 macOS 호스트의 실제 herdr(0.9.1)에서 동작함을 확인했다. 그러나 **브리프가 지정한 검증 명령이 이 호스트(리포지토리 .venv = Python 3.9.6)에서 수집 단계에서 중단**되고, 그 파일을 빼고 돌려도 **새 테스트 5개가 macOS 에서 실패**한다. 원인은 모두 테스트의 Linux·Python 3.11 가정이며 수정은 작다. 스킬 frontmatter 가 `platforms: [linux, macos]` 이므로 차단으로 판단한다.
|
||||
|
||||
## 차단 (테스트 이식성)
|
||||
### B1. tests/test_issue45_herdr_config.py:16 — 모듈 최상단 `import tomllib`
|
||||
- Python 3.9 에서 `ModuleNotFoundError` → pytest 가 "Interrupted: 1 error during collection" 으로 **세션 전체를 중단**한다. 브리프 지정 명령(7개 파일)은 결과 0건, rc 2. 오케스트레이터의 전체 스위트도 같은 .venv 면 같은 이유로 중단된다.
|
||||
- 제품 쪽(lib_py/herdr_config.py:54-57)은 tomllib 부재를 처리하는데 테스트만 강제한다. `tomllib = pytest.importorskip("tomllib")` 처럼 모듈 skip 또는 tomllib 이 필요 없는 경우만 남기는 처리가 필요하다.
|
||||
- 참고: 같은 파일을 Homebrew python3.11 + 기존 venv 의 pytest(PYTHONPATH)로 돌리면 **51 passed**. 내용 자체는 정상이다.
|
||||
|
||||
### B2. tests/test_issue45_restore.py:227, :163 — Linux 전용 가정 (macOS 4 failed)
|
||||
| 테스트 | 실패 | 원인 |
|
||||
|---|---|---|
|
||||
| test_a_…[plain-user-config], test_a_…[user-config-with-session-table] | FileNotFoundError `/proc/<pid>/environ` | :227 `/proc` 는 macOS 에 없다 |
|
||||
| test_b_only_the_resumed_sessions_restored_shell_is_closed | `'/private/tmp/m45…' == '/tmp/m45…'` (:263) | :163 `mkdtemp(dir="/tmp")` — macOS 에서 /tmp 는 /private/tmp 링크이고 herdr 는 실경로를 보고한다 |
|
||||
| test_b_resume_leaves_no_restored_shell_behind | StopIteration (:325, cwd 비교) | 같은 원인 |
|
||||
- 검증: 스크래치 사본에서 **이 두 군데만** 고친 테스트(`.resolve()` 추가, `/proc` 대신 `ps -E -ww -o command= -p <pid>`)는 **5 passed**(65 s). 즉 제품은 맞고 테스트만 이식되지 않았다. 양성 대조도 이 사본에서 동작한다.
|
||||
|
||||
### B3. tests/test_issue9_server_env.py:157 — 테스트 안 `import tomllib`, 그리고 bootstrap 의 python3 가정
|
||||
- 3.9 에서 import 로 실패. 또 3.11 로 테스트를 돌려도 bootstrap 은 `python3`(lib.sh:241, 이 호스트는 /usr/bin/python3 3.9.6)로 herdr_config 를 실행하므로 결과는 MINIMAL 이고, :170 의 `cfg == {"ui": …, "session": …}` 단언은 실패한다. `python3` 에 tomllib 이 없으면 skip(또는 MINIMAL 을 기대)해야 한다.
|
||||
|
||||
## 검토 항목별 판단
|
||||
### 1. A 의 설정 병합 (lib_py/herdr_config.py)
|
||||
- 원본 보호: 출력은 항상 output_path(:192-205)를 거친다 — 원본 자신(samefile/realpath, 별칭 포함)이나 다른 원본용·비생성 파일이면 해시 이름으로, 그것도 막히면 OSError → 쓰지 않음. 폴백(.mam)도 같은 함수(:273, R3 2회차 수정). 쓰기는 같은 디렉터리 임시 파일 + os.replace(:208-222), 원본 권한 복사(:255).
|
||||
- 재귀: HERDR_CONFIG_PATH 가 생성 파일이면 2행의 `# source:` 로 원본을 따라간다(:65-92). source 행이 없으면 XDG/HOME 으로.
|
||||
- 병합 정확성: tomllib parse → `session` 에 키만 덮어씀 → dumps → 재파싱이 기대값과 **정확히 같을 때만** 사용(:156-164). 인라인 표·점 표기·따옴표 키·배열은 파싱 결과(dict)로 다루므로 표기와 무관. 주석·표기 형식은 생성 파일에서 사라지지만 원본은 그대로이고 의미는 같다. 실패 시 MINIMAL + 경고.
|
||||
- herdr 검사: 쓴 뒤 `herdr config check`(:169-180)가 "parse error"/"using defaults" 를 내면 MINIMAL 로 재시도, 그것도 거부되면 None → bootstrap 이 경고하고 예전처럼(복원 켜짐) 기동(lib.sh:241-245). 단위 테스트가 실제 herdr 로 이를 확인(3.11 에서 51 passed).
|
||||
- 사용자 디렉터리 흔적: `~/.config/herdr/mam-server-config.toml`(또는 해시 이름) 1개, 중단 시 `.mam-server-config.*.tmp` 가능. create SKILL.md:120-121 에 위치가 적혀 있다.
|
||||
- 비차단 D1 (아래): python3 < 3.11 호스트에서는 병합이 일어나지 않는다.
|
||||
|
||||
### 2. 적용 경로
|
||||
- MAM 이 서버를 띄우는 곳은 shim 의 headless bootstrap 한 곳(lib.sh:261 `"$REAL_HERDR" --session "$_MAM_SESSION" server`)뿐이고 A 가 여기에 걸려 있다. create·resume·delegate 는 모두 shim 을 거친다. 다른 `server` 기동은 grep 으로 찾지 못했다. MAM_NO_AUTOSTART 호출자(status/reconcile)는 서버를 띄우지 않는다(lib.sh:196-201).
|
||||
- 걸리지 않는 경로: 사용자가 `herdr session attach`/`herdr --session X` 로 직접 띄운 서버, 기본 `default` 서버, 이미 떠 있는 서버. create SKILL.md:121 이 "rebuilt each time MAM starts the server … next started", "A server you start yourself (`herdr session attach`) does not get it" 로 적고 있다(합의된 한계와 일치).
|
||||
|
||||
### 3. B 의 닫기 조건 (lib_py/herdr_restore.py)
|
||||
- 조건(:54-65): 세션 id 비어 있지 않음, 서버가 default 아님, `agent` 없음, `agent_session.value == id`, 전경 = shell_pid(shell_pid 없으면 False). 사용자 pane(agent_session 없음)·다른 세션·전경 프로세스 있음·에이전트 살아 있음은 제외 — 실제 서버 테스트(사본)에서 사용자 셸·Z(실행 중)·X2(sleep 60)가 남고 X·Y 만 닫힘을 확인.
|
||||
- fail-safe: herdr 오류·JSON 오류는 예외 → exit 1(:79-83) → resume 은 경고만(resume_session.sh:240-242) 하고 진행. default 서버는 herdr 를 부르지도 않는다(test_b_default_server_is_never_touched 통과). Class A fresh-spawn(UUID 없음)은 즉시 반환.
|
||||
|
||||
### 4. 테스트의 격리·결정성
|
||||
- test_issue45_restore 는 HOME 을 `/tmp/m45*` 로, 세션을 `mamt45-<pid>-<4hex>` 로 두고 HERDR_*·XDG_CONFIG_HOME·HERDR_CONFIG_PATH 를 지운다(:42-44, :161-168). 정리는 그 세션 이름·그 루트 경로로만 pkill(:186-195). conftest 도 HERDR_CONFIG_PATH·XDG_CONFIG_HOME 을 지운다.
|
||||
- 실행 전후 확인(실패 실행 3회, 사본 실행 3회 모두): herdr 프로세스는 기존 `multi-agent-mux` 서버·attach 클라이언트뿐(잔류 없음), `/tmp/m45*` 없음, `~/.config/herdr` 목록·config.toml md5·sessions 목록 불변.
|
||||
- 양성 대조: 사본에서 lib.sh 의 `HERDR_CONFIG_PATH` 주입을 끄면 test_a 가 `--resume <id>` 로 실패(#45 재현), resume 의 close 호출을 끄면 test_b_resume 가 pane 2개로 실패. 테스트가 수정에 실제로 의존한다.
|
||||
- 결정성: 폴링 + 고정 대기(최대 수 초)라 느린 호스트에서 다소 민감할 수 있으나 3회 반복 모두 같은 결과였다.
|
||||
|
||||
### 5. 문서·인벤토리
|
||||
- 버전: lib.sh·eager.py·SKILL.md 8개·VERSIONS 가 4.8.5 로 일치(test_version_consistency 통과).
|
||||
- skill_code_blocks.json: blocks 의 (skill, class, kind) 순서 동일, create 블록 4개의 start/end 이동과 resume removed 항목의 evidence 줄 번호(+8) 이동뿐. 분류 변경 없음.
|
||||
- **D1 (비차단, 문서)**: VERSIONS 4.8.5 항목 1과 create SKILL.md:120 은 서버 설정을 "사용자 설정 + 키"로 설명하지만, bootstrap 은 `python3`(lib.sh:241)로 실행되므로 python3 < 3.11 인 호스트(이 macOS 의 /usr/bin/python3 3.9.6)에서는 **항상 키 하나만 있는 파일**로 기동하고 사용자 herdr 설정(예: default_shell·키 바인딩)이 MAM 서버에 적용되지 않으며, 기동마다 경고가 나온다. 복원은 꺼지므로 안전하지만, 이 한계를 문서에 적거나 3.11+ 인터프리터를 찾는 편이 좋다. VERSIONS 의 "실패 시 키 하나만" 목록에도 Python 3.11 미만이 빠져 있다.
|
||||
|
||||
### 6. purge 기준 실패
|
||||
검증 경로에서 tests/test_issue10_13_purge.py **23 passed**(macOS). 이 PR 과 무관하며 "12 실패"는 Linux 기준이다.
|
||||
|
||||
### 7. 경계(#17)
|
||||
새 판정(병합·검사·출력 경로, 닫기 조건)은 모두 Python(herdr_config.py, herdr_restore.py)에 있다. bash 쪽 추가는 호출과 빈 값 경고(lib.sh:241-245, resume_session.sh:240-242)뿐이다.
|
||||
|
||||
## 테스트 실행 요약
|
||||
| 실행 | 결과 |
|
||||
|---|---|
|
||||
| 브리프 지정 7개 파일 (pr-48 .venv, Python 3.9.6) | **수집 오류로 중단, 0건 실행, rc 2** (B1) |
|
||||
| herdr_config 제외 6개 파일 | **5 failed** / 192 passed / 1 skipped (B2 4건, B3 1건; skip = .claude/skills, 워크트리 정상) |
|
||||
| test_issue45_herdr_config.py (python3.11) | 51 passed |
|
||||
| test_issue45_restore.py 이식성 2곳만 고친 사본 | 5 passed; A·B 수정을 끄면 각각 실패 |
|
||||
| test_issue10_13_purge.py | 23 passed |
|
||||
MQTT_* unset, .mam.env 미export. 실제 .mam/jobs 에 새 항목 없음.
|
||||
|
||||
## 수정 제안 (최소)
|
||||
1. test_issue45_herdr_config.py:16 → `tomllib = pytest.importorskip("tomllib")`.
|
||||
2. test_issue45_restore.py:163 → `Path(tempfile.mkdtemp(...)).resolve()`; :227 → `/proc` 가 없으면 `ps -E -ww -o command= -p <pid>` 로 확인(또는 그 단언만 Linux 한정).
|
||||
3. test_issue9_server_env.py:157 → bootstrap 이 쓸 `python3` 에 tomllib 이 없으면 skip 하거나 MINIMAL 을 기대.
|
||||
4. (비차단) D1 문서화.
|
||||
|
||||
[VERDICT: NOT PASS]
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"loop_id": "loop-20261005T085016Z-c0f6eb",
|
||||
"base_sha": "e8d9151226d8139c7b5688e83d035a3a747aeb2d",
|
||||
"head_sha": "ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9",
|
||||
"tree_sha": "f49ffdc8e95dc316f752fd81b2d55a0efddf1a12",
|
||||
"review_commit": "ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9",
|
||||
"reviewer": "planner-reviewer-claude-01",
|
||||
"job_id": "ab18016d",
|
||||
"verdict": "PASS",
|
||||
"transport": "herdr",
|
||||
"evidence": "review-tree",
|
||||
"isolation": "worktree",
|
||||
"verified_at": "2026-10-05T09:05:24Z"
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# 리뷰 리포트 — Job ab18016d (PR #48 3회차 델타, e8d9151..ab93776)
|
||||
|
||||
- 리뷰어: planner-reviewer-claude-01 (claude)
|
||||
- 리뷰 identity: loop-20261005T085016Z-c0f6eb / base e8d9151 / head ab93776 / tree f49ffdc — 검증 경로에서 `git rev-parse HEAD 'HEAD^{tree}'` 일치, 미커밋 변경 없음
|
||||
- 범위(항목 4): lib_py/herdr_config.py(+25/−2), lib_py/herdr_restore.py(+10/−2), 테스트 2개. bash·문서·버전 변경 없음. 새 판정은 모두 Python 에 있다(#17).
|
||||
|
||||
## 결론 요약
|
||||
codex R1~R3 가 닫혔다. R1 의 "MINIMAL 무검사 사용" 판단에 동의한다. `_same_doc` 은 nan 외에는 이전 `==` 와 같거나 더 엄격하며, 그 엄격함이 오탐(불필요한 MINIMAL 폴백)을 만들지 않음을 확인했다. 차단 결함 없음.
|
||||
|
||||
## 1. R1~R3 종결
|
||||
| 지적 (codex 2회차) | 수정 | 확인 |
|
||||
|---|---|---|
|
||||
| R1 `config check` 를 실행 못 하면(timeout·OSError) 병합 결과를 검사된 것으로 넘김 | herdr_config.py:245-258 — 병합 본문이면 경고 후 `continue` 로 MINIMAL 시도, MINIMAL 의 검사도 실행 못 하면 MINIMAL 을 경고와 함께 사용 | test_an_unrunnable_check_never_hands_over_an_unchecked_merge[timeout/oserror]: 스키마 오류가 든 사용자 설정이 생성 파일에 들어가지 않고 MINIMAL + "not using an unchecked merge" 경고, 원본 불변. 이전 제품 코드에서 2건 실패 |
|
||||
| R2 `_shell_in_foreground` 가 0/0·False/False·""/""·-1/-1 에서 True | herdr_restore.py:44-53 — 두 값이 bool 아닌 양의 int 일 때만 비교 | test_issue45_restore_unit.py: zero·false·empty·negative·null·string·true·float 모두 닫지 않음, 정상 pid 같음 → 닫음, 다름 → 둠. 이전 코드에서 7건 실패(null 은 이전에도 통과) |
|
||||
| R3 재파싱 `==` 가 nan≠nan 이라 nan 하나로 사용자 설정 전체를 버림 | herdr_config.py:140-150 `_same_doc`, :171 에서 사용 | test_a_nan_value_does_not_drop_the_other_settings[top/array/nested]: 경고 없이 설정 유지. 이전 코드에서 3건 실패 |
|
||||
이전 제품 코드(e8d9151 의 두 파일)로 되돌린 스크래치 사본에서 새 테스트는 **12 failed / 55 passed**(3.11), 현재 코드에서 **67 passed**.
|
||||
|
||||
## 2. R1 판단 — MINIMAL 무검사 사용 vs 경로 반환 안 함
|
||||
동의한다. 근거:
|
||||
- 경로를 반환하지 않으면 lib.sh:241-245 가 `HERDR_CONFIG_PATH` 없이 서버를 띄운다 → herdr 는 사용자 설정(복원 기본 켜짐)을 읽어 **#45 가 확실히 재발**한다.
|
||||
- MINIMAL 을 넘기면, herdr 가 그것을 받아들이는 경우(고정 2줄, 실제 herdr 0.9.1 에서 test_real_herdr_accepts_every_result 가 확인) 복원이 꺼지고, 만약 거부하더라도 herdr 는 기본값으로 돌아 결과는 "경로 없음"과 같은 복원 켜짐이다. 즉 MINIMAL 은 복원 측면에서 "경로 없음"보다 나쁜 경우가 없고, 잃는 것은 이미 경고되는 사용자 설정뿐이다.
|
||||
- 검사가 **실행되어 거부**한 경우는 여전히 None(:259-262)이다. "검사 불가"와 "검사 거부"를 다르게 다루는 것이 일관적이다(거부는 herdr 가 그 파일을 버린다는 증거).
|
||||
- 알 수 없는 nonzero 종료를 통과로 두는 것(:178)도 기존 근거(무해한 경고에도 nonzero)와 같고 이번 변경 범위 밖이다.
|
||||
|
||||
## 3. `_same_doc` 의 의미 (항목 3)
|
||||
3.11 에서 이전 `==` 와 직접 비교:
|
||||
| 경우 | 이전 == | _same_doc |
|
||||
|---|---|---|
|
||||
| nan / nan, 중첩 nan | False | **True** (의도된 변경) |
|
||||
| 1 / 1.0, True / 1, 0 / False | True | **False** (더 엄격) |
|
||||
| -0.0 / 0.0, inf / inf, 같은 time, 같은 문자열 | True | True |
|
||||
| date / datetime | False | False |
|
||||
| [1,2] / [2,1] (배열 순서) | False | False |
|
||||
| dict 키 순서만 다름, 표 배열 [{…}] | True | True |
|
||||
- 더 엄격해진 int/float/bool 구분은 직렬화가 타입을 보존하는 한 오탐을 만들지 않는다: dumps(:107-126)는 int→정수, float→repr(소수점·지수 유지), bool→true/false 로 쓴다. 날짜·오프셋 datetime·소수 초 local time·표 배열·inf·nan·−nan·따옴표 점 키·1e100 이 든 문서로 server_config_text 를 돌려 **설정 유지, 경고 없음**을 확인했다. 오히려 왕복에서 1 이 1.0 으로 바뀌는 것 같은 결함은 이제 잡힌다.
|
||||
- 참고: `-nan` 은 `nan` 으로 쓰여 부호가 사라진다(nan 끼리 같다고 보므로 통과). herdr 설정에서 nan 부호가 의미를 가질 일은 없어 무해하다.
|
||||
|
||||
## 4. 테스트 실행
|
||||
| 실행 | 결과 |
|
||||
|---|---|
|
||||
| 지정 4개 파일 (pr-48 .venv, Python 3.9, 실제 herdr 포함) | **53 passed / 34 skipped / 0 failed** (76 s). skip = tomllib 전용; test_issue45_restore 는 skip 0 |
|
||||
| herdr_config + restore_unit (지정 venv311, Python 3.11.15) | **67 passed** |
|
||||
| 같은 테스트, 이전 제품 코드 사본 | 12 failed / 55 passed |
|
||||
격리: 실행 전후 herdr 프로세스는 기존 multi-agent-mux 서버·attach·오케스트레이터 `agent wait` 뿐, `/private/tmp/m45*` 없음, `~/.config/herdr` 목록·config.toml md5·sessions 불변. MQTT_* unset, .mam.env 미export.
|
||||
|
||||
## 5. 비차단 관찰
|
||||
- R1 테스트는 두 검사 모두 실행 불가인 경우만 다룬다. "병합 검사는 실행 불가, MINIMAL 검사는 실행되어 통과" 경로(:256 → 다음 반복의 :258)는 코드상 자명하지만 별도 테스트는 없다.
|
||||
- 2회차의 VERSIONS 4.8.5 항목 1 문장 순서 지적(N1)은 이번 델타에 포함되지 않았다(문서 변경 없음).
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,123 @@
|
||||
# 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]
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"loop_id": "loop-20261005T085016Z-c0f6eb",
|
||||
"base_sha": "e8d9151226d8139c7b5688e83d035a3a747aeb2d",
|
||||
"head_sha": "ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9",
|
||||
"tree_sha": "f49ffdc8e95dc316f752fd81b2d55a0efddf1a12",
|
||||
"review_commit": "ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9",
|
||||
"reviewer": "planner-reviewer-codex-01",
|
||||
"job_id": "b8dbd280",
|
||||
"verdict": "PASS",
|
||||
"transport": "herdr",
|
||||
"evidence": "review-tree",
|
||||
"isolation": "worktree",
|
||||
"verified_at": "2026-10-05T09:05:24Z"
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
# PR #48 — 3회차 Codex 델타 리뷰
|
||||
|
||||
## 판정: PASS
|
||||
|
||||
이전 R1–R3이 해결됐다. 고정 MINIMAL 설정을 검사 실행 불가 시 경고와 함께 사용하는 R1의 대안도 이번 지원 범위에서 수용한다. 차단 결함은 발견하지 않았다. 문서 표현 정리 1건은 비차단 의견이다.
|
||||
|
||||
## 검증 identity / 범위
|
||||
|
||||
- job: `b8dbd280`
|
||||
- loop: `loop-20261005T085016Z-c0f6eb`
|
||||
- base: `e8d9151226d8139c7b5688e83d035a3a747aeb2d`
|
||||
- head: `ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9`
|
||||
- tree: `f49ffdc8e95dc316f752fd81b2d55a0efddf1a12`
|
||||
- 검증 경로: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T085016Z-c0f6eb/1`
|
||||
- `git diff e8d9151..HEAD`의 제품 모듈 2개와 테스트 파일 2개만 델타 리뷰했다. 검증 전후 HEAD/tree 일치, 작업 트리 clean.
|
||||
- 소스 수정·커밋·브랜치 변경 없음. 실행 중 사용자 herdr 서버·설정·소켓, .mam.env를 사용하거나 변경하지 않았다.
|
||||
|
||||
## 이전 지적 종료 표
|
||||
|
||||
| 지적 | 현재 근거 | 검증 및 판단 |
|
||||
| --- | --- | --- |
|
||||
| R1: 검사 timeout/OSError에도 스키마 오류 병합 파일 반환 | `.agents/skills/lib_py/herdr_config.py:247–258` | 검사 불가한 병합 파일은 폐기하고 MINIMAL로 전환한다. timeout/OSError 회귀 검사와 이전 독립 탐침 통과. **해결**, 아래 대안 판단 참조. |
|
||||
| R2: 0/False/빈 문자열/음수의 동등 비교로 pane 닫기 | `.agents/skills/lib_py/herdr_restore.py:44–53` | 양쪽 값이 bool이 아닌 양의 정수일 때만 비교한다. 비정상·누락 응답에서 닫기 없음, 정상 셸은 닫고 busy pane은 유지한다. 이전 비정상 PID 탐침 4건도 통과. **해결**. |
|
||||
| R3: NaN 때문에 무관한 정상 설정까지 유실 | `.agents/skills/lib_py/herdr_config.py:140–149,171` | 재귀 비교에서 NaN끼리 동등하게 처리한다. 중첩/배열 NaN 및 기존 UI 설정 유지 검사, 이전 독립 탐침 통과. **해결**. |
|
||||
|
||||
## R1: MINIMAL을 검사 실행 불가 시 사용하는 선택
|
||||
|
||||
이전 보고서에서는 MINIMAL 검사도 실패하면 경로를 반환하지 않는 방안을 제안했다. 이번 구현은 그 제안을 그대로 따르지는 않지만, 원래 결함의 핵심인 **검사되지 않은 사용자 병합 내용의 사용**을 막으며 #45의 복원 비활성화 목적을 더 잘 보존하므로 수용한다.
|
||||
|
||||
- MINIMAL은 사용자 스키마 값과 분리된 고정 `[session]\nresume_agents_on_restore = false\n` 본문이다.
|
||||
- 실제 herdr 설정 검사와 실서버 복원 검사가 이 설정을 받아들이고 복원을 끄는 것을 검증했다.
|
||||
- 첫 검사 timeout 뒤 두 번째 MINIMAL 검사가 성공하는 경로를 별도 주입 검사했다. 호출 2회, 반환 파일은 MINIMAL이었다.
|
||||
- 두 번 모두 검사 실행이 불가능한 경우에도 병합 내용은 남지 않고, `using the fixed minimal config unchecked` 경고로 미검증 상태를 밝힌다.
|
||||
- 검사기를 실행했고 파일을 명시적으로 거부한 경우는 예외와 다르다. 병합/MINIMAL 양쪽에 명시적 거부 응답을 주입하면 여전히 경로를 반환하지 않는다(`:259–263`). 이 흐름도 별도로 확인했다.
|
||||
- 아무 파일도 넘기지 않는 기존 bootstrap 경로는 복원이 켜진 기본값으로 시작할 수 있으므로, 알려진 고정 설정을 넘기는 선택에는 구체적인 근거가 있다.
|
||||
|
||||
이 판단은 PR이 대상으로 삼은 herdr의 알려진 설정 스키마에 대한 것이다. “실행하지 못한 검사가 통과했다”는 의미는 아니다.
|
||||
|
||||
알 수 없는 nonzero 종료의 판정은 이번 커밋에서 바뀌지 않았다. herdr가 무해한 unknown-section 경고에도 nonzero를 반환하므로, 모든 nonzero를 파일 전체 거부로 취급하는 변경을 R1 종료의 조건으로 요구하지 않는다.
|
||||
|
||||
## _same_doc 의미 확인
|
||||
|
||||
**이전 일반 `==`와 완전히 동일하지 않다.** `:149`가 동일한 타입을 요구하므로 NaN 처리 외에 int/float/bool 교차 동등성도 더 엄격해졌다. 이는 TOML 재직렬화의 타입 보존 검증에 적절하며, 현재 writer가 유지하는 유효 값의 정상 왕복을 깨뜨리는 사례는 찾지 못했다.
|
||||
|
||||
| 비교 | 이전 == | 현재 _same_doc | 판단 |
|
||||
| --- | --- | --- | --- |
|
||||
| `1` / `1.0` | true | false | int/float 타입 변경을 감지 |
|
||||
| `True` / `1`, `False` / `0.0` | true | false | bool/숫자 혼동 방지 |
|
||||
| 같은 타입의 int/bool/string | 기존 값 비교 | 동일 | 유지 |
|
||||
| `nan` / `nan` | false | true | 의도한 수정 |
|
||||
| `nan` / 일반 숫자 | false | false | 유지 |
|
||||
| `0.0` / `-0.0`, 같은 infinity | true | true | 유지 |
|
||||
| 같은 datetime, 같은 순간의 다른 UTC offset | true | true | 유지 |
|
||||
| date / datetime | false | false | 유지 |
|
||||
| 배열 순서·길이 차이 | false | false | 유지 |
|
||||
| dict 키 차이 | false | false | 유지 |
|
||||
| dict/list 안의 새로 생성한 NaN들 | false | true | 재귀 수정 |
|
||||
|
||||
datetime/time offset, 중첩 NaN, 배열 길이·순서 등을 포함한 독립 비교 18건을 실행해 기대 결과를 확인했다. 이 비교는 파서가 반환하는 TOML 자료형을 대상으로 하며 임의 사용자 정의 Python 객체를 위한 범용 동등 비교를 요구하지 않는다.
|
||||
|
||||
## 실행 결과
|
||||
|
||||
모든 실행 cwd는 위 검증 경로였다. Python bytecode 및 pytest cache를 끄고 basetemp는 검증 경로 밖에 두었다.
|
||||
|
||||
| 검사 | 결과 |
|
||||
| --- | --- |
|
||||
| 지정 Python 3.9: herdr_config, restore_unit, 실제 restore, issue9_server_env | **53 passed, 34 skipped**, 72.81초 |
|
||||
| 브리프의 Python 3.11: herdr_config, restore_unit | **67 passed**, 0.40초 |
|
||||
| 이전 리뷰의 독립 결함 탐침 파일 그대로 현재 head에서 실행 | **6 passed**, 0.02초 (이전 head에서는 6 failed) |
|
||||
| _same_doc 독립 비교 | **18건 통과** |
|
||||
| 검사 timeout→MINIMAL 성공 / 양쪽 명시적 거부 | **2개 흐름 통과** |
|
||||
| git diff --check 및 변경 Python 모듈 AST parse | 통과 |
|
||||
|
||||
3.9의 34 skip은 tomllib이 필요한 검사다. 이를 보완해 3.11에서 해당 두 파일 전체 67건을 실행했다. 실제 herdr 복원 및 양성 대조 테스트도 3.9 실행에 포함되어 통과했다.
|
||||
|
||||
- 3.9 로그: `/tmp/b8dbd280-selected39.log`
|
||||
- 3.11 로그: `/tmp/b8dbd280-selected311.log`
|
||||
- 이전 탐침 파일: `/tmp/714c5189_extra_probes.py`
|
||||
- 이전 탐침 재실행: `PYTHONDONTWRITEBYTECODE=1 /tmp/270e8aaa-py311/bin/python -m pytest -p no:cacheprovider -q --basetemp=/tmp/b8dbd280-prior-probes /tmp/714c5189_extra_probes.py`
|
||||
|
||||
실행 wrapper에서 상속된 HERDR_*를 제거하고 버리는 HOME/XDG_CONFIG_HOME을 사용했다. 실제 테스트 세션은 uuid4 기반 `mamt45-84511-*`였으며, 종료 후 해당 서버 프로세스 및 해당 세션을 담은 임시 루트가 남아 있지 않음을 확인했다.
|
||||
|
||||
## 비차단 의견 C1 — 문서의 검사 필수 표현을 예외와 맞추기
|
||||
|
||||
`.agents/skills/lib_py/herdr_config.py:23–26,239–240`과 `VERSIONS.md:49`에는 결과가 반드시 herdr 검사에서 승인된다는 표현이 남아 있다. 이번 `:247–258`의 고정 MINIMAL 예외는 코드 주석과 런타임 경고로 설명되지만 이 문서 표현에는 반영되지 않았다.
|
||||
|
||||
“사용자 설정을 병합한 파일은 검사 불가 시 사용하지 않으며, 검사기를 실행할 수 없는 경우 고정 최소 설정을 경고와 함께 사용한다”는 예외를 문서와 docstring에 추가하면 정확해진다. 제품의 실패 처리와 경고는 충분히 명시적이므로 이 문서 보완을 이번 PASS의 차단 조건으로 삼지 않는다.
|
||||
|
||||
## 범위
|
||||
|
||||
변경은 기존 세 지적의 설정 검사 실패 처리, PID 검증, NaN 재파싱 비교와 회귀 테스트에 한정된다. bash 정책 추가, 버전 변경, 인벤토리 분류 변경, 승인되지 않은 already-running 검사나 무명 에이전트 종료 기능은 없다. 재계획은 필요하지 않다.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"loop_id": "loop-20261005T085016Z-c0f6eb",
|
||||
"base_sha": "e8d9151226d8139c7b5688e83d035a3a747aeb2d",
|
||||
"head_sha": "ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9",
|
||||
"tree_sha": "f49ffdc8e95dc316f752fd81b2d55a0efddf1a12",
|
||||
"review_commit": "ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9",
|
||||
"reviewer": "reviewer-agy-01",
|
||||
"job_id": "3e6f0467",
|
||||
"verdict": "PASS",
|
||||
"transport": "herdr",
|
||||
"evidence": "review-tree",
|
||||
"isolation": "worktree",
|
||||
"verified_at": "2026-10-05T09:05:24Z"
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
# PR #48 (fix/issue-45, Closes #45) Round 3 Delta Review Report
|
||||
|
||||
- **Reviewer Agent**: `agy` (session: `herdr:reviewer-agy-01`)
|
||||
- **Job ID**: `3e6f0467`
|
||||
- **Verification Directory**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T085016Z-c0f6eb/1`
|
||||
- **Base Commit**: `e8d9151226d8139c7b5688e83d035a3a747aeb2d` (Round 2 head)
|
||||
- **Head Commit**: `ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9` (Round 3 head)
|
||||
- **Delta Under Inspection**: `git diff e8d9151 ab93776` (Commit `ab93776`)
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
In Round 2, `claude`, `agy`, and `grok` reached a `PASS` verdict after verifying that Python 3.9 macOS environment and portability blockers were eliminated. However, `codex` reported three valid edge-case issues in the product code (`R1`: unchecked merge pass-through on `herdr config check` execution failure; `R2`: unsafe type coercion in `_shell_in_foreground` comparing non-PIDs with `==`; `R3`: IEEE 754 `NaN != NaN` dropping valid TOML configurations containing `nan`).
|
||||
|
||||
Commit `ab93776` surgically addresses all three findings (R1, R2, R3) in `lib_py/herdr_config.py` and `lib_py/herdr_restore.py`, accompanied by targeted unit tests in `tests/test_issue45_herdr_config.py` and `tests/test_issue45_restore_unit.py`.
|
||||
|
||||
All review criteria have been audited, analyzed, and verified across both Python 3.9 and Python 3.11 test environments. The modifications are minimal, robust, fail-safe, and fully within scope.
|
||||
|
||||
---
|
||||
|
||||
## 2. Review Findings (R1 ~ R3) Resolution Matrix
|
||||
|
||||
| # | Item | Defect Description | Resolution in `ab93776` | Status |
|
||||
|---|---|---|---|---|
|
||||
| **R1** | Unchecked merge pass-through on check failure | If `herdr config check` failed to execute (timeout, `OSError`), `_write_checked` previously treated the merged file as valid and returned `True`. If the unvalidated merge contained a schema error, `herdr` would silently discard the file on startup and re-enable restore, causing Issue #45 to recur. | In `lib_py/herdr_config.py:_write_checked`, an execution error during candidate validation skips any unchecked merged body and advances to `MINIMAL`. If `MINIMAL` also encounters a check execution error, it writes `MINIMAL` with a warning rather than returning no config. | **CLOSED** (Verified) |
|
||||
| **R2** | Loose equality in `_shell_in_foreground` | `_shell_in_foreground` compared `shell` and `foreground_process_group_id` directly with `==`, causing matching non-PIDs (`0 == 0`, `False == False`, `"" == ""`, `-1 == -1`) to evaluate to `True` and mistakenly close active panes. | Added `_is_pid(v)` helper: `isinstance(v, int) and not isinstance(v, bool) and v > 0`. `_shell_in_foreground` now requires both `shell` and `fg` to be valid positive integer PIDs. | **CLOSED** (Verified) |
|
||||
| **R3** | `NaN` equality dropping valid configs | Standard Python equality `tomllib.loads(text) == expected` fails for valid TOML `nan` values because `float('nan') != float('nan')`, causing all user settings to be discarded in favor of minimal config. | Implemented `_same_doc(a, b)` which recursively verifies dictionary keys and list items while treating `math.isnan(a) and math.isnan(b)` as equal and enforcing strict type equality (`type(a) is type(b)`). | **CLOSED** (Verified) |
|
||||
|
||||
---
|
||||
|
||||
## 3. Deep-Dive on Specific Review Criteria
|
||||
|
||||
### 3.1. Assessment of R1: Unchecked MINIMAL Fallback vs. Returning None
|
||||
- **Context & Design Trade-off**: Codex suggested that if validating `MINIMAL` fails or cannot be executed, `write_server_config` should return `None` (no path). The Creator instead decided to emit a warning and return the `MINIMAL` config.
|
||||
- **Analysis**:
|
||||
- If `write_server_config` returns `None`, `lib.sh` starts `herdr` without setting `HERDR_CONFIG_PATH`. Herdr's built-in default is `resume_agents_on_restore = true`. When the server starts, every previously saved agent session would be relaunched without permissions, resurrecting the primary vulnerability of Issue #45.
|
||||
- In contrast, `MINIMAL` is a static, immutable, two-line string:
|
||||
```toml
|
||||
[session]
|
||||
resume_agents_on_restore = false
|
||||
```
|
||||
This fixed content has zero dynamic variables and has been rigorously verified against `herdr 0.9.1+` (`test_real_herdr_accepts_every_result`).
|
||||
- If `herdr config check` cannot be executed due to a transient execution error (e.g., subprocess timeout or file descriptor exhaustion), deploying `MINIMAL` provides the safest available defense-in-depth against unauthorized agent auto-restoration.
|
||||
- **Verdict on Criterion 2**: The Creator's decision is sound, defensible, and superior in fail-safety compared to returning `None`.
|
||||
|
||||
### 3.2. Assessment of R3: Equivalence of `_same_doc`
|
||||
- `_same_doc(a, b)` enforces:
|
||||
1. `math.isnan(a) and math.isnan(b)` returns `True` for floats.
|
||||
2. Dict comparison checks `a.keys() == b.keys()` and recurses on all values.
|
||||
3. List comparison checks `len(a) == len(b)` and recurses element-wise in sequence, strictly preserving list order.
|
||||
4. Primitive comparison checks `type(a) is type(b) and a == b`.
|
||||
- **Type Discrimination**: In Python, `0 == False`, `1 == True`, and `1 == 1.0` evaluate to `True`. Because TOML maintains distinct types for boolean, integer, and float, the added check `type(a) is type(b)` ensures that serialization does not inadvertently morph types (e.g. integer `1` into float `1.0`).
|
||||
- **Verdict on Criterion 3**: `_same_doc` preserves identical behavior to `==` for all non-nan types while providing strictly higher fidelity TOML AST verification.
|
||||
|
||||
### 3.3. Scope and Surgical Precision
|
||||
- Modified lines:
|
||||
- `lib_py/herdr_config.py`: +31 lines, -2 lines.
|
||||
- `lib_py/herdr_restore.py`: +9 lines, -3 lines.
|
||||
- `tests/test_issue45_herdr_config.py`: +38 lines.
|
||||
- `tests/test_issue45_restore_unit.py`: +56 lines (new unit test file for stubbed PID verification).
|
||||
- No extraneous changes or unrelated formatting edits were introduced.
|
||||
|
||||
---
|
||||
|
||||
## 4. Verification and Test Results
|
||||
|
||||
Tests were executed directly within the isolated review verification path:
|
||||
`/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T085016Z-c0f6eb/1`
|
||||
|
||||
### 4.1. Round 3 Target Test Suite (Python 3.9 venv)
|
||||
```bash
|
||||
AGENT_PYTHON_BIN=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-48/.venv/bin/python \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-48/.venv/bin/python \
|
||||
-m pytest -p no:cacheprovider -q --basetemp=/tmp/pytest-pr48-r3 \
|
||||
tests/test_issue45_herdr_config.py \
|
||||
tests/test_issue45_restore_unit.py \
|
||||
tests/test_issue45_restore.py \
|
||||
tests/test_issue9_server_env.py
|
||||
```
|
||||
- **Exit Code**: `0`
|
||||
- **Result**: `53 passed, 34 skipped in 72.58s (0:01:12)`
|
||||
|
||||
### 4.2. Python 3.11 Environment Verification
|
||||
```bash
|
||||
PYTHONPATH=.agents/skills \
|
||||
/private/tmp/claude-501/-Users-godopu16-PuKi-laa-canary-projects-multi-agent-mux/dbe6dd10-a210-4b46-b613-58a1860499b6/scratchpad/venv311/bin/python \
|
||||
-m pytest -p no:cacheprovider -q --basetemp=/tmp/pytest-pr48-r3-py311 \
|
||||
tests/test_issue45_herdr_config.py \
|
||||
tests/test_issue45_restore_unit.py
|
||||
```
|
||||
- **Exit Code**: `0`
|
||||
- **Result**: `67 passed in 0.42s`
|
||||
|
||||
### 4.3. Full Auxiliary & Consistency Suite (Python 3.9 venv)
|
||||
```bash
|
||||
AGENT_PYTHON_BIN=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-48/.venv/bin/python \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-48/.venv/bin/python \
|
||||
-m pytest -p no:cacheprovider -q --basetemp=/tmp/pytest-pr48-r3-full \
|
||||
tests/test_issue23_24_startup_path.py \
|
||||
tests/test_issue42_skill_blocks.py \
|
||||
tests/test_version_consistency.py
|
||||
```
|
||||
- **Exit Code**: `0`
|
||||
- **Result**: `110 passed, 1 skipped in 137.12s (0:02:17)`
|
||||
|
||||
---
|
||||
|
||||
## 5. Conclusion & Final Verdict
|
||||
|
||||
All findings from the previous round have been resolved completely, test coverage is thorough and passing across both Python 3.9 and 3.11, and no regressions or scope violations exist.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,105 @@
|
||||
# PR #48 (fix/issue-45, Closes #45) Round 2 Delta Review Report
|
||||
|
||||
- **Reviewer Agent**: `agy` (session: `herdr:reviewer-agy-01`)
|
||||
- **Job ID**: `3f82b402`
|
||||
- **Verification Directory**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T081844Z-e92124/1`
|
||||
- **Base Commit**: `c5c7eff6f758c76ac37a138dbc3c301f3f7e10cf` (main, v4.8.4)
|
||||
- **Head Commit**: `e8d9151226d8139c7b5688e83d035a3a747aeb2d` (v4.8.5)
|
||||
- **Delta Under Inspection**: `35935f9`..`e8d9151` (commits `36f0b8d` and `e8d9151`)
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
In Round 1 (`f3699a96`), the review concluded with `[VERDICT: NOT PASS]` strictly due to cross-platform and environment compatibility issues on macOS under Python 3.9 (missing `tomllib` in Python < 3.11, `/proc/{pid}/environ` absence on macOS, and `/tmp` vs `/private/tmp` symlink divergence causing test assertion failures).
|
||||
|
||||
The Creator has applied targeted, high-quality fixes across commits `36f0b8d` and `e8d9151`. The changes are strictly confined to test suites and documentation, leaving production code (`lib.sh`, `lib_py/herdr_config.py`, `lib_py/herdr_restore.py`, `resume_session.sh`) stable and untouched.
|
||||
|
||||
All 5 issues identified during Round 1 by `agy` have been completely and cleanly resolved. The test suite passes cleanly both under the host's Python 3.9 environment (152 passed, 30 skipped) and Python 3.11 (51 passed, 0 skipped).
|
||||
|
||||
---
|
||||
|
||||
## 2. Round 1 Findings Resolution Matrix
|
||||
|
||||
| # | Round 1 Finding (Job `f3699a96`) | File & Line | Round 2 Resolution (Commits `36f0b8d`, `e8d9151`) | Verification Status |
|
||||
|---|---|---|---|---|
|
||||
| **1** | Unconditional `import tomllib` crashing pytest collection under Python 3.9 | `tests/test_issue45_herdr_config.py:16` | Wrapped `import tomllib` in a `try...except ImportError` block; defined `@needs_tomllib` decorator skipping tests requiring TOML parsing when `tomllib is None` (22 passed, 29 skipped under 3.9; 51 passed under 3.11). | **CLOSED** (Verified) |
|
||||
| **2** | Hardcoded `import tomllib` causing execution failure in `test_bootstrap_hands_the_server_a_no_restore_config` under Python 3.9 | `tests/test_issue9_server_env.py:154` | Removed inline `import tomllib`. Replaced with a dynamic subprocess parser invoking the active `python3` binary on PATH. Verifies full JSON parsed output when `tomllib` is present, or asserts fallback to minimal config and stderr warning when absent. | **CLOSED** (Verified) |
|
||||
| **3** | Linux-specific `/proc/{pid}/environ` path causing `FileNotFoundError` on macOS | `tests/test_issue45_restore.py:227` | Added helper function `_process_env(pid)` that detects `/proc/{pid}/environ` existence, falling back gracefully to `ps -E -ww -o command= -p <pid>` on macOS / BSD environments. | **CLOSED** (Verified) |
|
||||
| **4** | macOS `/tmp` vs `/private/tmp` symlink mismatch causing failure in `assert cwd == str(rig.root)` | `tests/test_issue45_restore.py:263` | In fixture `rig`, resolved the temporary directory with `.resolve()` (`root = Path(tempfile.mkdtemp(prefix="m45", dir="/tmp")).resolve()`), ensuring canonical `/private/tmp` alignment with `herdr`'s reported cwd. | **CLOSED** (Verified) |
|
||||
| **5** | macOS `/tmp` vs `/private/tmp` symlink mismatch causing `StopIteration` during pane cwd search | `tests/test_issue45_restore.py:325` | Resolving `root` via `Path(...).resolve()` resolved the cwd mismatch for `next(p for p in ... if p.get('cwd') == str(rig.root))`. | **CLOSED** (Verified) |
|
||||
|
||||
---
|
||||
|
||||
## 3. Delta Code Review Details (`35935f9`..`e8d9151`)
|
||||
|
||||
### 3.1. `tests/test_issue45_herdr_config.py` (Commit `36f0b8d`)
|
||||
- **Import Guarding**: Gracefully handles missing `tomllib` standard library on Python < 3.11.
|
||||
- **Skipping Strategy**: 29 unit tests asserting deep AST / dictionary equivalence of merged TOML configs are decorated with `@needs_tomllib`. Tests testing paths, file naming, collision avoidance, and permissions continue to execute under Python 3.9.
|
||||
- **Verification**: Under Python 3.9, 22 tests pass and 29 skip without errors. Under Python 3.11, all 51 tests pass.
|
||||
|
||||
### 3.2. `tests/test_issue45_restore.py` (Commit `e8d9151`)
|
||||
- **Canonical Root Resolution**: `root = Path(tempfile.mkdtemp(prefix="m45", dir="/tmp")).resolve()` resolves macOS symlinks cleanly.
|
||||
- **Cross-Platform Process Inspection**: `_process_env(pid: int)` cleanly splits `/proc/{pid}/environ` by `\0` when present, or extracts environment variables from `ps -E -ww -o command= -p <pid>`.
|
||||
|
||||
### 3.3. `tests/test_issue9_server_env.py` (Commit `e8d9151`)
|
||||
- Seamlessly validates dual behavior:
|
||||
- If `python3` on `PATH` has `tomllib`, confirms full configuration merge `{"ui": {"confirm_close": False}, "session": {"resume_agents_on_restore": False}}`.
|
||||
- If `python3` lacks `tomllib`, confirms minimal config fallback (`resume_agents_on_restore = false`), absence of unmerged user keys (`confirm_close`), and emission of the `"no tomllib"` warning on stderr.
|
||||
|
||||
### 3.4. Documentation (`SKILL.md` & `VERSIONS.md`)
|
||||
- `multi-agent-mux-create/SKILL.md:121` explicitly documents the runtime requirement:
|
||||
> *"Building that file needs `tomllib` (Python 3.11+) in the `python3` on PATH; with an older `python3` (macOS ships 3.9) restore is still off, but the file holds only that key, so the MAM server runs without your other herdr settings and the bootstrap prints a warning saying so."*
|
||||
- `VERSIONS.md` under `v4.8.5` changelog reflects both the Python 3.11+ requirement and the test suite portability improvements across macOS and Linux.
|
||||
|
||||
---
|
||||
|
||||
## 4. Verification & Test Execution Results
|
||||
|
||||
All verification was conducted strictly inside the designated review worktree (`/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T081844Z-e92124/1`).
|
||||
|
||||
### 4.1. Primary Test Suite (Python 3.9 Host Environment)
|
||||
```bash
|
||||
AGENT_PYTHON_BIN=/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-48/.venv/bin/python \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-48/.venv/bin/python \
|
||||
-m pytest -p no:cacheprovider -q --basetemp=/tmp/pytest-pr48-r2 \
|
||||
tests/test_issue45_herdr_config.py \
|
||||
tests/test_issue45_restore.py \
|
||||
tests/test_issue9_server_env.py \
|
||||
tests/test_issue23_24_startup_path.py \
|
||||
tests/test_issue42_skill_blocks.py \
|
||||
tests/test_version_consistency.py
|
||||
```
|
||||
**Result**:
|
||||
- **Exit Code**: `0`
|
||||
- **Output**: `152 passed, 30 skipped in 211.50s (0:03:31)`
|
||||
|
||||
### 4.2. Python 3.11 Compatibility Sanity Check
|
||||
```bash
|
||||
PYTHONPATH=.agents/skills:/Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux-wt/pr-48/.venv/lib/python3.9/site-packages \
|
||||
/opt/homebrew/bin/python3.11 \
|
||||
-m pytest -p no:cacheprovider -q --basetemp=/tmp/pytest-pr48-py311 \
|
||||
tests/test_issue45_herdr_config.py
|
||||
```
|
||||
**Result**:
|
||||
- **Exit Code**: `0`
|
||||
- **Output**: `51 passed in 0.40s`
|
||||
|
||||
---
|
||||
|
||||
## 5. Non-Blocking Architectural Note
|
||||
|
||||
- **Python < 3.11 Config Merging**: In Round 1, grok suggested bundling a `tomli` backport for environments with Python < 3.11. The project decision to rely on standard library `tomllib` (Python 3.11+) and fall back to minimal config with an explicit warning is well-founded:
|
||||
1. Multi-Agent-Mux relies on zero extra external runtime dependencies for core shell shims.
|
||||
2. On hosts running macOS default Python 3.9, the default user `herdr` configuration is minimal, and restoring without extra settings safely satisfies Issue #45.
|
||||
3. The behavior is fully documented in `create/SKILL.md` and `VERSIONS.md`.
|
||||
Therefore, this design choice is accepted as complete and non-blocking.
|
||||
|
||||
---
|
||||
|
||||
## 6. Final Verdict
|
||||
|
||||
All Round 1 blockers have been resolved with precision. No regressions or unintended mutations were introduced.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,107 @@
|
||||
# Final Review Report: Job f3699a96 (PR #48 / Issue #45)
|
||||
|
||||
## 1. Review Identity & Metadata
|
||||
- **Loop ID**: `loop-20261005T064728Z-a4e9c8`
|
||||
- **Job ID**: `f3699a96`
|
||||
- **Reviewer**: agy (session: `herdr:reviewer-agy-01`)
|
||||
- **Base Commit**: `c5c7eff6f758c76ac37a138dbc3c301f3f7e10cf` (main, v4.8.4)
|
||||
- **Head Commit**: `35935f9e8a052ca9e9d9b806a62d17eb72afea78` (10 commits)
|
||||
- **Tree Hash**: `dff9db13f3e018528d04a024acda942f501513ff`
|
||||
- **Verification Path**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1`
|
||||
- **Git Status**: Clean (working tree clean, no uncommitted changes)
|
||||
- **Whitespace Check (`git diff base...HEAD --check`)**: Clean (0 errors)
|
||||
|
||||
---
|
||||
|
||||
## 2. Review Findings & Reasons for NOT PASS
|
||||
|
||||
During verification with the specified test command (`AGENT_PYTHON_BIN=... python -m pytest ...`), multiple critical test failures occurred on macOS / Python < 3.11:
|
||||
|
||||
### 1) Unconditional `import tomllib` at Module Level in Test Files
|
||||
- **Failure 1**: [`tests/test_issue45_herdr_config.py:16`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1/tests/test_issue45_herdr_config.py#L16)
|
||||
```python
|
||||
import tomllib
|
||||
```
|
||||
- **Error**: `ModuleNotFoundError: No module named 'tomllib'`
|
||||
- **Impact**: Pytest fails collection immediately with exit code 2. The entire test module cannot be run on Python < 3.11 (such as Python 3.9 in the project virtualenv).
|
||||
- **Context**: While [`lib_py/herdr_config.py:152-155`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1/.agents/skills/lib_py/herdr_config.py#L152-L155) guards the import with `try ... except ImportError: tomllib = None`, the test module itself imports `tomllib` unconditionally at line 16 and in test functions.
|
||||
|
||||
- **Failure 2**: [`tests/test_issue9_server_env.py:1592`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1/tests/test_issue9_server_env.py#L1592)
|
||||
```python
|
||||
def test_bootstrap_hands_the_server_a_no_restore_config(mam_sandbox):
|
||||
import tomllib
|
||||
```
|
||||
- **Error**: `ModuleNotFoundError: No module named 'tomllib'`
|
||||
- **Impact**: Test fails immediately on Python < 3.11.
|
||||
|
||||
---
|
||||
|
||||
### 2) Non-Portable Linux Assumptions in Real Herdr Server Test (`test_issue45_restore.py`)
|
||||
Four out of 5 tests in [`tests/test_issue45_restore.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1/tests/test_issue45_restore.py) fail on macOS:
|
||||
|
||||
- **Failure 3 (Linux `/proc` filesystem hardcoding)**: [`tests/test_issue45_restore.py:227`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1/tests/test_issue45_restore.py#L227)
|
||||
```python
|
||||
environ = Path(f"/proc/{rig.server_pids()[0]}/environ").read_bytes().split(b"\0")
|
||||
```
|
||||
- **Error**: `FileNotFoundError: [Errno 2] No such file or directory: '/proc/26854/environ'`
|
||||
- **Impact**: Tests `test_a_mam_server_does_not_relaunch_saved_agents[plain-user-config]` and `test_a_mam_server_does_not_relaunch_saved_agents[user-config-with-session-table]` both crash. macOS (Darwin) does not have a `/proc` filesystem. Reading `/proc/<pid>/environ` is non-portable.
|
||||
|
||||
- **Failure 4 (macOS `/tmp` vs `/private/tmp` symlink mismatch)**: [`tests/test_issue45_restore.py:263`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1/tests/test_issue45_restore.py#L263)
|
||||
```python
|
||||
assert _pane_with(rig, ids["Y"])["cwd"] == str(rig.root)
|
||||
```
|
||||
- **Error**: `AssertionError: assert '/private/tmp/m45phdbspj1' == '/tmp/m45phdbspj1'`
|
||||
- **Impact**: `test_b_only_the_resumed_sessions_restored_shell_is_closed` fails. On macOS, `/tmp` is a symlink to `/private/tmp`. Herdr canonicalizes working directories to `/private/tmp/...`. Exact string comparison against uncanonicalized `str(rig.root)` fails. Comparing with `Path(cwd).resolve()` or `os.path.samefile` is required.
|
||||
|
||||
- **Failure 5 (Pane lookup failure due to uncanonicalized `/tmp` path)**: [`tests/test_issue45_restore.py:325`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1/tests/test_issue45_restore.py#L325)
|
||||
```python
|
||||
pane = next(p for p in rig.panes() if p.get("cwd") == str(ws))
|
||||
```
|
||||
- **Error**: `StopIteration`
|
||||
- **Impact**: `test_b_resume_leaves_no_restored_shell_behind` fails. `ws` is constructed under `/tmp/...`, while `p.get("cwd")` is reported as `/private/tmp/...`. The generator finds no match and raises `StopIteration`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Evaluation of Brief Review Questions
|
||||
|
||||
1. **A's config merging**:
|
||||
- On Python 3.11+, parsing and rewriting logic in `lib_py/herdr_config.py` preserves tables, dotted keys, and verifies with `tomllib.loads(text) == expected` followed by `herdr config check`.
|
||||
- On Python < 3.11 (e.g. system Python 3.9), `tomllib` is absent, causing `server_config_text()` to emit a warning and fall back to `MINIMAL` (`[session]\nresume_agents_on_restore = false`), thereby losing user-defined herdr settings on Python 3.9 systems.
|
||||
2. **Coverage across server boot paths**:
|
||||
- `lib.sh:230-236` runs `python3 -m lib_py.herdr_config server-config` during headless bootstrap.
|
||||
- Accurately documented that manually attached/started servers outside MAM do not pick up `mam-server-config.toml`.
|
||||
3. **B's pane closing conditions**:
|
||||
- Clean fail-safe in `lib_py/herdr_restore.py`: checks `HERDR_SESSION_NAME` is not default, ensures no live agent is running in the pane, checks matching `agent_session`, and verifies foreground process group is the shell. If herdr cannot be queried, it fails-safe by leaving panes untouched and emitting a warning in `resume_session.sh:614`.
|
||||
4. **Test isolation & portability**:
|
||||
- Test suites properly use temporary HOME and isolated session names, preventing mutation of live user servers or configs.
|
||||
- However, portability on macOS is broken due to hardcoded `/proc` access and uncanonicalized `/tmp` symlink path comparisons.
|
||||
5. **Version and Inventory**:
|
||||
- Framework and 8 skills were updated to `4.8.5`.
|
||||
- `tests/data/skill_code_blocks.json` updated line ranges for `create` and `resume` while preserving classification.
|
||||
6. **PR purge failure note**:
|
||||
- Baseline purge test passes 23/23 on macOS and is unrelated.
|
||||
7. **Boundary (#17)**:
|
||||
- Complex logic is implemented in Python (`herdr_config.py`, `herdr_restore.py`), while shell scripts delegate to them.
|
||||
|
||||
---
|
||||
|
||||
## 4. Test Suite Execution Summary
|
||||
|
||||
Executed in verification worktree (`/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1`):
|
||||
|
||||
| Test Suite | Result | Details |
|
||||
|---|---|---|
|
||||
| `tests/test_issue45_herdr_config.py` | **FAILED** | Collection error: `ModuleNotFoundError: No module named 'tomllib'` at line 16 |
|
||||
| `tests/test_issue9_server_env.py` | **FAILED** | `test_bootstrap_hands_the_server_a_no_restore_config` failed at line 1592 (`ModuleNotFoundError`) |
|
||||
| `tests/test_issue45_restore.py` | **FAILED** (4/5 failed) | Lines 227 (`/proc` FileNotFoundError), 263 (`/tmp` vs `/private/tmp` AssertionError), 325 (`StopIteration`) |
|
||||
| `tests/test_issue23_24_startup_path.py` | 27 passed | Clean |
|
||||
| `tests/test_issue42_skill_blocks.py` | 37 passed, 1 skipped | Clean |
|
||||
| `tests/test_version_consistency.py` | 3 passed | Clean |
|
||||
|
||||
---
|
||||
|
||||
## 5. Conclusion
|
||||
|
||||
Due to the critical test failures on macOS and Python < 3.11 (`ModuleNotFoundError: tomllib` in test suites, Linux `/proc` hardcoding, and macOS `/private/tmp` path matching failures in `test_issue45_restore.py`), the review verdict is NOT PASS.
|
||||
|
||||
[VERDICT: NOT PASS]
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"loop_id": "loop-20261005T085016Z-c0f6eb",
|
||||
"base_sha": "e8d9151226d8139c7b5688e83d035a3a747aeb2d",
|
||||
"head_sha": "ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9",
|
||||
"tree_sha": "f49ffdc8e95dc316f752fd81b2d55a0efddf1a12",
|
||||
"review_commit": "ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9",
|
||||
"reviewer": "reviewer-grok-01",
|
||||
"job_id": "2a661116",
|
||||
"verdict": "PASS",
|
||||
"transport": "herdr",
|
||||
"evidence": "review-tree",
|
||||
"isolation": "worktree",
|
||||
"verified_at": "2026-10-05T09:05:24Z"
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Review: PR #48 round 3 @ ab93776
|
||||
|
||||
- Role: Reviewer (grok)
|
||||
- Identity: loop `loop-20261005T085016Z-c0f6eb`, base `e8d9151226d8139c7b5688e83d035a3a747aeb2d`, head `ab93776d6d1b8ca534c14b6be53f61dc3a0fa2a9`, tree `f49ffdc8e95dc316f752fd81b2d55a0efddf1a12`, one commit, clean snapshot
|
||||
- Scope: `git diff e8d9151 HEAD`. Four files, +125/−4, `git diff --check` clean. Main stayed `c5c7eff`.
|
||||
- Verified read-only at `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T085016Z-c0f6eb/1`.
|
||||
|
||||
## Codex R1–R3
|
||||
|
||||
| Codex | ab93776 |
|
||||
|---|---|
|
||||
| R1. `herdr_rejects` raising (`TimeoutExpired`, `OSError`) was treated as a pass, so an unchecked merge could be handed to the server. A schema error in that file makes herdr drop the whole file and turn restore back on. | `_write_checked` (`herdr_config.py:247-258`) no longer returns success on that exception. If the candidate is the merged body, it warns `not using an unchecked merge` and continues. The loop's next candidate is `MINIMAL` (`:241`). The merged text is not what remains: `test_an_unrunnable_check_never_hands_over_an_unchecked_merge` writes `confirm_close = "invalid-bool"`, raises on the check, and the file on disk parses as only `resume_agents_on_restore = false`. The user file is unchanged. |
|
||||
| R2. `_shell_in_foreground` used `==`, so `0/0`, `False/False`, `""/""`, and `-1/-1` closed a pane. | A value counts only when it is an `int`, not a `bool`, and `> 0` (`herdr_restore.py:46-55`). Both pids must pass, and they must be equal. `tests/test_issue45_restore_unit.py` stubs herdr: those eight odd pairs, a missing `process_info`, and a busy pane (`4242` vs `4300`) do not close; `4242/4242` does. |
|
||||
| R3. Reparse used `==`, and `nan != nan`, so one valid `nan` dropped the rest of the user's settings. | `_same_doc` (`herdr_config.py:140-149`) treats two floats as equal when both are `nan`, and walks dicts and lists. `server_config_text` uses it at `:171`. The three cases (top-level, array including `-nan`, nested inline table) keep `confirm_close` and warn nothing. |
|
||||
|
||||
## MINIMAL when the check cannot run
|
||||
|
||||
Codex asked to return no path if the check of `MINIMAL` also cannot run. This commit writes `MINIMAL` and warns `using the fixed minimal config unchecked` (`:257-258`).
|
||||
|
||||
That is the right call. `lib.sh` starts the server with restore on when the config path is empty. Returning `None` would reopen #45 in the one case where no check can be obtained (timeout, missing binary, `OSError`). `MINIMAL` is the constant `[session]\nresume_agents_on_restore = false\n` (`:51`), not a user document, and `test_real_herdr_accepts_every_result` already shows herdr 0.9.1 accepts it whenever `config check` can run. The unchecked write happens only when that command cannot be executed, so there is no schema verdict to wait for. Unknown nonzero exits are still ignored unless the output contains `parse error` or `using defaults` (`herdr_rejects`, `:190-191`); herdr uses a nonzero status for a dropped setting that does not drop the file.
|
||||
|
||||
## `_same_doc` beside `==`
|
||||
|
||||
For values a TOML round trip produces, it matches `==`, plus `nan`. Dict keys are compared as a set (`a.keys() == b.keys()`), list order is kept (`zip`), datetimes use `==`, and `inf` / `-inf` follow `==`.
|
||||
|
||||
Two pairs differ, both stricter: `True == 1` is true and `_same_doc(True, 1)` is false; `1 == 1.0` is true and `_same_doc(1, 1.0)` is false. The writer emits a bool before the int branch (`_value`, `:108-111`) and emits a float with `repr` (`:117`), so a document `tomllib` parsed does not come back as the other type. A round trip that did change type fails closed into `MINIMAL` with a warning, which `==` would have accepted.
|
||||
|
||||
## Scope
|
||||
|
||||
The delta is `herdr_config.py`, `herdr_restore.py`, the two new tests in `test_issue45_herdr_config.py`, and `tests/test_issue45_restore_unit.py`. No skill text, inventory, or version line. 4.8.5 is not on main; the fallback to the key-only file is already what `VERSIONS.md` describes.
|
||||
|
||||
## Tests
|
||||
|
||||
Python 3.9.6, the four files in the brief, `--basetemp=/tmp/2a66follow.bPQAFP`: **53 passed, 34 skipped** in 77.84s. The skips are the `needs tomllib` cases, including the new R1 and R3 tests. `test_issue45_restore_unit.py` ran.
|
||||
|
||||
Python 3.11, the same venv named in the brief, `tests/test_issue45_herdr_config.py` and `tests/test_issue45_restore_unit.py`, `HOME` pointed at an empty temp directory: **67 passed** in 0.41s.
|
||||
|
||||
No `mamt45` process remained. `~/.config/herdr/config.toml` mtime stayed `1784456394`, the directory mtime stayed `1791183576`, and `mam-server-config.toml` was not created there. Snapshot `git status` was empty.
|
||||
|
||||
[VERDICT: PASS]
|
||||
@@ -0,0 +1,69 @@
|
||||
# Review: PR #48 (fix/issue-45) @ 35935f9
|
||||
|
||||
- Role: Reviewer (grok)
|
||||
- Identity: loop `loop-20261005T064728Z-a4e9c8`, base `c5c7eff6f758c76ac37a138dbc3c301f3f7e10cf` (main, v4.8.4), head `35935f9e8a052ca9e9d9b806a62d17eb72afea78`, tree `dff9db13f3e018528d04a024acda942f501513ff`, 10 commits, clean snapshot
|
||||
- Verified read-only at `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1`. Main stayed `c5c7eff`. No product edit, no commit, no push.
|
||||
- Diff vs base: 25 files, `tests/test_issue10_13_purge.py` absent. Interpreter for the required pytest: `pr-48/.venv` Python 3.9.6. The shim's `python3` is `/usr/bin/python3`, also 3.9.6. CI (`deploy/gitea-ci.yml:94`) is Python 3.10. `tomllib` is stdlib only from 3.11. `requirements-dev.txt` is `pytest>=8.0`. `/usr/bin/python3` has no `tomli`. The venv has `tomli` and no `tomllib`.
|
||||
|
||||
The required command exits at collection. With `--continue-on-collection-errors` added only so the rest of the list could run: **5 failed, 192 passed, 1 skipped, 1 error** in 254.58s (`--basetemp=/tmp/667follow.YrMavO`). The first command, as written in the brief, was **1 collection error in 0.22s, exit 2**, and ran no test.
|
||||
|
||||
## What fails
|
||||
|
||||
### 1. The merge tests do not load on the Python this repo runs
|
||||
|
||||
`tests/test_issue45_herdr_config.py:16` is a top-level `import tomllib`. Collection raises `ModuleNotFoundError`. `tests/test_issue9_server_env.py:157` imports it inside `test_bootstrap_hands_the_server_a_no_restore_config` and fails the same way.
|
||||
|
||||
`lib_py/herdr_config.py:54-57` catches that import and sets `tomllib = None`. `server_config_text` then returns `MINIMAL` plus a warning (`:146-147`). A throwaway `HOME=/tmp/m45probe-min` run of `/usr/bin/python3 -m lib_py.herdr_config server-config` printed that warning and wrote only:
|
||||
|
||||
```toml
|
||||
# generated by multi-agent-mux (Issue #45) - do not edit
|
||||
# source: /tmp/m45probe-min/home/.config/herdr/config.toml
|
||||
[session]
|
||||
resume_agents_on_restore = false
|
||||
```
|
||||
|
||||
The throwaway user file's checksum was unchanged. That is the accepted "no parser, restore still off" path. It is also the path the shim takes on this host and on CI 3.10.
|
||||
|
||||
`VERSIONS.md:49` and `multi-agent-mux-create/SKILL.md:120` describe the generated file as the user's config with that one key forced false. `test_issue9_server_env.py:171` asserts the merged document (`ui.confirm_close` kept). On this `python3` the file is `MINIMAL`, so fixing only the test import still fails `:171`.
|
||||
|
||||
Alternative: in `herdr_config.py:54-57` and in both test imports, use `tomli` when `tomllib` is missing (`tomli.loads` / `tomli.TOMLDecodeError` match). Add `tomli` to `requirements-dev.txt`, and install it for the `python3` that `lib.sh:241` actually runs (`/usr/bin/python3` here, not the venv). Keep the `MINIMAL` warning only when neither library imports. With `tomli` assigned onto the module, the merge kept inline tables, dotted keys, quoted keys, arrays, and comments semantically, and a broken document or a non-table `session` fell back to `MINIMAL`. `herdr config check` on `confirm_close = "no"` in a throwaway file exited 1 with `parse error` and `using defaults`, and `herdr_rejects` (`:169-180`) reported it.
|
||||
|
||||
### 2. The #45 positive control never runs on macOS
|
||||
|
||||
`tests/test_issue45_restore.py:227-228` reads `/proc/<pid>/environ`. Both parametrizations of `test_a_mam_server_does_not_relaunch_saved_agents` raised `FileNotFoundError` there. The asserts above that line had already passed: the fake claude did not log `--resume`, the restored pane had no agent, and the foreground pid was the shell. The positive control at `:230-236` (plain server must relaunch) did not execute.
|
||||
|
||||
Alternative: build the `HERDR_CONFIG_PATH=...` needle, read `/proc/<pid>/environ` when that file exists, and otherwise use `ps eww -p <pid> -o command=` and search the raw output. Run `:230-236` even when the environ read fails, so a missing `/proc` cannot skip the control that shows restore-on still relaunches.
|
||||
|
||||
### 3. Restored cwd is the resolved path
|
||||
|
||||
`tests/test_issue45_restore.py:263`:
|
||||
|
||||
```text
|
||||
AssertionError: assert '/private/tmp/m45swco8dxx' == '/tmp/m45swco8dxx'
|
||||
```
|
||||
|
||||
`mkdtemp(dir="/tmp")` returns `/tmp/...`. On this Mac `/tmp` is `/private/tmp`, and herdr's pane `cwd` is the resolved path. The close calls start at `:267`, so the selective-close asserts (live agent Z, busy `sleep` X2, user pane U, close only X and Y) did not run. `test_b_resume_leaves_no_restored_shell_behind` got create rc 0 and `status == running`, then `StopIteration` at `:325` on `p.get("cwd") == str(ws)` with that same unresolved `/tmp` path.
|
||||
|
||||
`test_b_default_server_is_never_touched` passed: `HERDR_SESSION_NAME=default` returns before invoking `--herdr /nonexistent/herdr` (`herdr_restore.py:54-55`).
|
||||
|
||||
Alternative: compare `Path(cwd).resolve()` with `Path(rig.root).resolve()` at `:263`, and `Path(p["cwd"]).resolve() == Path(ws).resolve()` at `:325`.
|
||||
|
||||
## What holds
|
||||
|
||||
1. User file bytes are not the output. `output_path` (`herdr_config.py:192-205`) skips the source (including `samefile`) and a file whose marker source is not this source. Both names taken raises `OSError`; the fallback directory goes through the same helper (`:270-276`). Writes are `mkstemp` + `os.replace` (`:208-216`). A generated `HERDR_CONFIG_PATH` is followed one hop via the `# source:` line (`:79-87`). The trace beside the user config is `mam-server-config.toml` (or `mam-server-config-<sha256(realpath)[:12]>.toml`), marker plus `# source: <path>`. A `nan` value makes `tomllib.loads(text) == expected` false (`:159`, `nan != nan`) and falls back to `MINIMAL` with a warning. The user file stays.
|
||||
|
||||
2. The only server spawn is the shim heredoc `lib.sh:212-261` (`$REAL_HERDR --session <name> server`). create (`create_session.sh:27` sources `lib.sh`, `:264` `_herdr new-session`) and resume (`resume_session.sh:8`, `:245`) go through that shim. The bootstrap runs only when the name is not `default`, `MAM_NO_AUTOSTART` is not `1`, and `session list` says the server is down (`lib.sh:193-212`). `status.sh:13` sets `MAM_NO_AUTOSTART=1` before sourcing. `reconcile.sh:28` sets it before herdr calls; the bootstrap is in the shim, not at source time. An already-running server is left as it was. `create/SKILL.md:120-121` and `VERSIONS.md:50` say the file is rebuilt the next time MAM starts the server, and a server the user starts with `herdr session attach` does not get it.
|
||||
|
||||
3. A pane is closed only when the session name is set and not `default`, the pane has no live agent, `agent_session.value` equals the id being resumed, and `shell_pid` equals `foreground_process_group_id` (`herdr_restore.py:54-65`). Empty id and `default` return `[]` before herdr. `main` maps `OSError`, `SubprocessError`, and `ValueError` to exit 1 (`:81-83`). `resume_session.sh:240-242` calls that and on failure only prints a warning; dry-run has already exited at `:225-228`. Bash does not decide which pane. There is a gap between `process-info` and `pane close` in which a command can start; a failed query still closes nothing further.
|
||||
|
||||
5. `MAM_VERSION` and `FRAMEWORK_VERSION` are `4.8.5`. The eight `SKILL.md` frontmatters match. `tests/data/skill_code_blocks.json` changes line numbers and the resume script anchors (`:237/:241/:247-250` → `:245/:249/:255-258`). No class, kind, or sha field changes. `test_version_consistency` and `test_issue42_skill_blocks` are in the 192 passes. S11's base moved to `811b080` (`tests/test_issue23_24_startup_path.py`) because the heredoc change is intentional.
|
||||
|
||||
6. `tests/test_issue10_13_purge.py` is not in the diff. Those claimed full-suite failures are not produced by this PR. This review did not run that file.
|
||||
|
||||
7. New judgment is in `herdr_config.py` and `herdr_restore.py`. The new bash is "config path empty → warning, start anyway" (`lib.sh:243-245`) and the resume warning above. The spawn snippet copies `MAM_SERVER_CFG` into the child as `HERDR_CONFIG_PATH` and drops `MAM_SERVER_CFG` from the child (`lib.sh:253-256`).
|
||||
|
||||
## Isolation
|
||||
|
||||
Restore tests use `mkdtemp(prefix="m45", dir="/tmp")`, session `mamt45-<pid>-<uuid4>`, and strip `HERDR_WORKSPACE_ID`, `HERDR_PANE_ID`, `HERDR_TAB_ID`, `HERDR_SOCKET_PATH`, `HERDR_SESSION`, `HERDR_ENV`, `HERDR_CONFIG_PATH`, and `XDG_CONFIG_HOME` (`test_issue45_restore.py:41-42`, `:160-164`). After the run, no `mamt45` process remained. `~/.config/herdr/config.toml` mtime stayed `1784456394`, the directory mtime stayed `1791183576`, and no `mam-server-config.toml` appeared there. The live `multi-agent-mux` server log grew on its own during the window. Snapshot `git status` was empty afterward. Throwaway pytest dirs were removed.
|
||||
|
||||
[VERDICT: NOT PASS]
|
||||
@@ -0,0 +1,32 @@
|
||||
# Review: PR #48 round 2 @ e8d9151
|
||||
|
||||
- Role: Reviewer (grok)
|
||||
- Identity: loop `loop-20261005T081844Z-e92124`, base `c5c7eff6f758c76ac37a138dbc3c301f3f7e10cf`, head `e8d9151226d8139c7b5688e83d035a3a747aeb2d`, tree `8b39188453ff8a7263aa4400792fabc99188dad6`, clean snapshot, ancestor of both `c5c7eff` and round-1 `35935f9`
|
||||
- Scope for this reviewer: `git diff 35935f9 e8d9151` only (commits `36f0b8d`, `e8d9151`). Five files, +56/−9, `git diff --check` clean. No product code. Main stayed `c5c7eff`.
|
||||
- Verified read-only at `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T081844Z-e92124/1`.
|
||||
|
||||
## Round-1 findings
|
||||
|
||||
| Round 1 | e8d9151 |
|
||||
|---|---|
|
||||
| `tests/test_issue45_herdr_config.py:16` imported `tomllib` at module level, so Python 3.9/3.10 could not collect the file. `tests/test_issue9_server_env.py:157` did the same inside the bootstrap test, and expected the merged document from a 3.9 `python3` that writes only the key. | Import is `try/except` (`test_issue45_herdr_config.py:18-21`). `needs_tomllib` skips the 29 tests that parse a result or expect the user's other settings (`:36`, applied at `:71` and the other parse sites). `test_without_tomllib_restore_is_still_off` still runs. `test_issue9_server_env.py:173-183` parses with the `python3` on `PATH` and, when that interpreter has no `tomllib`, expects the key-only file and `no tomllib` in the shim stderr. `create/SKILL.md:120` and `VERSIONS.md:49` say a `python3` older than 3.11 leaves restore off, drops the other herdr settings, and warns. |
|
||||
| `tests/test_issue45_restore.py:227` read `/proc/<pid>/environ` before the restore-on control, so macOS never reached that control. | `_process_env` (`:160-168`) reads `/proc/<pid>/environ` when it exists, otherwise `ps -E -ww -o command=`. The control stays after that check (`:242-248`). On this Mac the herdr server's `ps -E` line contains `HERDR_CONFIG_PATH=<resolved>/mam-server-config.toml`, and both `test_a` cases passed, so the control ran. |
|
||||
| `:263` and `:325` compared pane `cwd` to the unresolved `/tmp/...` from `mkdtemp`. herdr reports `/private/tmp/...`. | The fixture resolves the temp root (`:175`) before `HOME` is set, so later `str(rig.root)` and `str(ws)` match herdr. `test_b_only_the_resumed_sessions_restored_shell_is_closed` and `test_b_resume_leaves_no_restored_shell_behind` passed. |
|
||||
|
||||
The tomli backport suggested in round 1 is not in this delta. The docs now describe the path this host's `/usr/bin/python3` (3.9.6, no `tomli`) actually takes. That matches the decision to document it.
|
||||
|
||||
`tests/data/skill_code_blocks.json` is unchanged. The create skill edit is the same line 120, so no fence moved. `test_issue42_skill_blocks` passed, with the usual worktree skip at `:266` because `.claude/skills` is absent in the snapshot.
|
||||
|
||||
## Tests
|
||||
|
||||
Required command, PR venv Python 3.9.6, `--basetemp=/tmp/b471follow.eYGqXM` (no `mam-review` in the path), MQTT variables unset:
|
||||
|
||||
**152 passed, 30 skipped** in 214.33s. The 30 skips are the 29 `needs tomllib` cases plus `test_issue42_skill_blocks.py:266`.
|
||||
|
||||
Same file on a throwaway Python 3.11.15 venv (`/opt/homebrew/bin/python3.11`), `HOME` set to an empty temp directory:
|
||||
|
||||
**51 passed** in 0.38s, none skipped.
|
||||
|
||||
Afterward no `mamt45` process remained. `~/.config/herdr/config.toml` mtime stayed `1784456394`, the directory mtime stayed `1791183576`, and `mam-server-config.toml` was not created there. Snapshot `git status` was empty.
|
||||
|
||||
[VERDICT: PASS]
|
||||
Reference in new issue
Block a user