Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
9.7 KiB
9.7 KiB
PR #69 (Issue #63) 리뷰 — head 1614af5
- 리뷰어: planner-reviewer-claude-01 (macOS, bash 3.2, /usr/bin/python3 3.9.6)
- 검증 경로:
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261023T035928Z-2ccc02/1 - identity 확인: HEAD
1614af5b67, treef1af6fdd54,git status --porcelain --ignored비어 있음,.mam없음 (시작·종료 시 모두). main HEADdc886ec그대로. - 범위: base dc886ec..1614af5 전체(커밋 13개).
- 참고: 같은 head 를 맡은 잡 906427d1 은 관련 스위트 도중 검증 경로(
…a646cb/3)가 사라져 끝내지 못했다. 그 잡에서 얻은 head 결과(issue63 27 passed, 변이 결과)는 같은 tree 의 스크래치 사본에서 나온 것이다.diff -rq로 이번 검증 경로와 같음을 확인했다.
1. 1614af5 가 바꾼 것
.agents/skills/lib_py/agents/id_order.py:223:elif sess == me or not sess:를elif not sess:로 바꿨다.- 그래서 호출자 자신의 세션 이름에 남은 죽은 owner 마커도 고아 규칙을 따른다. 이번 시도의 행이 등록되면 지우고(:225), herdr 가 정상 목록으로 "없음"이라 답하면 지우고(:228-229), 그 밖에는 남긴다.
_read_and_prune_markers의me인자를 지웠다(:183, :308).blockers는ede9c62부터m_sess == me를 건너뛰지 않는다(:136-144). 그래서 남은 자기 이름 마커가 실제로 막는다.
2. 세 수정이 각 지적을 닫는가, 서로 어긋나지 않는가
8f64174— 조회 실패를 "없음"으로 읽던 문제herdr_session_state(:58-86) 는 정상 파싱한agent list에 이름이 없을 때만 False 를 돌려준다. 그 밖에는 None 이다.- prune 은
drop = live is False(:228) 다. - 변이 m1(
not live)과 m2(rc≠0 → False)가 테스트에 잡혔다(잡 1c6eca2b).
7cac6f6— 상한 0 에서 30 초 조회, server_not_running- 조회 시간은
deadline - now다(:288). timeout≤0 이면 None 을 돌려준다(:64-65). - 변이 m4(고정 30 s)와 m7(timeout≤0 에도 조회)이 잡혔다.
- 상한 0 테스트는 0.3 s 안에 끝나는 것을 확인한다(test :1222-1224).
- 조회 시간은
1614af5— 같은 이름의 죽은 owner 마커를 확인 없이 지우던 문제- 옛 규칙(
sess == me이면 drop)을 되돌린 변이 사본에서는 두 테스트가 실패한다:test_id_order_dead_owner_marker_blocks_while_its_unregistered_herdr_session_is_alive,test_id_order_real_probe_against_mock_herdr_fails_closed. - 커밋 메시지가 말한 "재시도의 실패 정리가 살아 있는 pane 을 죽임"도 막힌다. create 의 대기(
create_session.sh:287)는 롤백 trap 설치(:367)보다 앞서 있어서, 대기에서 거부(exit 1)되면cleanup_herdr_on_error가 돌지 않는다.
- 옛 규칙(
- 정합성: 세 수정은 모두
_read_and_prune_markers의 "죽은 owner 마커" 분기 하나를 쓰고, 조회는bounded_probe하나를 거친다. 자기 이름 마커만 다른 규칙을 쓰던 예외가 사라져서 규칙이 하나로 모였다. 어긋나는 곳은 찾지 못했다.
3. 흔한 사용을 600 초 대기로 막지 않는가
스크래치에서 id_order.wait 를 프로세스 안에서 직접 불렀다. 조회 함수를 주입하고, cap 2 s(일부 0 s)로 돌렸다.
정상 사용 (실패 없음): 막지 않는다.
- create 마커가 남은 채로, 그 세션이 다음 상태일 때 같은 이름으로 resume 했다:
- stopped 이고 own id 가 기록됨
- stopped 이고 id 없음 (herdr-dead stop)
- reconcile 이 terminated 로 바꿈
- 세 경우 모두 herdr 가 답하지 못해도(None), cap 0 에서도 rc=0 이었다.
blockers의pinned_for_attempt와ended_after_attempt(:158-171) 가 이 경우들을 걸러낸다.- 정상 create/resume 이 끝난 뒤의 마커는 행이 running 인 동안에는
_registered_for_attempt(:177-180) 로 지워진다.
실패 뒤 재시도:
| 경우 | herdr 가 "없음" 응답 (서버 살아 있음) | herdr 무응답 (서버 꺼짐 등) |
|---|---|---|
| create X 가 행 쓰기 전에 실패(exit 42 롤백) → create X 재시도 | rc=0, 0.0 s | rc=1, cap 만큼 대기 |
| resume X 가 spawn 에서 실패(행 그대로) → resume X 재시도 | rc=0, 0.0 s | rc=1, cap 만큼 대기 |
| 위 resume 실패 → 같은 cwd 에서 create Y | rc=0, 0.0 s | rc=1, cap 만큼 대기 |
7cac6f6사본으로 같은 시뮬레이션을 돌리면 무응답 열의 위 두 줄(같은 이름 재시도)이 rc=0 이다.- 즉
1614af5는 서버가 살아 있는 흔한 재시도는 그대로 두고, herdr 가 답하지 못하는 재시도만 막는다. 이는 아래 R1 이 같은 이름까지 넓어진 것이다.
4. 그 밖의 확인
브리프 항목 1·2·4·5·6·8(완료 조건 테스트, autopin 재사용, resume·stop, 지운 심볼, issue34 무관)은 1614af5 가 session_ids.py, lib.sh, 셸 스크립트, 문서를 건드리지 않으므로 이전 리뷰의 결론 그대로다.
macOS:
/usr/bin/python33.9.6 으로lib_py.agents.id_orderimport 가 성공한다(잡 1c6eca2b;1614af5는 문법 요소를 더하지 않는다).- 잠금은
fcntl.flock이다(:294-295).
실제 herdr 를 건드리지 않았는가:
- 기존 서버 두 개(pid 3407
herdr server, 8323herdr --session multi-agent-mux server)는 스위트 전후로 같다(시작 시각 08:21). - 스위트 중 13:22 에
herdr --session mam-pylecturenotes server가 새로 보였다. 하지만 이 프로세스는 실제 HOME 과WORKSPACE_ROOT=/Users/godopu16/PuKi/lac/curriculum/01_python_getting_started_lecturenotes로 떠 있고, 사용자의 attach 클라이언트(13:27)가 붙어 있다. 테스트는 HOME 을 스크래치로 바꿔 돌므로 다른 작업 공간의 것이다.
5. 실행한 테스트
모두 env -i, 임시 HOME/XDG, PYTHONDONTWRITEBYTECODE=1 -p no:cacheprovider --basetemp=<scratchpad> 로 돌렸다. 테스트에서 .mam.env 를 export 하지 않았다.
| 대상 | 결과 |
|---|---|
검증 경로: tests/test_issue63_same_cwd_ids.py + 관련 19개 모듈 (issue40_auto_pin, issue64_ro_connect, issue10_13_purge, issue31_purge_residual, issue42_skill_blocks, tier1_unit, tier2_component, b4_session_created, issue24_resume_dialogs, issue30_stall_watch_stop, issue23_24_startup_path, issue45_restore, issue50_pane_lock, c1_tui_readiness, issue6_fail_closed, issue60_opencode_v2, issue60_linux_fixes, uuid_target, issue17_workspace_root_isolation) |
597 passed, 2 skipped, 0 failed (32 분) |
| 같은 tree 스크래치 사본: issue63 단독 | 27 passed |
| base 사본 + head 테스트 | 27 errors (ImportError: id_order). 조건별 base 실패는 1차에서 22 failed + 1 passed 로 확인했다 |
변이: 1614af5 를 되돌림(자기 이름 마커를 drop) |
2 failed (위 두 테스트) |
6. 차단 결함
없음.
7. 비차단
R1 (잡 1c6eca2b 의 R1, 범위가 넓어짐): herdr 가 답하지 못하는 고아 마커는 같은 폴더·같은 종류의 create/resume 을 매번 cap 동안 붙잡는다
- 이제 같은 이름 재시도도 포함된다(위 표).
- 서버가 꺼진 상태(server_not_running)가 대표적이다.
- 조회는 서버를 띄우지 않는다(:67).
- create/resume 은 대기를 통과해야 spawn 으로 서버를 띄운다(
create_session.sh:287→:289,resume_session.sh:243→:260). - 그래서 다른 무언가가 서버를 켜기 전까지는 기본 600 s 를 기다린 뒤 거부가 반복된다.
- 회복 경로는 있다: 메시지의
HERDR_SESSION_NAME=<srv> herdr kill-session -t <s>를 shim 으로 실행하면 서버가 기동된다(잡 1c6eca2b 의 가짜 herdr 재현에서--session srv-down server1회). - 생기는 조건은 "행 등록 전 실패나 kill"에 "서버가 꺼짐"이 겹치는 경우다. 정상 사용과 서버가 살아 있는 재시도는 막지 않는다(3절).
- 권고 (둘 중 하나):
- (a) server_not_running 만은 "없음"으로 읽는다. MAM 서버는 restore 가 꺼져 있어서 꺼진 서버 위에는 살아 있는 pane 이 없다.
lib.sh:204-207의 ISSUE-9 규약과도 맞는다. - (b) 그 경우는 cap 을 기다리지 말고 바로 거부한다. 메시지는 "서버가 꺼져 있음 → 위 명령으로 정리"처럼 사실대로 쓴다.
- (a) server_not_running 만은 "없음"으로 읽는다. MAM 서버는 restore 가 꺼져 있어서 꺼진 서버 위에는 살아 있는 pane 이 없다.
- fail-closed 를 택한 것은 루프에서 정한 설계 선택이라 차단으로 두지 않는다.
그 밖의 비차단 (잡 1c6eca2b 와 같음)
- N1: 조회가
MAM_NO_AUTOSTART=1을 쓴다는 것(:67)을 고정하는 테스트가 없다. - N2: sanitize 한 이름 비교(:85)를 고정하는 테스트가 없다.
- N3:
flock(LOCK_EX)대기(:295)는 시간 제한이 없다. 다른 호출자가 앞 대기의 남은 시간만큼 더 기다릴 수 있다. 상한은 있다. - N4: create·resume SKILL.md 의 exit 1 설명이 고아 마커로 거부되는 경우와 그 정리 방법을 말하지 않는다.
- N5: 이전 라운드의 비차단 항목이 남아 있다: resume
mam_id_order_wait제거가 테스트로 고정되지 않음, c6(b) 의 1.5 s 타이밍 flake, lib.sh--herdr-server전달이 테스트되지 않음.
8. 결론
1614af5는 codex 의 3차 지적(같은 이름의 죽은 owner 마커를 확인 없이 지움)을 닫는다. 8f64174·7cac6f6 과 같은 고아 규칙 하나로 모였다.- 정상 create/resume 과 서버가 살아 있는 재시도는 막지 않는다.
- herdr 가 답하지 못할 때 cap 동안 기다리는 비용(R1)은 회복 경로가 있는 설계 선택이므로 비차단이다.
[VERDICT: PASS]