- In stop_session.sh and update_yaml_resumed.sh: standardize explicit --agent option and use resolve_agent_type_from_registry to read agent type from YAML/DB state rather than brittle suffix-only regex inference. - Update multi-agent-mux-stop/SKILL.md, multi-agent-mux-resume/SKILL.md, multi-agent-mux-create/SKILL.md, and deploy/INSTALL.md to standardize passing --agent explicitly. - Fix J-1 in layout.py: refactor _env_int(*names, default=None) to take an explicit default parameter, eliminating the falsy-zero trap so MAM_MIN_PANE_COLS=0 is respected. - Add regression and contract tests: test_j1_env_zero_min_cols_matches_flag_zero, test_j1_env_zero_min_rows_matches_flag_zero, test_comp_stop_agent_fallback_*, test_comp_docs_stop_examples_pass_agent. - Verified 100% UNANIMOUS PASS from Planner claude and Reviewers claude and cline.
10 KiB
🔍 교차 코드 리뷰 — Job dcbdf349
- 역할: Reviewer
- 대상: Rev.2 계획서(
d6f54b7f) 구현분 — 워킹 트리 수정 14파일 (+255 / −46) - 기준 커밋:
14e306b/ 미추적 파일 0건
1. 결론
계획서 Rev.2 의 8개 작업항목(S1~S8)이 전부 사양대로 구현됐습니다. 전체 스위트 341 passed / 425.16s / exit 0 으로 계획이 예측한 수치와 정확히 일치하고, 뮤테이션 10종 전부 지정 테스트를 FAIL 시킵니다.
차단 사유 없음. P3 관찰 3건만 기록합니다.
2. 검증 결과 요약
| 검증 | 결과 |
|---|---|
| 전체 스위트 | 341 passed, 425.16s, exit 0 (계획 예측 341 과 일치) |
| 배포 신선도 단독 | 31 passed (기존 유지) |
bash -n 6개 스크립트 |
6/6 OK |
py_compile (시스템 Python 3.9.6) |
OK |
| 수집 수 | 333 → 341 (신설 8건) |
test_layout.py |
19 → 23 (IMPROVEMENTS.md 기재치와 일치) |
| 보호 대상 2파일 무수정 | test_tier1_unit.py / test_tier3_integration.py — git diff --stat 출력 0줄 |
| 뮤테이션 | 10/10 검출 |
3. 뮤테이션 매트릭스 — 10/10 검출
격리 rsync 사본에서 실행. 무뮤테이션 대조군은 대상 7건 전건 통과(7 passed in 2.73s).
| # | 뮤테이션 | 결과 |
|---|---|---|
| M1 | stop_session.sh 폴백을 옛 case 블록으로 복원 |
…reads_pane_cmd FAILED |
| M2 | 헬퍼에서 row 폐기 (agent_of_row({}, …)) |
2건 모두 FAILED |
| M3 | 헬퍼에 match_cmd=False |
…reads_pane_cmd FAILED / …prefers_explicit_agent_field PASSED |
| M4 | default=60 → … or 60 |
test_j1_env_zero_min_cols… FAILED |
| M5 | except ValueError: continue → return None |
2건 모두 FAILED |
| M5b | continue → return default (Rev.1 안으로 복귀) |
test_j1b… FAILED / …malformed_env_behaviour_unchanged PASSED |
| M6 | 헤드리스 홀수 분기에도 상한 검사 추가 | test_headless_max_columns_growth_guard FAILED |
| M7 | SKILL.md 예제 1곳에서 --agent 삭제 |
문서 가드 FAILED |
| M7b | INSTALL.md 두 호출 중 하나만 --agent 삭제 |
문서 가드 FAILED |
| M7c | SKILL.md 워크플로 예제 1개 통째 삭제 | 문서 가드 FAILED |
값어치 있는 세 가지
M3 이 정확히 하나만 깬다. 두 T4 테스트가 서로 다른 성질을 잡는다는 것이 실증됐습니다. …reads_pane_cmd 하나만 있었다면 pane.cmd 를 직접 긁는 얕은 구현도 통과했을 것이고, …prefers_explicit_agent_field 가 그 구현을 배제합니다.
M5 와 M5b 가 서로 다른 테스트를 깬다. C-2 반영의 검증 조건이 그대로 성립했습니다 — M5(None 복귀)는 크래시 경로를, M5b(Rev.1 안 복귀)는 별칭 섀도잉을 각각 잡습니다. 둘 중 하나라도 잡히지 않았다면 T1b 는 장식이었을 것입니다.
M7b / M7c 가 서로 다른 사유로 깨진다. 문서 가드의 두 독립 기제가 각각 살아 있다는 뜻입니다.
[M7b] AssertionError: INSTALL.md: stop_session.sh example without --agent: ← 커맨드 단위 검사
[M7c] AssertionError: SKILL.md: expected >= 3 examples, saw 2 ← 문서별 개수 하한
M7b 는 Rev.1 원안(블록 단위)이 놓쳤던 바로 그 케이스입니다. 챌린저 9f85218e 의 지적이 실물 가드에서 값어치를 증명했습니다.
4. 동작 실측
4.1 핵심 결함 — 라이브 세션 해석
agy-creator-01 -> agy
canary-projects-multi-agent-mux-creator-cline -> cline
bad-session-name -> <none rc=1>
--agent 없이 exit 2 로 거부되던 실제 running 세션 agy-creator-01 이 pane.cmd 로 해석됩니다. 동시에 bad-session-name 은 rc=1 로 실패해 호출자의 exit 2 계약이 유지됩니다.
계약 테스트 직접 확인: test_stop_session_invalid_agent_suffix PASSED (무수정 상태). 계획 §3 안 B 의 "기존 테스트를 한 줄도 안 고치고 결함만 제거" 라는 수용 조건이 충족됐습니다.
4.2 J-1 / C-2
MAM_MIN_PANE_COLS=0 : {"direction": "right", "reason": "single_pane_height_constrained"}
--min-cols 0 : {"direction": "right", "reason": "single_pane_height_constrained"} ← 동치
MAM_MIN_COLS=foo +PANE_COLS=25 : {"direction": "right", "reason": "single_pane_height_constrained"} ← C-2
MAM_MIN_COLS=foo +PANE_COLS=bar : {"direction": "overflow", "reason": "single_pane_overflow"} ← 불변식 보존
baseline : {"direction": "overflow", "reason": "single_pane_overflow"}
3행이 C-2 수정(무효 별칭이 문서화된 변수를 가리지 않음), 4행이 Rev.1 불변식 보존(모든 후보 무효 → 문서화된 기본값)입니다. 두 성질이 한 구현에 공존합니다.
4.3 실패 경로 — set -euo pipefail 하 안전성
PYTHONPATH 를 파손시킨 상태에서:
rc-guarded, AGENT=[] (빈 값이면 호출자가 exit 2 로 처리)
stderr 첫 줄: Traceback (most recent call last):
AGENT="$(...)" || AGENT="" 가 set -e 조기 종료를 막고, 계획대로 stderr 를 억제하지 않아 traceback 이 보입니다. 진짜 오류와 정상 해석 실패가 구분됩니다. 명시 --agent bogus 검증도 그대로입니다(invalid agent type 'bogus').
4.4 S8 소싱 경로 복구 — 실효 확인
계획이 "드롭 가능한 별도 커밋" 으로 분리했던 항목이라, 실제 효과가 있는지 되돌려 봤습니다.
[되돌린 사본] / 에서 WORKSPACE_ROOT 없이 실행
→ stop_session.sh: line 39: //.agents/skills/lib.sh: No such file or directory
[현행] / 에서 WORKSPACE_ROOT 없이 실행
→ Usage: ... --session <name> [--agent claude|agy|hermes|cline] ...
장식이 아니라 실제 장애를 닫습니다. cd 가 성공할 때 빈 문자열이 되던 결함이라 1차 소싱 경로가 100% 죽어 있었고, 이제 살아났습니다.
5. 관찰 사항 (P3 — 전부 비차단)
🟡 O-1: S8 소싱 복구에 회귀 가드가 없다
되돌린 사본에 tier1+tier2+tier3 전체를 돌린 결과 78 passed — 아무 테스트도 잡지 못합니다. 모든 테스트가 WORKSPACE_ROOT 를 설정하거나 저장소 루트에서 실행되므로 :36 폴백이 항상 성공하기 때문입니다.
계획이 M8(update_yaml_resumed.sh 폴백 무가드)을 정직하게 남긴 것과 같은 성격입니다. 다만 S8 은 도달 불가 경로가 아니라 실측된 실동작 결함(§4.4)을 고친 것이므로 M8 보다 가드 부재의 무게가 큽니다. 처방은 한 줄입니다 — 저장소 밖 cwd + WORKSPACE_ROOT 미설정으로 --help 를 실행해 rc=0 을 단언.
차단하지 않는 이유: 변경 자체가 순수 개선이고(되돌리면 명백히 실패), 계획이 이 커밋을 분리 가능하도록 설계했으며, 가드 부재가 다른 어떤 것도 가리지 않습니다.
🟡 O-2: create/SKILL.md 스니펫이 자기모순 상태가 됐다
agy)
herdr new-session ... "agy --dangerously-skip-permissions"
;;
*) echo "ERROR: --agent must be claude, agy, hermes or cline, got: $AGENT"; exit 2 ;;
오류 메시지는 4종을 허용한다고 광고하는데 case arm 은 claude/agy 둘뿐입니다. 스니펫을 그대로 따라 --agent hermes 를 주면 *) 로 떨어져 "hermes 는 허용된다" 는 메시지를 내며 죽습니다. 변경 전에는 메시지와 구현이 (둘 다 2종으로) 일치했으므로, 이 한 스니펫의 내부 정합성은 오히려 나빠졌습니다.
실물 create_session.sh:187 은 agy|hermes|cline) 로 4종을 정상 처리하므로 생산 코드에는 결함이 없습니다. 계획 §4.4 는 이 지점에 대해 "스니펫을 축약하고 실물을 가리키게 하는 쪽을 권장" 했고 Creator 는 메시지 수정 쪽을 골랐는데, 그 선택이 계획이 축약을 권한 이유를 그대로 드러냈습니다. agy) → agy|hermes|cline) 한 글자 수정이면 정합해집니다.
🟡 O-3: update_yaml_resumed.sh:45 주석의 라인 참조가 남의 것
# 종전과 동일하게 exit 2 (헤더 :27-30 의 종료 코드 계약 유지).
:27-30 은 stop_session.sh 의 종료 코드 헤더 위치입니다. update_yaml_resumed.sh 의 :27-30 은 인자 파서이고, 이 파일에는 종료 코드 계약 헤더 자체가 없습니다. 두 스크립트에 같은 블록을 심으면서 따라온 복사 흔적입니다. 동작 무영향.
6. 계획 대비 이행 점검
| 항목 | 이행 |
|---|---|
S1 resolve_agent_type_from_registry() |
✅ 계획 코드 그대로. 이름 분리(run_loop 동명 함수 섀도잉 회피) 반영 |
S2 stop_session.sh |
✅ :101 위치 유지, 메시지·종료 코드 불변, 헤더/usage() 갱신 |
S3 update_yaml_resumed.sh |
✅ 동일 블록 + 4종 표기 |
| S4 문서 동기화 | ✅ SKILL.md 3종 + 스크립트 헤더 2종 |
| S5 J-1 + C-2 | ✅ default= 인자화 + continue, docstring 이 두 결정 근거를 모두 기술 |
| S6 테스트 8건 + J-2 보강 | ✅ 341 collected |
S7 IMPROVEMENTS.md |
✅ B-21 절 신설, 완료 29→30, 테스트 수 19→23 갱신 |
| S8 소싱 복구 3곳 | ✅ (가드 없음 — O-1) |
| 범위 준수 | ✅ run_loop.sh·reconcile.sh 무수정 (K-1/K-4 유지) |
usage() 에 4개 에이전트명이 모두 남아야 하는 제약(test_comp_stop_usage_matches_parser)도 유지됐고, FEATURE 3 주석 개수(4→7)와 import re 추가라는 사전 조건 2건도 반영됐습니다.
7. 총평
계획 Rev.2 는 챌린지 라운드를 거치며 문서 가드가 두 번 재설계됐는데, 그 결과가 M7b 에서 값어치를 증명했습니다 — Rev.1 설계였다면 통과했을 회귀를 현행 가드가 잡습니다. C-2 도 M5/M5b 가 서로 다른 테스트를 깨면서 "행동 변경이지만 기존 불변식은 보존" 이라는 주장이 실증됐습니다.
남은 3건은 전부 P3 이고 어느 것도 결함을 가리지 않습니다. O-2 는 문서 스니펫 한 글자, O-3 은 주석 한 줄, O-1 은 가드 신설 권고입니다. 다음 커밋에 함께 정리하면 충분합니다.
[VERDICT: PASS]