Files
multi-agent-mux/.agents/reports/planner-reviewer-codex-01/report-863e2edf.md
T
GodopuandClaude Opus 5.5 7cf728e9f8 release(v4.9.4): the audit log follows a moved registry (Issue #57, PR #58) - archive the review reports and bump the lockstep version
Review loop (--review-only, --all-reviewer, focused on this Mac:
macOS, /usr/bin/python3 3.9.6, bash 3.2): round 1 (@63d8164) agy and
grok PASS, claude and codex NOT PASS (F1 paho via the /usr/bin/python3
fallback, F2 logs default path, R1 disk-fallback redispatch
interleaving, R2 symlinked registry), sent as a PR comment and fixed by
the author in four commits; round 2 (@a7cb90e) 4/4 PASS.

Full suite on macOS / Python 3.9.6 @a7cb90e: 1867 passed, 56 skipped,
1 error (the known #55 false positive: WAL/SHM of an agy conversation
running in another workspace). Real herdr panes and ~/.claude/projects
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
2026-10-15 22:52:04 +09:00

8.6 KiB

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]이다. 이전 시도가 실패했는데도 호출자가 성공으로 종료할 수 있다.

탐침 출력 발췌:

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. <tmp>/actual/jobs와 <tmp>/alias를 만들고 <tmp>/alias/jobs -> <tmp>/actual/jobs 심볼릭 링크를 만든다.
  2. cwd와 WORKSPACE_ROOT를 <tmp>로 둔다.
  3. /bin/bash <review>/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit --dry-run --agent human --registry-dir <tmp>/alias/jobs --workdir <tmp> --prompt symlink-wrapper 실행.
  4. 생성된 job ID로 같은 wrapper의 logs <id> --registry-dir <tmp>/alias/jobs 실행.

기대: 동일한 registry 인수로 만든 잡의 감사 로그를 조회할 수 있어야 한다.

실제: submit은 rc 0이며 <tmp>/actual/delegate_job_logs/<id>에 감사 로그를 생성하지만, logs는 <tmp>/alias/delegate_job_logs에서 찾아 rc 1로 실패한다.

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]