170 lines
12 KiB
Markdown
170 lines
12 KiB
Markdown
# 🔍 Cross Code Review — Job ddc8d9f1
|
||
|
||
- **Reviewer**: `planner-reviewer-claude-01` (role: `planner,reviewer`)
|
||
- **Target**: 2xK grid layout engine — `MAM_MIN_PANE_COLS` 기본값 60 → 40 및 단일 워크스페이스 다중 페인 타일링
|
||
- **Reviewed files**: `.agents/skills/lib.sh`, `.agents/skills/lib_py/layout.py`, `.mam.env.example`, `tests/test_layout.py`, `tests/test_tier1_unit.py`, `tests/test_a4_adapter_contract.py`
|
||
|
||
---
|
||
|
||
## ⚠️ 검증 범위 고지 (Verification Scope Disclaimer)
|
||
|
||
**본 세션에서 `pytest` 스위트를 실행하지 못했습니다.** 실행 시도가 중단되어(사용자 거부) 브리프 4번 요구사항 *"Ensure full pytest test suite passes"* 는 **실측으로 확인되지 않았습니다.**
|
||
|
||
따라서 아래 판정은 다음 범위로 한정됩니다:
|
||
- ✅ 소스 정적 분석 (`layout.py` 전체 로직 판독)
|
||
- ✅ 신규 테스트의 **모든 단언을 엔진 분기에 대입한 수동 트레이스**
|
||
- ✅ `grep` 기반 상수 드리프트 전수 조사
|
||
- ❌ **테스트 실행 결과 (미수행)**
|
||
|
||
수치·분기 추적은 결정론적 정수 연산이라 수동 검증의 신뢰도가 높지만, 실행 확인은 별도로 이루어져야 합니다. §5에 잔여 항목을 명시했습니다.
|
||
|
||
---
|
||
|
||
## 1. 변경 요약
|
||
|
||
| 위치 | 변경 | 판정 |
|
||
|---|---|---|
|
||
| `lib_py/layout.py:73` | `compute_2xk_layout(min_cols=60)` → `40` | ✅ |
|
||
| `lib_py/layout.py:201` | `--min-cols` 기본값 `_env_int(..., default=60)` → `40` | ✅ |
|
||
| `lib_py/layout.py:179` | 독스트링 `60 default` → `40 default` | ✅ |
|
||
| `lib.sh:432` | `${MAM_MIN_PANE_COLS:-60}` → `:-40` | ✅ |
|
||
| `.mam.env.example:132-133` | 주석 `#default: 60` 및 예시 `=60` → `40` | ✅ |
|
||
| `tests/test_layout.py:290` | lib.sh 소스 스니펫 가드 문자열 동기화 | ✅ |
|
||
|
||
**3중 기본값 동기화 확인**: 이 코드베이스는 동일한 기본값을 **세 곳**(shell 파라미터 확장, Python 시그니처, Python argparse)에 중복 보유합니다. 세 곳 모두 40으로 일치하며 `.mam.env.example` 문서값까지 4중 일치합니다. 드리프트 없음.
|
||
|
||
> **참고**: `lib.sh:432` 는 항상 `--min-cols` 를 **명시 전달**하므로 실운영 경로에서 `layout.py:201` 의 argparse 기본값은 도달하지 않습니다. 201번 줄은 CLI 직접 호출·테스트 경로용 fallback 입니다. 두 값이 어긋나도 즉시 드러나지 않는 구조이므로 §4에 가드 제안을 남깁니다.
|
||
|
||
---
|
||
|
||
## 2. 로직 정합성 — 신규 테스트 수동 트레이스
|
||
|
||
`compute_2xk_layout` 의 분기를 신규 단언에 그대로 대입해 전건 검증했습니다. 폭 판정은 `layout.py:169` 의 `width // 2 < min_cols` 단일 게이트입니다.
|
||
|
||
### 2.1 경계값 (80 / 79 cols)
|
||
|
||
| 입력 | 계산 | 도달 분기 | 기대 | 실제 |
|
||
|---|---|---|---|---|
|
||
| 2페인 × w=80 (x=0 동일열) | `80 // 2 = 40`, `40 < 40` = False | `:172 new_column_right` | `right`, not overflow | ✅ 일치 |
|
||
| 2페인 × w=79 | `79 // 2 = 39`, `39 < 40` = True | `:170 column_width_overflow` | `overflow` | ✅ 일치 |
|
||
|
||
**80이 정확한 하한**임이 확인됩니다(`>= 80` 에서 분할 가능). 브리프 2번 요구사항 *"width >= 80 에서 조기 overflow 금지"* 는 상수 변경만으로 산술적으로 충족되며, 별도 분기 추가가 불필요합니다 — **엔진 로직 무변경은 올바른 판단**입니다. 불필요한 특수 케이스를 넣지 않은 점을 긍정 평가합니다.
|
||
|
||
### 2.2 90 / 100 col 단일 워크스페이스 타일링 (1→2→3→4→overflow)
|
||
|
||
`total_w ∈ {90, 100}`, `half_w = total_w // 2 ∈ {45, 50}` 기준 전 단계 추적:
|
||
|
||
| 단계 | 입력 형상 | 판정 경로 | 결과 |
|
||
|---|---|---|---|
|
||
| 1→2 | 1페인 `w×40` | `:94` `40//2 = 20 >= min_rows 20` → False(제약 아님) → `:102` | `down` / `single_pane_split_down`, target `p1` ✅ |
|
||
| 2→3 | 2페인 x=0 단일열 | singleton 없음 → `:169` `45//2=22`? **아니오** — 이 시점 페인 폭은 아직 `total_w`(90/100) → `90//2=45 >= 40` | `right` / `new_column_right`, target `p1`(`columns[-1][0]`) ✅ |
|
||
| 3→4 | `[p1,p2]` @x=0, `[p3]` @x=half_w | `:148-154` singleton 열 `[p3]` 탐지 → 높이 `40//2=20 >= 20` | `down` / `fill_singleton_column`, target `p3` ✅ |
|
||
| 4→5 | 2열 × 2페인 완성 | singleton 없음, `max_columns=None` → `:169` `45//2=22 < 40` (100col: `50//2=25 < 40`) | `overflow` / `column_width_overflow` ✅ |
|
||
|
||
**핵심 확인 사항 2건**:
|
||
1. **1→2 단계의 높이 경계**: `height=40` 에서 `40 // 2 = 20`, `min_rows=20` 과 **같음**. `:94` 조건은 `< min_rows` 이므로 False → 정상적으로 `down` 진입. `<=` 였다면 오분기했을 지점으로, 테스트가 이 경계를 정확히 짚고 있습니다.
|
||
2. **4페인에서의 의도적 overflow**: `min_cols=40` 에서 90~100col 워크스페이스는 **최대 4에이전트**가 상한이며 5번째는 새 워크스페이스로 넘어갑니다. 브리프 목표(*"3-4 agents in ~100-col terminal"*)와 정확히 부합하고, 테스트가 이 상한을 명시적으로 고정하고 있어 향후 회귀 시 즉시 검출됩니다. ✅
|
||
|
||
### 2.3 컬럼 그룹핑 정합성
|
||
|
||
`:129-139` 의 x좌표 퍼지 그룹핑(임계 2col)에 신규 픽스처 대입 시:
|
||
- 90col: x ∈ {0, 45} → `|0-45| = 45 > 2` → 2개 열로 정확히 분리 ✅
|
||
- 100col: x ∈ {0, 50} → 동일 ✅
|
||
|
||
퍼지 임계값 2와 충돌하는 좌표가 없어 그룹핑 오분류 위험이 없습니다.
|
||
|
||
---
|
||
|
||
## 3. 회귀 영향 분석 (유실 관점)
|
||
|
||
기본값 변경은 **기본값에 의존하는 기존 테스트**에만 파급됩니다. 전수 조사 결과:
|
||
|
||
| 기존 테스트 | 기본값 의존 여부 | 영향 |
|
||
|---|---|---|
|
||
| `test_layout.py:28~262` (8건) | `min_cols=60` **명시 전달** | 영향 없음 ✅ |
|
||
| `test_layout.py:137,191` | `min_cols=30` 명시 | 영향 없음 ✅ |
|
||
| `test_layout.py:207` (subprocess) | `--min-cols 60` 명시 | 영향 없음 ✅ |
|
||
| `test_layout.py:358,374` | `--min-cols 30` 명시 | 영향 없음 ✅ |
|
||
| `test_j1_env_zero_min_cols_matches_flag_zero` | `_ZERO_TRAP` 기본값 실행 포함 | **영향 검토 필요 → 아래** |
|
||
| `test_j1b_invalid_alias_does_not_shadow...` | 기본값 실행 비교 | 동일 ✅ |
|
||
|
||
**J-1 계열 정밀 검토** (`test_layout.py:432` 주석 기준 `_ZERO_TRAP` = 단일 페인 `50×30`):
|
||
- `height // 2 = 15 < min_rows 20` → `:94` 제약 분기 진입
|
||
- `width // 2 = 25` 를 `min_cols` 와 비교: 기존 `25 < 60` → overflow / 신규 `25 < 40` → **overflow (동일)**
|
||
- 즉 기본값이 60이든 40이든 `_ZERO_TRAP` 의 결과는 `single_pane_overflow` 로 불변. **J-1/C-2 불변식 보존 확인** ✅
|
||
|
||
또한 J-1b는 "기본값 실행 == 기본값 실행" 형태의 자기참조 비교라 기본값 자체와 무관하게 성립합니다.
|
||
|
||
`test_layout.py:290` 의 lib.sh 소스 스니펫 가드는 **문자열 완전 일치** 검사이므로 `lib.sh:432` 와 함께 갱신되지 않았다면 즉시 실패했을 항목입니다. 양쪽 모두 `:-40` 으로 동기화되어 있음을 대조 확인했습니다 ✅
|
||
|
||
---
|
||
|
||
## 4. 지적 사항 (모두 비차단 / Non-blocking)
|
||
|
||
차단 결함(P0/P1)은 발견되지 않았습니다. 아래는 개선 권고입니다.
|
||
|
||
### 🟡 N-1 (P3) — `test_herdr_shim_contract.py:100` 의 `MAM_MIN_PANE_COLS=60` 미검토
|
||
`tests/test_herdr_shim_contract.py:92,100` 의 H-13 케이스가 `export MAM_MIN_PANE_COLS=60` 을 사용합니다. 이는 **환경변수 오버라이드 동작 자체**를 검증하는 케이스이므로 기본값 변경과 논리적으로 독립이며(명시 오버라이드 경로), 정상 통과가 예상됩니다. 다만 파일 본문을 열람하지 못해 **단언 내용까지는 확인하지 못했습니다.**
|
||
|
||
**개선 방향**: 이 테스트가 "60이 아닌 값이 적용됨"을 검증하는 의도라면, 이제 기본값 40과 오버라이드 값 60이 명확히 구분되어 오히려 대조가 선명해집니다. 확인만 권고합니다.
|
||
|
||
### 🟡 N-2 (P3) — `IMPROVEMENTS.md:49` 의 `min_cols=60` 잔존
|
||
```
|
||
IMPROVEMENTS.md:49: ... 해상도 오버플로 가드(`min_cols=60`, `min_rows=20`) ...
|
||
```
|
||
해당 줄은 **엔진 최초 도입 시점을 기록한 변경 이력**이므로 당시 값 60을 남기는 것이 이력 문서로서는 정확합니다. 다만 현재 이 저장소에서 **60을 기본값이라 서술하는 유일한 문서**가 되었습니다.
|
||
|
||
**개선 방향 (택1)**: (a) 그대로 두되 이번 변경을 `IMPROVEMENTS.md` 신규 항목으로 추가하여 60→40 전환 이력을 잇는다 — **권장**. (b) 해당 줄에 `(현행 40, 잡 ddc8d9f1에서 변경)` 각주를 붙인다. 이력 문서를 소급 수정하는 방식은 권장하지 않습니다.
|
||
|
||
### 🟡 N-3 (P3) — 기본값 4중 중복에 대한 파리티 가드 부재
|
||
동일 상수가 `lib.sh:432` / `layout.py:73` / `layout.py:201` / `.mam.env.example:133` 4곳에 문자열로 중복 존재합니다. `test_layout.py:290` 이 lib.sh↔테스트 스니펫 쌍만 고정할 뿐, **`layout.py:73` 시그니처 기본값과 `layout.py:201` argparse 기본값의 일치는 어떤 테스트도 강제하지 않습니다.** 두 값이 어긋나면 CLI 경로와 라이브러리 임포트 경로가 조용히 갈라집니다.
|
||
|
||
**개선 방향 (구체안)**:
|
||
```python
|
||
# tests/test_layout.py
|
||
import inspect
|
||
from lib_py.layout import compute_2xk_layout
|
||
|
||
def test_default_min_cols_parity_across_entrypoints():
|
||
"""시그니처 기본값 == argparse 기본값 == lib.sh fallback."""
|
||
sig_default = inspect.signature(compute_2xk_layout).parameters["min_cols"].default
|
||
assert sig_default == 40
|
||
# argparse 경로: env 미설정 시 동일 결정을 내야 함
|
||
assert _run_layout(_ZERO_TRAP) == _run_layout(_ZERO_TRAP, ("--min-cols", str(sig_default)))
|
||
# lib.sh fallback 문자열
|
||
lib_sh = (REPO_ROOT / ".agents/skills/lib.sh").read_text()
|
||
assert f'${{MAM_MIN_PANE_COLS:-{sig_default}}}' in lib_sh
|
||
```
|
||
이는 이전 잡에서 `ready_tokens` 가 `lib.sh`/`claude.py` 양쪽에 중복된 것과 **동일 유형의 구조적 취약점**이며, 같은 처방이 적용됩니다. 별도 잡으로 분리해도 무방합니다.
|
||
|
||
### 🟢 N-4 (P4) — 워킹트리 위생
|
||
`git status` 에 `m nats-docker` (서브모듈 dirty, `5db38da...-dirty`) 가 포함되어 있습니다. 본 변경과 무관한 오염이며 커밋 전 정리를 권고합니다. 또한 `tests/test_layout.py` 말미에 빈 줄 3개(`+++`)가 추가되어 있어 PEP8 관점의 사소한 정리 여지가 있습니다. 기능 영향 없음.
|
||
|
||
### ℹ️ N-5 (정보) — 누적 diff 내 `test_a4_adapter_contract.py` 변경
|
||
`ready_tokens` 에 `Claude Code|Opus|Sonnet|Haiku` 를 추가한 직전 잡의 변경분이 누적 diff에 포함되어 있습니다. 계약 테스트의 기대값이 `lib.sh` / `claude.py` 양쪽 구현과 3자 일치함을 대조 확인했습니다 ✅ (본 잡 범위 외)
|
||
|
||
---
|
||
|
||
## 5. 잔여 검증 항목 (Outstanding)
|
||
|
||
| # | 항목 | 상태 |
|
||
|---|---|---|
|
||
| V-1 | `pytest tests/ -q` 전체 통과 | ❌ **미수행** — 본 세션에서 실행 중단됨 |
|
||
| V-2 | `test_herdr_shim_contract.py` H-13 단언 내용 | ⚠️ 미열람 (영향 없음으로 추정, N-1) |
|
||
|
||
**V-1은 머지 전 반드시 실측되어야 합니다.** 정적 분석상 실패를 유발할 요인은 발견하지 못했으나(§3 회귀 영향 전무), 이는 예측이지 관측이 아닙니다.
|
||
|
||
---
|
||
|
||
## 6. 총평
|
||
|
||
변경은 **상수 1개의 값 조정과 그에 대한 4중 동기화**라는 최소 표면적을 정확히 지켰습니다. 엔진 분기 로직을 건드리지 않고 브리프의 4개 요구사항을 충족한 점, 특히 요구사항 2를 위해 불필요한 특수 분기를 추가하지 않고 산술로 해소한 점이 설계적으로 건전합니다.
|
||
|
||
신규 테스트는 단순 happy-path에 머물지 않고 **80/79 경계**, **height 40//2 == min_rows 20 동등 경계**, **4페인 상한 후 overflow** 라는 세 개의 실질적 경계를 고정합니다. 기존 J-1/C-2 불변식도 보존됩니다.
|
||
|
||
지적 사항 4건은 모두 P3 이하이며 문서 이력·테스트 위생·워킹트리 정리 범주로, 어느 것도 현재 동작을 해치거나 결함을 은폐하지 않습니다. 설계 변경이나 재계획이 필요한 사안은 없습니다.
|
||
|
||
**단, 본 PASS는 §5 V-1(전체 테스트 실행) 이 별도로 확인된다는 전제 위에 성립합니다.** 정적 검토 범위에서는 차단 사유가 없습니다.
|
||
|
||
[VERDICT: PASS]
|