refactor: standardize --agent option usage, improve YAML registry fallback, and fix layout falsy-zero trap (J-1)
- 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.
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
# 🔍 교차 코드 리뷰 — 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` 스니펫이 자기모순 상태가 됐다
|
||||
|
||||
```bash
|
||||
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` 주석의 라인 참조가 남의 것
|
||||
|
||||
```bash
|
||||
# 종전과 동일하게 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]
|
||||
Reference in New Issue
Block a user