Review loop (--review-only, --all-reviewer, focused on this Mac: macOS, /usr/bin/python3 3.9.6, bash 3.2): round 1 (@6f4f40a) agy and grok PASS, claude and codex NOT PASS (a non-uuid id `memory` purged the auto-memory dir, a linked dir below a root was followed, a NaN startedAt counted as dead), sent as a PR comment and fixed by the author in eleven commits; round 2 (@2a8d738) confirmed all three fixed but 4/4 NOT PASS on one test holder (`sh -c "sleep 120" <uuid>` loses the uuid on macOS /bin/sh), fixed in de6f3fc; round 3 (@de6f3fc) 4/4 PASS. Full suite on macOS / Python 3.9.6 @de6f3fc: 1934 passed, 56 skipped, 1 error (the known #55 false positive: agy conversations of another workspace). The real ~/.claude, /private/tmp/claude-<uid> and herdr panes unchanged. Also names lib_py.delegation.logroot in eager.CORE_MODULES (the v4.9.4 review's non-blocking note). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
9.3 KiB
PR #59 2차 Mac 리뷰 — NOT PASS
- Job:
a59e709d; loop:loop-20261017T135731Z-e93f0a - Base:
26e2c43753169671b600d03274901dbe203988dd - Head:
2a8d73825b3832376aeaf96b09548359dc3cb887 - Tree:
d146f1ac68bcc3485d24fb92edb87264e4d1e8cb - 검증 트리:
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261017T135731Z-e93f0a/1 - 환경: macOS, 시스템 Python 3.9.6, bash 3.2, BSD ps. pytest는 지정된 제어 저장소의
.venv/bin/python으로 실행.
기존 3건의 결함은 이번 HEAD에서 해결됐고 수정 전 실패도 확인했다. 다만 새 UUID 대소문자 회귀 테스트 2건이 macOS에서 실패한다. 원인은 제품의 UUID 정규화가 아니라 테스트가 UUID 없는 프로세스를 생성하는 데 있다. 현재 HEAD를 이 Mac에서 통과한 상태로 승인할 수 없으므로 NOT PASS다. 수정 범위는 테스트 픽스처이며 재계획은 필요하지 않다.
수정 필요: [P2] UUID 명령행 보유자 픽스처가 macOS에서 UUID를 잃음
근거: tests/test_issue31_purge_residual.py:315의 subprocess.Popen(["sh", "-c", "sleep 120", conv]), 이어지는 :318의 live 단언. upper와 mixed 두 매개변수에서 동일하게 실패한다.
macOS의 sh는 마지막 명령을 실행하면서 동일 PID의 명령행을 sleep 120으로 바꾼다. conv는 셸의 $0였으며 sleep 인자로 전달되지 않는다. 앞서 live.kill() / live.wait()로 세션 파일의 PID도 죽였기 때문에 이 시점에는 세션 파일이나 명령행 어디에도 살아 있는 보유자가 없다. 어댑터가 False를 돌려주는 것은 이 픽스처 상태에서 올바르다.
재현 절차:
-
검증 트리에서 아래 테스트 노드를 실행한다. 임시 HOME/HOME_DIR, CLAUDE_PROJECT_DIR, CLAUDE_CODE_TMPDIR, TMPDIR를 사용하고 브리프의 HERDR/MAM/WORKSPACE 환경 변수들을 제거한다. 테스트의
home픽스처는 CLAUDE_CONFIG_DIR를 제거하여 임시 HOME/.claude를 쓰며,own_tmp_roots는 고정 /tmp fallback까지 임시 경로로 대체한다.PYTHONDONTWRITEBYTECODE=1 /Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python -m pytest -p no:cacheprovider --basetemp=<검증트리밖의새임시경로> -q tests/test_issue31_purge_residual.py::test_any_spelling_of_the_uuid_is_the_lowercase_session -
독립적으로 새 UUID를 생성하고
Popen(["sh", "-c", "sleep 120", uuid])를 띄운 뒤 0.1초 후ps -p <그 PID> -o args=를 읽는다. 관측 결과는sleep 120으로 UUID가 없다. 종료 시 생성한 프로세스만 kill/wait한다. -
검증 트리 HEAD의 git archive 임시 복사본에서 해당 한 줄만
Popen([sys.executable, "-c", "import time; time.sleep(120)", conv])로 대체하고 같은 두 테스트를 실행한다.
기대/실제: 원래 테스트는 lowercase UUID를 명령행에 가진 살아 있는 프로세스를 만들고 upper/mixed ID 모두 in-use=True임을 검증해야 한다. 실제로는 UUID 없는 sleep을 만들어 두 건 모두 :318에서 실패했다. 임시 복사본의 한 줄 대체 대조군에서는 2 passed in 2.36s, 종료 코드 0이었다. 별도 ps 확인에서도 Python 프로세스는 UUID 인자를 유지했다.
수정 방향: 셸의 마지막 명령 최적화에 의존하지 않는 프로세스를 생성한다. 이미 import한 sys.executable을 사용한 위 Python sleep 픽스처가 최소 수정이다. 생성 PID의 명령행에 UUID가 실제로 남아 있는지 확인하고 기존 finally의 kill/wait 정리는 유지한다. 원본 검증 트리나 creator 트리는 수정하지 않았다.
기존 지적의 해결 및 수정 전 실패 검증
현재 테스트 파일을 각 수정 직전 커밋의 격리된 git archive 복사본에 놓고 해당 노드를 실행했다. 테스트 임시 루트 격리 픽스처도 함께 사용했다. 이는 예전 테스트가 아니라 새 회귀 단언이 예전 구현을 실제로 거부하는지 확인하기 위한 것이다.
| 기존 지적 | 현재 구현 및 HEAD 결과 | 수정 전 결과 |
|---|---|---|
UUID 아닌 memory가 공유 폴더를 삭제 |
claude.py:10 정규 UUID 확인/소문자 변환, :301 후보 경로 구성 및 :368, :390 purge/residual 진입 가드. malformed/non-UUID 보존 테스트 통과. upper/mixed의 세션 파일 기반 in-use 및 CLI exit 4 단계도 통과 |
bd09c6a^에서 test_claude_rejects_malformed_uuid[memory] 실패: 기대 []와 달리 memory 관련 경로 3개 삭제 |
| 상위 심볼릭 링크를 통한 범위 밖 삭제 | claude.py:105의 _inside가 루트 아래 상위 구성요소를 lstat하고 :336의 _owned_paths가 필터링. tmp key/project key/file-history 링크 뒤 파일 보존, leaf 링크만 삭제, 루트 자체 링크 허용 테스트 통과 |
6e0361c^에서 test_claude_purge_never_follows_a_linked_dir_below_a_root 실패: 링크 뒤 경로 4개 삭제 |
| NaN/inf/과대 정수 startedAt이 죽음으로 판정 | claude.py:69의 _session_file_live: bool/type/finite/positive 검사 및 float 변환 OverflowError 처리로 판정 불가를 live로 유지. 숫자 경계값 및 CLI 테스트 통과 |
f943b0d^에서 [NaN] 테스트 실패: 살아 있는 PID인데 False. 추가로 60779ba^에서 [10**400] 테스트 실패: OverflowError |
각 수정 전 실행은 1 failed였으며, 동일 노드들이 현재 HEAD의 관련 모듈 실행에서는 통과했다. 기존 3건을 계속 미해결인 제품 결함으로 보고하는 것은 아니다.
검증 결과와 범위
다음 네 모듈을 함께 실행: tests/test_issue31_purge_residual.py, tests/test_issue42_skill_blocks.py, tests/test_issue55_agent_homes.py, tests/test_a4_adapter_contract.py.
- 원본 HEAD: 146 passed, 2 failed, 1 skipped in 37.80s. 위 P2의 upper/mixed 두 건만 실패. 전체 스위트는 실행하지 않았다.
- 임시 복사본의 픽스처 대조군: 위 두 건만 2 passed. 이를 원본 HEAD 전체 통과로 합산하지 않는다.
- 변경 Python 파일의 시스템 Python 3.9 구문 검사, 변경 셸 파일의 bash 구문 검사,
git diff --check통과. - 종료 전 HEAD/tree 재확인 일치, 검증 트리
git status --short출력 없음. - 실제 Claude 실행/실제 세션 purge·stop·in-use는 수행하지 않았다. destructive 테스트는 임시 홈/프로젝트/config/tmp에서만 실행. issue31 외 모듈에도 실제
/tmp/claude-<uid>에 접근하지 않도록 별도 테스트 프로세스용 가드를 적용했다.
추가 검토 사항
- BSD ps 시작 시각:
LC_ALL=C,TZ=UTC0로 읽어calendar.timegm과 epoch-ms를 비교한다. 실제 임시 sleep 프로세스의 live 판정, 재사용 PID 모형, 판정 불가, UTC/DST 회귀 테스트를 확인했다. startedAt+2초 비교는 유지됐다. - tmp 레이아웃: 실제 경로는 디렉터리 이름/종류 메타데이터만 확인했다. 이 Mac의
/tmp는/private/tmp로 연결되고 실제 per-uid scratchpad는 workspace key 아래 UUID 디렉터리 구조였다. TMPDIR는 별도의/var/folders/.../T/로 해석된다. override/base/per-uid/realpath 중복 제거 테스트 통과. - 실제 ~/.claude의 projects, file-history, session-env, sessions, jobs 디렉터리 구조는 구현이 가정한 배치와 부합했다. 실제 세션 파일 및 key 내용은 읽지 않았다. 관측된 metadata 수: projects 디렉터리 16개, file-history 19개, session-env 21개, sessions JSON 6개와 key 6개, jobs 디렉터리 2개와 JSON 파일 1개. 이는 구조 확인이지 모든 실제 파일의 소유권을 검증했다는 뜻은 아니다.
dd292c1: 삭제 가능한_owned_paths대신 전체_candidates로 has_artifacts가 Claude를 선택하므로, 링크 뒤 잔여물만 있어도 auto purge는 Claude로 선택하여 보존 및 residual을 보고한다.test_purge_cli_reports_a_linked_orphan_it_will_not_remove통과.- 공유 파일/다른 UUID scratchpad/동일 접두사 jobs/다른 세션 metadata 보존 및 실패 후 ownership evidence 유지 검증을 관련 모듈에서 확인했다. key 경로는 보고 시 hex를 마스킹한다.
base.py:311,__main__.py:186,stop_session.sh:292의 안내 전달을 검토했다. stdout JSON과 stderr 힌트를 분리하고 stop은 in-use gate의 실패를 exit 4로 처리한다. exit 4 및 purge 시 registry 행 삭제 설명이 stop SKILL에 반영됐다. skill code block 관련 검사 통과.- agy에도 in-use gate가 적용되는 점은 공통 어댑터 계약의 의도된 동작이며 이 자체를 결함으로 판정하지 않았다.
test_tests_never_reach_the_real_tmp_root및 자식 Python의 fallback 주입 검증 통과. #55 관련 외부 agy 아티팩트를 결함 근거로 사용하지 않았다.- 상위 경로 링크 검사는 검사 시점의 디렉터리 구조에 관한 보장이다. 동시 파일시스템 변경을 포함한 원자적 삭제 보장까지 검증한 것은 아니다.
로컬 증거
임시 증거 루트: /tmp/review-a59e709d-4yb9c6z4 (macOS 실경로 /private/tmp/...).
head.log: 원본 HEAD 네 모듈 실행 결과.before-id.log,before-links.log,before-nan.log,before-overflow.log: 수정 전 실패 결과.fixture-control.log: 한 줄 픽스처 대체 대조군의 2건 통과.fixture-control/: HEAD archive의 임시 대조군. 제품 코드 변경 없음.
[VERDICT: NOT PASS]