12 KiB
교차 코드 리뷰 리포트 — Job 3c062f3a (재리뷰)
- 대상:
deploy/install.sh—.agents/자산 소유권 분리 +--refresh-skills게이트 우회 도입 - 작업 목표: "skill files in
.agents/are updated with latest metadata frontmatter" - 이전 리뷰: Job
1b787c36([VERDICT: NOT PASS]+[ESCALATE: PLANNER]) - 리뷰어 세션:
canary-projects-multi-agent-mux-creator-claude
⚠️ 역할 불일치 고지 (이전 리뷰에서 이어짐):
.mam/agent-sessions.yaml상 본 세션의role은 여전히planner이나 브리프는Reviewer를 지정합니다. MULTI_AGENT_RULES.md §1에 따라 명시하되 연속성을 위해 직접 수행했습니다. GM 측에서 레지스트리 role을 정정하거나 리뷰 전담 세션으로 재배정할 것을 권고합니다.
검증 방법
정적 판독에 그치지 않고 실제 코드를 추출해 샌드박스에서 실행했습니다.
| 검증 | 방법 | 결과 |
|---|---|---|
| 문법 | bash -n deploy/install.sh |
✅ 통과 |
| 소유권 분리 동작 | install.sh:113-118(헬퍼) + 160-176(복사 루프)를 sed로 원본에서 추출해 사전 시딩된 target에 실행 |
✅ 아래 표 |
| manifest 멱등성 | 동일 루프 3회 반복 실행 | ✅ 2줄 유지 |
| 인자 파싱 | install.sh:10-46 추출 후 7가지 호출 형태 매트릭스 |
✅ 전부 정상 |
update.sh 호환 |
cat script | bash -s -- <dir> (실제 호출 형태) 재현 |
✅ 회귀 없음 |
FORCE_REFRESH 견고성 |
1/0/true/yes/""/2 6개 값 주입 |
⚠️ N1 |
| shellcheck | 로컬 미설치, pip install shellcheck-py 시도 실패(오프라인) |
❌ 미검증 — N7 |
1. 이전 차단 사유 해소 확인
✅ B1 해소 — 사용자 소유 파일 보존 (실측 검증)
is_framework_owned()로 .agents/skills/*만 덮어쓰기 대상으로 한정했습니다. 사용자 커스터마이즈본을 미리 심어둔 target에 실제 복사 루프를 실행한 결과:
| 경로 | 분류 | 실행 후 내용 | 판정 |
|---|---|---|---|
.agents/skills/lib.sh |
framework | UPSTREAM lib |
✅ 갱신됨 |
.agents/skills/multi-agent-mux-resume/SKILL.md |
framework | UPSTREAM resume SKILL |
✅ frontmatter 갱신 — 목표 달성 |
.agents/MULTI_AGENT_RULES.md |
user | USER charter |
✅ 보존 |
.agents/INSTALL.md |
user | USER install manual |
✅ 보존 |
.agents/references/herdr_docs.md |
user | USER refs |
✅ 보존 |
.agents/reports/sess-a/report-x.md |
user | USER report |
✅ 보존 |
이전 리뷰에서 지적한 헌장 파기 시나리오가 실제로 차단됨을 확인했습니다.
✅ B2 해소 — manifest 오염 제거 (가장 중요한 회귀 수정)
manifest append가 각 분기 내부로 이동해, 사용자 소유 선존재 파일은 복사도 등재도 되지 않습니다. 위 실행 후 manifest 실측:
.agents/skills/lib.sh
.agents/skills/multi-agent-mux-resume/SKILL.md
사용자 문서 4종이 전부 미등재입니다. 따라서 remove.sh의 manifest 기반 delete_asset 루프가 이들을 건드리지 않으며, remove.sh가 사용자에게 출력하는
" (Your own custom files inside .agents/ will NOT be touched)."
라는 고지가 다시 참이 됩니다. remove.sh의 fallback 경로(manifest 부재 시)도 .agents/skills/* 디렉터리만 삭제하고, 3단계 find .agents -depth -type d -exec rmdir {} +는 빈 디렉터리만 제거하므로 사용자 문서는 양쪽 경로 모두에서 안전합니다.
부수 확인: 신규 설치 시 .agents/MULTI_AGENT_RULES.md는 설치 스크립트가 생성했으므로 manifest에 등재되고 언인스톨 시 삭제됩니다 — 이는 올바른 대칭입니다.
✅ B3 해소 — 주 업그레이드 경로 동작
if [ "$FORCE_REFRESH" -eq 1 ] || ! check_assets_present "." 로 게이트를 우회할 수단이 생겼습니다. 인자 파싱 매트릭스 실측:
| 호출 | TARGET_DIR |
FORCE_REFRESH |
|---|---|---|
install.sh |
$(pwd) |
0 |
install.sh /tmp/x |
/tmp/x |
0 |
install.sh --refresh-skills |
$(pwd) |
1 |
install.sh --refresh-skills /tmp/x |
/tmp/x |
1 |
install.sh /tmp/x --refresh-skills |
/tmp/x |
1 |
install.sh -f / --force |
$(pwd) |
1 |
install.sh a b |
— | ❌ Unknown argument: b (exit 1) |
update.sh 회귀 없음: update.sh:141의 curl … | bash -s -- "$TARGET_DIR" 에서 --는 bash 자신이 소비하므로 스크립트는 위치 인자 1개만 받습니다. 실제 파이프 형태로 재현해 TARGET_DIR=[/tmp/x] FORCE_REFRESH=[0] 을 확인했습니다. README 원라이너(무인자)도 정상입니다.
네트워크 실패 시 안전성도 유지됩니다 — check_assets_present "$STAGE_DIR" 검증이 복사 이전에 있고, git clone 실패는 set -e로 중단되며 trap이 STAGE_DIR을 정리하므로 기존 설치는 무손상입니다.
✅ B4 해소 — 주석·출력 정합성
install.sh:120-127헤더: FW-D1 안전 모델이 새 소유권 정책으로 정확히 재서술됨.install.sh:155-159인라인 주석: 적용 범위(.agents/skills/*덮어쓰기 / 그 외 no-clobber·unmanifested)를 실제 동작과 일치하게 기술.install.sh:215:"✅ Skills staged into workspace (user documents and custom configs preserved)."— 이제 참.
🔓 에스컬레이션 철회
이전 리뷰의 [ESCALATE: PLANNER] 근거였던 두 설계 결정이 모두 일관되게 해소되었습니다.
.agents/소유권 경계 →.agents/skills/*= 프레임워크 소유, 그 외 = 사용자 소유. 명시적 헬퍼 함수로 코드에 표현되어 검증·확장 가능합니다.- 갱신 책임 주체 →
install.sh가--refresh-skills로 in-place 갱신을 담당하고,update.sh는 기존의 remove-후-재설치 방식을 유지합니다. 두 경로가 경합하지 않음을 실측으로 확인했습니다.
추가 재계획은 불필요합니다.
2. 잔여 관찰 (전부 비차단)
N1. MAM_FORCE_REFRESH 비숫자 값 — 조용한 무시 + 원시 셸 에러 (실측)
[ "$FORCE_REFRESH" -eq 1 ]은 산술 비교라 비숫자 입력에서 깨집니다. 6개 값 주입 결과:
| 입력 | 동작 | stderr |
|---|---|---|
1 |
REFRESH | — |
0, "" |
no-refresh | — |
true |
no-refresh | [: true: integer expression expected |
yes |
no-refresh | [: yes: integer expression expected |
2 |
no-refresh | — (완전 무음) |
if 문맥이라 set -e로 중단되지는 않음을 별도 확인했습니다(not-taken (survived)). 즉 크래시는 없으나, MAM_FORCE_REFRESH=true를 지정한 사용자는 정체불명의 셸 에러를 보고, 갱신은 일어나지 않은 채 "🎉 Installation complete!" 를 받습니다. 리터럴 1 이외에는 전부 무효라는 사실이 어디에도 드러나지 않습니다.
# 권장: 문자열 비교로 전환 (0/미설정만 비활성)
if [ "$FORCE_REFRESH" != "0" ] || ! check_assets_present "."; then
N2. 신규 플래그/환경변수가 사용자 문서에 전무
README.md, BOOTSTRAP.md, BOOTSTRAP.ko.md, deploy/README.md, deploy/INSTALL.md, .agents/INSTALL.md 전수 검색 결과 --refresh-skills / MAM_FORCE_REFRESH 언급이 0건입니다. 반면 형제 환경변수 MAM_REPO_URL / MAM_ARCHIVE_URL / MAM_INSTALLER_URL은 deploy/README.md:48-53에 문서화되어 있어 일관성도 어긋납니다.
B3의 메커니즘은 갖춰졌지만 발견 가능성이 없습니다 — README가 안내하는 원라이너를 정상 설치 위에서 재실행하면 여전히 조용히 no-op입니다. 기능이 실사용되려면 최소한 README.md Quick Start와 deploy/README.md 환경변수 표에 추가가 필요합니다. (동작 자체는 정상이므로 비차단으로 분류하나, 머지 전 처리를 권장합니다.)
N3. --force 의미 충돌 (suite 내 일관성)
| 스크립트 | --force 의미 |
|---|---|
remove.sh:18, update.sh:16 |
확인 프롬프트 생략 (비대화형) |
install.sh:18 (신규) |
네트워크 fetch + skill 덮어쓰기 강제 |
같은 배포 suite에서 정반대 성격입니다. install.sh에는 프롬프트가 없어 즉각적 피해는 없으나, bash remove.sh --force에 익숙한 사용자가 bash install.sh --force를 "무확인 실행"으로 오해하면 의도치 않은 네트워크 fetch와 skill 덮어쓰기가 발생합니다. --force 별칭을 떼고 -f | --refresh-skills만 남기는 것을 권장합니다.
N4. 갱신 시 prune 부재
merge-only 복사라 업스트림에서 삭제·개명된 skill 파일이 target에 잔류하고 manifest에도 남습니다. 기존부터 있던 한계지만, --refresh-skills가 공식 갱신 경로로 승격되면서 체감 중요도가 올라갑니다("갱신했는데 왜 옛 파일이 남지"). manifest에 기록된 .agents/skills/* 중 이번 stage에 없는 항목을 정리하는 후속 작업을 권장합니다.
N5. cp 모드 비전파 (기존 이슈)
cp는 기존 dest를 덮어쓸 때 dest 퍼미션을 유지하므로, 업스트림의 실행 비트 추가가 갱신 설치에 전파되지 않습니다. 현재 모든 스크립트가 bash <script> 형태로 호출되어 실질 영향은 없습니다.
N6. .agents/INSTALL.md 가드 사문화 (기존 이슈, 이번 diff와 무관)
.agents/INSTALL.md는 git 추적 대상이며 deploy/INSTALL.md와 내용이 다릅니다. 복사 루프가 이를 먼저 생성하므로 install.sh:205-211의 [ ! -e ".agents/INSTALL.md" ] 가드는 신규 설치에서 항상 거짓 — 도달 불가 코드입니다. 결과적으로 의도한 deploy/INSTALL.md가 아닌 .agents/INSTALL.md가 배포됩니다. 이번 변경이 만든 문제는 아니나 별도 티켓으로 추적할 가치가 있습니다.
N7. shellcheck 미검증 — CI가 실제로 강제함
deploy/gitea-ci.yml:36이 shellcheck deploy/install.sh를 옵션 없이(= 전 severity) 실행하며, 발견 시 non-zero로 스텝이 실패합니다. 본 환경에는 shellcheck가 없고 shellcheck-py 설치도 오프라인으로 실패해 정적 린트를 수행하지 못했습니다. bash -n은 통과했고 신규 코드(while/case 파싱, 헤어독, case 기반 헬퍼)에서 명백한 SC 위반은 육안상 보이지 않으나, 머지 전 CI 린트 통과를 별도 확인하십시오. 이 항목은 제 검증 범위 밖입니다.
N8. 워크스페이스 위생 (경과)
- 이전 리뷰에서 지적한 루트
SKILL.md(손실된 frontmatter 사본)는 제거되었습니다 ✅. 다만 그것을 생성한 writer의 경로 해석·키 보존 동작을 점검했는지는 이 diff로 확인되지 않으므로, 원인 규명은 별도로 남겨두시길 권합니다. .agents/skills/multi-agent-mux-delegate-job/*.tmp잔여물이 PID를 바꿔가며 재출현했다 자체 소멸합니다(.29278_90739→.14424_90739). 비원자적 쓰기의 일시적 흔적으로 보이며 리뷰 대상 변경과 무관합니다.
3. 결론
이전 리뷰의 차단 사유 4건이 모두 해소되었고, 실제 코드를 추출해 실행한 기능 테스트로 확인했습니다. 특히 사용자 소유 파일 보존(B1)과 manifest 미등재를 통한 remove.sh 약속 회복(B2)은 실측 결과가 명확합니다. 작업 목표인 skill frontmatter 갱신도 .agents/skills/** 범위에서 정확히 동작하며, update.sh 호환성 회귀는 없습니다. 소유권 경계를 is_framework_owned()라는 단일 함수로 표현한 설계는 향후 정책 변경 시 수정 지점이 하나로 모여 있어 유지보수성도 좋습니다.
잔여 8건은 전부 비차단입니다. 다만 N2(문서화 부재) 는 기능의 발견 가능성을 좌우하므로 머지 전 함께 처리하고, N1(숫자 비교) 은 한 줄 수정이므로 같이 반영할 것을 권합니다. N7(shellcheck) 은 제가 검증하지 못한 항목이니 CI 결과로 갈음해 주십시오.
[VERDICT: PASS]