Review loop (--review-only, --all-reviewer): round 1 (@3a30d7d) 4/4 PASS; each reviewer reproduced W1 failing on the pre-fix tree. Seven #50/#55 and leak modules (112 tests) from a clean worktree left no .mam in the checkout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
5.6 KiB
5.6 KiB
테스트 격리 수정(u1e 의 WORKSPACE_ROOT) 리뷰 — 커밋 3a30d7d (job 495a1373)
- 리뷰어: planner-reviewer-claude-01 (Reviewer)
- 리뷰 identity: loop-20261013T025803Z-c69408 / base
42e2a7ea.. head3a30d7da(커밋 1개, 테스트 2 파일) / treebd43e464 - 검증 경로:
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1— HEAD·tree 일치,git status --porcelain0 줄,.mam없음(시작·종료 모두). 본 체크아웃 HEAD 42e2a7e(main) 변동 없음. - 실행 안전: 모든 실행은
env -i(PATH/HOME/LANG/TMPDIR 만)로 브리프의-u목록을 포함한 라우팅 변수를 비웠고, basetemp 는 scratchpad 아래(이름에 "shim" 없음). 수정 전 재현은 검증 경로를git clone --no-hardlinks한 임시 복사본에서만 했고 거기 생긴.mam은 지웠다. 검증 경로에서 돌린 것은 W1/W2 뿐이며(W1 은 체크아웃을 tmp 로 복사해 그 안에서 노드를 돌린다), 끝난 뒤 검증 경로에.mam이 없음을 확인했다. 실제.mam·.mam.env·모니터·실제 서버·에이전트 홈 미접촉.
1. 원인과 수정
- 원인(1차 README 리뷰에서 재현한 그대로):
test_u1e는 래퍼를subprocess.run(["bash", WRAPPER, "submit", …, "--dry-run"], env=dict(os.environ, HOME=…, MAM_ENV_FILE=…), cwd=tmp_path)로 부르는데, 래퍼가 source 하는lib.sh는WORKSPACE_ROOT가 없으면 체크아웃을 기본값으로 삼고(lib.sh:26)_init_herdr_isolation이$WORKSPACE_ROOT/.mam/shim/herdr를 쓴다(lib.sh:150-154). conftest 의pytest_configure는 다른 체크아웃을 가리키는WORKSPACE_ROOT만 떨어뜨리므로 pane 밖에서는 변수가 없고, 체크아웃이 기본값이 된다. - 수정(
tests/test_issue50_dispatch.py:167-170): env 에WORKSPACE_ROOT=str(tmp_path)추가 → shim 은tmp_path/.mam/shim/herdr에 쓰인다.tests/test_issue17_workspace_root_isolation.py:43: 이 노드를 W1NODES에 추가. 두 파일py_compile통과. 같은 파일의 다른 테스트는decide.*를 in-process 로 부르므로(_register/_edit/_event헬퍼) shim 을 쓰지 않는다 — 아래 §2 의 파일 전체 실행이 이를 확인.
2. 실행 결과
| 실험 | 결과 |
|---|---|
수정 전 복사본(검증 경로 clone + test_issue50_dispatch.py 를 base 42e2a7e 로 되돌림): u1e 단독 |
1 passed, 복사본에 .mam/shim/herdr 생성 — 원인 재현 |
| 수정 전 복사본: W1(새 NODES 포함) | FAIL: AssertionError: tests wrote into the checkout they ran in: ['.mam/shim', '.mam/shim/herdr'] — W1 이 이 노드로 회귀를 잡는다(복사본 안의 tmp 복사본에만 쓰였고 복사본 자체에는 .mam 없음) |
head, 검증 경로: test_issue17_workspace_root_isolation.py (W1+W2) |
2 passed, 검증 경로 .mam 없음 |
head 복사본(git archive): test_issue50_dispatch.py 전체 |
49 passed, 복사본 .mam 없음 |
W1 이 잡는 원리: W1 은 복사본에 WORKSPACE_ROOT=<복사본> 을 주고 노드를 돌린다. 수정 전 u1e 는 os.environ 을 그대로 물려주므로 래퍼가 그 복사본에 shim 을 쓰고, 수정 후는 tmp_path 로 덮어써 복사본에 아무것도 남지 않는다. pane 밖(변수 없음)에서도 lib.sh 의 체크아웃 기본값 때문에 같은 결과이므로 두 환경 모두를 대표한다.
3. 같은 결함이 남은 테스트가 있는가
- 정적: 래퍼·
lib.sh·run_loop.sh·create_session.sh·reconcile.sh·stop/resume_session.sh를 subprocess 로 부르는 테스트 파일 59 개를 추려mam_sandbox/own_workspace_root/WORKSPACE_ROOT참조 여부를 봤다. 참조가 하나도 없는 7 파일과, 참조가 적어 테스트별 누락이 있을 수 있는 18 파일(W1 NODES 에 없는 것 위주)을 골랐다. - 경험적: 그 25 파일을 head 의
git archive복사본에서 파일마다 따로 돌리고 매번 복사본의.mam을 봤다 —test_b7_diff_untracked,test_deploy_freshness,test_deploy_layout,test_issue42_skill_blocks,test_issue45_herdr_config,test_issue5_phase0_contracts,test_local_mqtt,test_issue14_delivery_ack,test_issue18_liveness_contract,test_issue6_turn_handshake,test_issue26_envfile,test_issue26_ints,test_issue33_budget,test_layout,test_workspace_scope,test_a4_adapter_contract,test_issue45_restore,test_sanitize_and_mock_errors,test_sanity,test_issue12_registry_secrets,test_issue21_acceptance,test_issue15_registry_isolation,test_issue20_state_isolation,test_issue36_prompt_transport,test_issue34_job_tree: 전부 통과(skip 은 tomllib/nats-docker 류),.mam생성 0 건. - 이전 리뷰들에서 같은 방식으로 돌린 파일들(leak·startup_path·shim_contract·issue17/9/45, scenario4, pane_lock, issue50_dispatch_herdr 등)도 검증 경로에
.mam을 남기지 않았고,.mam/shim이 생긴 경우는 모두test_issue50_dispatch.py가 포함된 실행뿐이었다(u1e). 즉 이 결함은 u1e 하나였고, 수정으로 닫혔다.
4. 잔류물
- clone 복사본·archive 복사본·basetemp·로그는 scratchpad 아래. 검증 경로는 깨끗(
.mam없음,git status0 줄). #55 가드 오탐은 이번 실행에서 나오지 않았다.
결론
원인 진단이 맞고, WORKSPACE_ROOT=tmp_path 가 정확한 최소 수정이며, W1 은 수정 전 트리에서 이 노드로 실패하고 수정 후 통과한다. 다른 테스트에서 같은 누출은 25 파일의 파일별 실행으로 재현되지 않았다.
[VERDICT: PASS]