From 002d9b268dfbb04618216ae6db2ca8a9b4ecd287 Mon Sep 17 00:00:00 2001 From: Godopu Date: Mon, 27 Jul 2026 13:16:14 +0900 Subject: [PATCH] feat: add force-refresh capability and allow framework-owned skill updates during installation --- .../report-3c062f3a.md | 162 ++++++++++++++++++ deploy/install.sh | 84 +++++++-- 2 files changed, 228 insertions(+), 18 deletions(-) create mode 100644 .agents/reports/canary-projects-multi-agent-mux-creator-claude/report-3c062f3a.md diff --git a/.agents/reports/canary-projects-multi-agent-mux-creator-claude/report-3c062f3a.md b/.agents/reports/canary-projects-multi-agent-mux-creator-claude/report-3c062f3a.md new file mode 100644 index 0000000..d685374 --- /dev/null +++ b/.agents/reports/canary-projects-multi-agent-mux-creator-claude/report-3c062f3a.md @@ -0,0 +1,162 @@ +# 교차 코드 리뷰 리포트 — 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 -- ` (실제 호출 형태) 재현 | ✅ 회귀 없음 | +| `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]` 근거였던 두 설계 결정이 모두 일관되게 해소되었습니다. + +1. **`.agents/` 소유권 경계** → `.agents/skills/*` = 프레임워크 소유, 그 외 = 사용자 소유. 명시적 헬퍼 함수로 코드에 표현되어 검증·확장 가능합니다. +2. **갱신 책임 주체** → `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` 이외에는 전부 무효라는 사실이 어디에도 드러나지 않습니다. + +```bash +# 권장: 문자열 비교로 전환 (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