# PR #58 Mac review — NOT PASS Job: `863e2edf`. Role: Reviewer. 소스 수정 없이 리뷰 및 임시 디렉터리 안의 검증만 수행했다. - loop: `loop-20261014T152849Z-f1a415` - base: `e32c9574644a9185813a4dc881812694d62b6204` - head: `63d816468e6d48d01bd96f31d8a25eeadfbaab60` - tree: `e8f162f74b7ce8ee8cc6ba7954b7d28dfa3f89f6` - 검증 트리: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261014T152849Z-f1a415/1` - 범위: base..head 11 commits / 21 files. 아래 파일:라인은 이 head 기준이다. 관련 테스트 **77 passed in 177.28s**, exit 0. 그러나 테스트가 다루지 않는 두 경우를 추가 탐침으로 재현했다. 병합 전에 아래 두 항목을 수정하고 회귀 테스트를 추가해야 한다. 국소 수정으로 해결 가능하므로 설계 재계획 에스컬레이션은 필요하지 않다. ## R1 — [P1] 디스크 폴백이 다른 dispatch의 완료를 이전 dispatch의 성공으로 오인한다 근거: - `.agents/skills/lib_py/delegation/job_subscriber.py:89`: 레지스트리의 status/token/attempt/transition_seq를 먼저 읽는다. - 같은 파일 `:100`, `:114`, `:121`: 나중에 읽은 감사 상태의 seq가 그 스냅샷 이상이면 상태를 채택한다. - 같은 파일 `:126`, `:132`: 토큰/attempt 검증에는 여전히 처음 읽은 레코드만 사용한다. - `.agents/skills/lib_py/delegation/engine.py:322`: redispatch는 새 토큰을 발급한다. 감사 상태에는 토큰/attempt가 기록되지 않는다(`:383`). 재현 절차: 1. 임시 cwd/WORKSPACE_ROOT 및 별도 registry에서 토큰 A로 잡을 등록하고 `started`로 전이시켜 running 상태로 만든다. 2. `_check_disk_fallback`을 expected token A로 실행한다. 3. `read_logged_status`를 감싸서, 레지스트리 read가 끝나고 감사 read가 시작되는 지점에 다음 실제 engine 전이를 실행한다: A의 `error` → `redispatch(attempt_id=2)` → 새 토큰 B로 `completed`. 4. 실제 `read_logged_status` 결과를 반환한다. 즉 감사 파일을 조작한 것이 아니라 정상 engine 전이 사이에 subscriber가 일시 중단된 순서를 결정적으로 재현했다. 기대: 토큰 B의 성공으로 A의 대기를 완료하면 안 된다. 최신 레코드와 비교해 토큰 변경을 거부하거나 A의 실제 실패를 구분해야 한다. 실제: 초기 레코드는 A/running이고 최종 레코드는 B/completed인데, `pending=[]`, `terminal={job_id: "completed"}`가 된다. 출력도 `completed [disk-fallback]`이다. 이전 시도가 실패했는데도 호출자가 성공으로 종료할 수 있다. 탐침 출력 발췌: ```text RACE {"initial_status": "running", "initial_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "latest_token": "7b761cb2ea2d4514bbd631467997ad9d", "pending": [], "terminal": {"5b5cd6e8": "completed"}} ``` 위 토큰은 임시 테스트 잡의 식별자이다. 이전 감사 상태를 거부하는 현재 회귀 테스트는 통과하지만, **레코드를 읽은 이후 다음 attempt의 더 새로운 감사 상태가 도착하는 경우**는 검사하지 않는다. seq는 최신성만 나타내며 dispatch identity를 보장하지 않는다. 수정 방향: 감사 상태를 채택하기 직전에 레지스트리를 다시 읽어 token/attempt/seq가 같은 관찰에 속하는지 확인하고 변경 시 재시도/거부한다. 또는 레지스트리 lock 아래에서 일관된 관찰을 보장한다. 감사 상태에 dispatch identity를 기록·검증하는 방식도 가능하다. 단순히 `aud_seq >= rec_seq`만으로 다른 시도의 상태를 채택하지 않도록 위 interleaving을 회귀 테스트로 추가한다. ## R2 — [P2] 레지스트리 자체가 심볼릭 링크이면 submit과 logs의 로그 경로가 갈라진다 근거: - `.agents/skills/lib_py/delegation/mqtt_common.py:189`: sibling 계산은 `abspath`만 사용한다. - `.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job:84`: submit의 `_abs_registry_dir`는 `cd -P`/`pwd -P`로 링크를 해석한다. - 같은 파일 `:280`: 물리 경로로 바꾼 다음 로그 루트를 계산한다. - 같은 파일 `:1108`: logs는 전달받은 링크 경로 그대로 registry CLI에 넘긴다. - `.agents/skills/lib_py/control_plane.py:34`, `:52`: control-plane binding도 realpath로 정규화하므로 직접 Python resolver와 다를 수 있다. 재현 절차(모두 임시 디렉터리 안에서, DELEGATE_JOB_LOGS_DIR 없이): 1. `/actual/jobs`와 `/alias`를 만들고 `/alias/jobs -> /actual/jobs` 심볼릭 링크를 만든다. 2. cwd와 WORKSPACE_ROOT를 ``로 둔다. 3. `/bin/bash /.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit --dry-run --agent human --registry-dir /alias/jobs --workdir --prompt symlink-wrapper` 실행. 4. 생성된 job ID로 같은 wrapper의 `logs --registry-dir /alias/jobs` 실행. 기대: 동일한 registry 인수로 만든 잡의 감사 로그를 조회할 수 있어야 한다. 실제: submit은 rc 0이며 `/actual/delegate_job_logs/`에 감사 로그를 생성하지만, logs는 `/alias/delegate_job_logs`에서 찾아 rc 1로 실패한다. ```text job_id: a6cdb85e submit_log_root: /private/tmp/review-863e2edf-m7fmcbsw/probes-igd0iy09/actual/delegate_job_logs/a6cdb85e actual_audit_exists: true logs_rc: 1 no audit log for job a6cdb85e under /tmp/review-863e2edf-m7fmcbsw/probes-igd0iy09/alias/delegate_job_logs ``` 별도 직접 registry 호출도 같은 레코드를 가리키면서 alias 옆에 로그를 쓰고, control_plane.from_env는 actual 옆을 기록함을 확인했다. `/tmp -> /private/tmp`처럼 **부모 전체가 같은 위치를 가리키는 별칭**은 통과하며, 이 문제는 **jobs 디렉터리 자체가 다른 부모 아래로 연결되는 링크**에서 발생한다. 수정 방향: 기본 감사 경로를 산출하기 전에 registry 경로를 정규화하는 정책을 중앙 resolver에 통일한다. 예를 들어 registry의 realpath에서 sibling을 구하면 submit/control_plane과 일치한다. 명시적 DELEGATE_JOB_LOGS_DIR 우선권은 유지한다. `submit --dry-run` 후 동일한 symlink 인수로 `logs`를 조회하는 회귀 테스트를 추가한다. ## 검증 결과 및 범위 - 실제 Mac Python 3.9.6에서 변경된 Python 13개 파일 AST 파싱 통과. 변경된 shell/wrapper 5개를 각각 `/bin/bash -n`으로 검사하여 통과. Bash는 `3.2.57(1)-release (arm64-apple-darwin25)`이다. - 관련 pytest 77건 통과: `test_issue57_logs_follow_registry.py`, `test_issue15_registry_isolation.py`, `test_issue12_registry_secrets.py`, `test_issue21_worktree_prereqs.py`, `test_issue21_acceptance.py` 전체; `test_tier1_unit.py::test_b9_logs_dir_follows_cwd_changes`; issue18의 subscriber state/seq/liveness 및 remote redispatch 관련 4건. - 실행은 지정 venv Python, `PYTHONDONTWRITEBYTECODE=1`, `-p no:cacheprovider`, 검증 트리 밖 `/tmp/review-863e2edf-m7fmcbsw/pytest` basetemp를 사용했다. 지정된 HERDR/WORKSPACE_ROOT/MAM/DELEGATE 환경변수를 `env -u`로 제거했고, HOME도 임시 위치로 격리했다. create/resume은 mock fixture를 사용했다. 실제 에이전트/monitor/herdr 서버를 시작하지 않았다. - 일반 기본 경로, 명시적 로그 override, 상대 registry, 이동 registry, control-plane row 및 prefix, worktree create/resume, audit-only status, 오래된 감사 seq 거부는 관련 테스트로 검증했다. - 추가 Mac 부모 경로 별칭 탐침(`/tmp`와 `/var`): 정규화된 binding에서 sibling 로그 키를 전달하지 않는 동작 통과. - `git diff --check base HEAD` 통과. 검증 후 HEAD/tree 일치 및 clean git status 확인. 소스/문서 변경 없음. - 전체 스위트는 이 리뷰에서 실행하지 않았다(브리프에 따라 오케스트레이터가 별도 실행). 알려진 purge 모듈도 이번 선택에 포함되지 않았다. 선택 테스트에서는 #55 가드 오탐이나 teardown 오류가 없었다. - 테스트의 Linux 결과를 Mac 결과로 대신 인용하지 않았다. 검증 트리 바깥 creator 소스에서 테스트를 실행하지 않았다. 재현 스크립트: `/tmp/review-863e2edf-m7fmcbsw/probes.py` (실제 대상 모듈 import, 임시 디렉터리 안에서 실행). 탐침 로그: `/tmp/review-863e2edf-m7fmcbsw/probes.log`. 선택 테스트 로그: `/tmp/review-863e2edf-m7fmcbsw/pytest.log`. 재실행: 검증 트리 cwd에서 `PYTHONDONTWRITEBYTECODE=1 /Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python /tmp/review-863e2edf-m7fmcbsw/probes.py`. 스크립트가 제어 평면 관련 환경을 제거하고 cwd/WORKSPACE_ROOT/HOME을 새 임시 디렉터리로 설정한다. [VERDICT: NOT PASS]