Files
multi-agent-mux/.agents/reports/planner-reviewer-codex-01/report-f275a259.md
T
GodopuandClaude Opus 5.5 65dc7fde82 release(v4.8.4): bump framework and eight skills to 4.8.4 in lockstep; archive PR #47 reviews (two rounds)
The .claude/skills check accepts both local layouts and fails on a dangling
link. Review: round 1 (@e1ed270) claude agy grok PASS, codex NOT PASS (a
dangling top-level link was skipped); fixed in 150b493; round 2 4/4 PASS.
Reports and the round-2 identity sidecars go to .agents/reports/<session>/.

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

71 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PR #47 리뷰 — f275a259
**NOT PASS**. 단일 링크와 스킬별 링크 디렉터리를 모두 받는 수정은 동작한다. 다만 `.claude/skills` 자체가 깨진 심볼릭 링크이면 부재로 오인해 skip하는 경계 사례가 남아 있다. 작은 테스트 수정으로 해결할 수 있으며 제품 코드 변경이나 재계획은 필요 없다.
## Identity와 범위
- loop: `loop-20261004T043243Z-baf133`
- base: `6af4a796b1bdce3b2e929b7a45a8a22d66b36855`
- head: `e1ed2708d835d712f5f3a9dfd51b8f66ca1ac447`
- tree: `2f781e0db4df9a3082b4f55e69c42118868b18a1`
- 검증 cwd: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261004T043243Z-baf133/1`
HEAD/tree 일치, 시작·종료 시 git status clean. 변경 파일은 `tests/test_issue42_skill_blocks.py` 하나이며 제품 코드·버전 변경은 없다. `git diff --check 6af4a79 HEAD` 통과.
## R1 — P2: 깨진 최상위 링크를 skip하지 말 것
**근거:** `tests/test_issue42_skill_blocks.py:245`–`:248`.
`Path.exists()`는 링크 자체가 아니라 대상을 따라 검사하므로 dangling symlink에서 False다. 따라서 `.claude/skills`가 존재하는 잘못된 링크여도 :245의 부재 분기가 실행돼 :247의 symlink 검증까지 도달하지 않는다. 정상적인 “이 워크트리에 로컬 배치가 없음”과 “로컬 배치가 있으나 깨짐”을 구별하지 못한다.
**재현:** 검증 트리의 원래 `.claude/skills`가 없음을 확인하고, 그 경로에 존재하지 않는 `absent-skills-target`을 가리키는 임시 링크를 만든 뒤 해당 pytest node를 실행했다. 실행 후 링크를 삭제했다.
```sh
PYTHONDONTWRITEBYTECODE=1 /Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python -m pytest -p no:cacheprovider -q --basetemp=/tmp/f275a259-broken_single tests/test_issue42_skill_blocks.py::test_claude_skills_link_resolves_to_agents_skills
```
- 기대: 잘못된 로딩 경로이므로 fail, nonzero.
- 실제: **1 skipped, rc 0**. 반면 디렉터리 안 깨진 개별 링크는 정상적으로 fail한다.
- 영향: 모든 프로젝트 스킬의 진입 경로가 끊겨도 “워크트리에 없음”으로 숨겨져 CI/검증이 성공한다.
- 수정 방향: 부재 skip을 `if not link.exists() and not link.is_symlink():`로 한정하거나 symlink 검증을 부재 검사보다 먼저 수행한다. 완전 부재는 계속 skip하고, dangling root link는 실패해야 한다. 부재/정상 단일 링크/정상 개별 링크/깨진 최상위 링크를 회귀 사례로 남긴다.
메모리상으로 위 조건만 바꾼 같은 함수에 대해 **완전 부재는 SKIP, 깨진 링크는 AssertionError**가 되는 것을 확인했다. 체크아웃 코드는 수정하지 않았다.
이 `exists()` 선행 분기는 이전 버전에도 있었다. 이번 변경이 새로 만든 회귀라고 주장하지 않는다. 그러나 브리프가 명시적으로 요청한 깨진 링크 검토에서 확인된 허점이고, 이 함수가 두 배치의 경로 무결성을 검증하도록 수정되는 범위 안에서 해결할 수 있으므로 반려 사유로 남긴다.
## 실행 결과
원래 워크트리 배치(로컬 `.claude/skills` 없음)에서 지정 모듈 실행:
```sh
PYTHONDONTWRITEBYTECODE=1 /Users/godopu16/PuKi/laa/canary_projects/multi-agent-mux/.venv/bin/python -m pytest -p no:cacheprovider -q --basetemp=/tmp/f275a259-selected tests/test_issue42_skill_blocks.py
```
**31 passed, 1 skipped in 0.06s**, rc 0. 이 기본 실행은 로컬 링크 분기를 검증하지 않으므로 아래 임시 배치 탐침을 별도로 실행했다.
| 임시 배치 | 실제 pytest 결과 | 판단 |
|---|---|---|
| `.claude/skills` 완전 부재 | 1 skipped, rc 0 | 정상 |
| 8개 스킬별 올바른 링크 | 1 passed, rc 0 | 정상; base 함수는 같은 배치에서 AssertionError |
| 위 배치 + lib.sh/lib_py 링크 및 notes.txt | 1 passed, rc 0 | 정상 |
| 개별 링크 하나가 다른 스킬을 가리킴 | 1 failed, rc 1 | 정상 거부 |
| 개별 링크 하나가 깨짐 | 1 failed, rc 1 | 정상 거부 |
| 단일 링크 → `.agents/skills` | 1 passed, rc 0 | 정상 |
| 단일 링크 → 다른 기존 디렉터리 | 1 failed, rc 1 | 정상 거부 |
| 단일 링크 → 존재하지 않는 대상 | 1 skipped, rc 0 | **R1** |
| 일반 파일 | 1 failed, rc 1 | 정상 거부 |
| 빈 디렉터리 | 1 failed, rc 1 | 정상 거부 |
탐침 스크립트: `/tmp/f275a259-layout-probes.py`; 결과: `/tmp/f275a259-layout-probes.log`. 위 cwd에서 `PYTHONDONTWRITEBYTECODE=1 <위 Python> /tmp/f275a259-layout-probes.py`로 재현한다. 스크립트는 원래 경로가 없는지 먼저 확인하고 자신이 만든 임시 배치만 finally에서 정리한다. 기대한 부정 사례의 pytest 실패는 제품 스위트 실패 수치에 합산하지 않았다.
## 나머지 검토 항목
- **실제 본 체크아웃:** 읽기 전용 조사에서 `.claude/skills`는 디렉터리이며 8개 스킬 링크와 lib.sh/lib_py 링크가 있다. 8개 스킬의 resolve 결과는 각각 본 체크아웃의 `.agents/skills/<skill>`과 정확히 일치했다. 수정의 동기가 된 정상 배치를 지원한다.
- **빈 SKILL_NAMES:** 대상 함수만 메모리상 `SKILL_NAMES=[]`로 실행하면 for-loop가 비어 통과한다. 그러나 같은 모듈의 `test_inventory_covers_eight_skills`가 8개를 강제해 전체 모듈은 실패한다. 현재 모듈 단위 실행에는 침묵하는 성공이 없으므로 별도 반려 사유는 아니다. 대상 node를 단독 운영할 때까지 방어하려면 nonempty 단언을 추가할 수 있다.
- **여분 항목:** framework 8개를 확인하는 테스트에서 lib.sh/lib_py 등 여분 항목을 금지하지 않는 것은 적절하다. Claude Code 공식 문서는 프로젝트 스킬을 `.claude/skills/<skill-name>/SKILL.md`로 설명하며 스킬 폴더의 symlink를 지원한다. 일반 helper 파일·SKILL.md 없는 helper 디렉터리를 framework 스킬로 취급할 필요는 없다. 다만 이 테스트는 다른 사용자 스킬 전체를 감사하지 않으므로 docstring의 “each skill Claude Code loads”는 “each framework skill” 정도로 좁히면 더 정확하다. [공식 Claude Code 스킬 로딩 문서](https://code.claude.com/docs/en/skills#choose-where-skills-load).
- **과도한 엄격성:** 스킬 디렉터리의 내용 복사본은 같은 resolve 대상이 아니므로 거부된다. 브리프가 두 링크 배치만 지원하려는 계약에 맞는다. 다른 추가 사용자 스킬의 존재는 허용된다.
검증 트리 안의 임시 `.claude/skills`는 모두 삭제했다. 본 체크아웃의 배치를 변경하지 않았으며 제품 코드·실제 .mam.env·실제 agent/herdr 상태를 건드리지 않았다. 실제 `.mam` 쓰기는 지시된 잡 lifecycle과 이 최종 리포트에 한정했다.
[VERDICT: NOT PASS]