Files
multi-agent-mux/.agents/reports/canary-projects-multi-agent-mux-creator-claude/report-55d1a1d9.md
T
Godopu 14e306be46 feat(layout,test): resolve backlog items I-2, I-3, and C-1 with unanimous peer review
- I-2: add 5.0s upper-bound execution time assertion in test_bug4_headless_unobservable_fast_path to contractually guard SKS_EMPTY_GIVEUP early-exit latency
- I-3: clean up PaneInfo.focused, wire MAM_MAX_PANE_COLS/MAM_MAX_COLS environment variables and CLI --max-cols flag
- C-1: apply max_columns growth guard to headless 0x0 layouts, mirroring GUI behavior
- Promoted Planner consensus plan Rev.2 (plan-fea5f1b2.md) and unanimous PASS reports from Reviewers Claude (report-55d1a1d9.md) and Cline (report-6f18ba0f.md)
- Verified all 333 test cases pass with exit code 0
2026-08-23 21:59:03 +09:00

13 KiB
Raw Blame History

🔍 리뷰 리포트: 백로그 I-2 / I-3 + C-1 구현 (Job 55d1a1d9)

  • 작성일: 2026-08-23
  • 역할: Reviewer (claude)
  • 대상 계획: 5e4ef463 (Rev.2 — I-2 / I-3 / C-1)
  • 리뷰 대상: HEAD 31b2d70 위의 미커밋 작업 트리 변경 5파일 (+160 / 26)
  • 테스트: 333 passed in 428.43s (exit 0), 333 collected

0. 종합 판정

계획 항목 상태 검증
I-2 헤드리스 지연 상한 단언 🟢 해결 (뮤테이션 2종 검증) 조기 return 2 제거 · SKS_EMPTY_GIVEUP 3→20 양쪽에서 FAIL. 정상 소요 1.18 s
I-3a PaneInfo.focused 제거 🟢 해결 .focused 판독 0건, extract_panes_and_focus 잔존 0건, Tuple import 정리됨
I-3b --max-cols env 배선 🟢 해결 (뮤테이션 2종 검증) argparse 인자 제거 · env 기본값 None 복귀 양쪽에서 FAIL. lib.sh 무변경 확인
I-3c 앵커/no_panes_default 주석 🟢 해결 근거 주석(추론 vs 측정, 성장 가드) 반영
C-1 헤드리스 max_columns 우회 교정 🟢 해결 (뮤테이션 검증) 체크 제거 시 FAIL. GUI/헤드리스가 4페인·max=2 에서 동일하게 max_columns_reached
문서 .mam.env.example 3종 + IMPROVEMENTS.md 🟢 해결 템플릿 규약 준수, 배포 신선도 31/31 유지

[VERDICT: PASS]

계획의 모든 요구가 구현되었고 뮤테이션 7종이 지정 테스트를 FAIL 시킵니다. 남은 두 항목은 P3 수준이며, 그중 하나는 제 계획의 뮤테이션 명세 오류입니다(§3.2). 차단하지 않고 J-1 / J-2 로 이월합니다.


1. 검증 기반 (Measurement Ledger)

# 검증 방법 결과
M-1 전체 회귀 pytest tests/ -q 333 passed (428 s), exit 0
M-2 수집 수 --collect-only 333 — 계획 예상치와 일치 (330 → 333)
M-3 대상 파일 test_layout.py + test_b19_*.py 25 passed (19 + 6)
M-4 I-2 정상 소요 --durations headless 1.18 s (상한 5.0 s)
M-5 컴파일 / 인터프리터 py_compile, /usr/bin/python3 (3.9.6) 양호 / right P9
M-6 배포 신선도 pytest tests/test_deploy_freshness.py -q 31 passed.mam.env.example 추가가 D-7/D-21/D-32 를 깨지 않음
M-7 죽은 표면 제거 grep -rn "\.focused\b" .agents/skills/ / extract_panes_and_focus in tests/ 0 / 0
M-8 GUI ↔ 헤드리스 대칭 4페인 · max_columns=2 양 모드 둘 다 overflow / max_columns_reached
M-9 뮤테이션 M1 조기 return 2 제거 격리 복제본 🟢 test_bug4_headless_unobservable_fast_path FAIL
M-10 뮤테이션 M2 SKS_EMPTY_GIVEUP 3→20 🟢 동 테스트 FAIL
M-11 뮤테이션 M3 --max-cols argparse 인자 삭제 🟢 CLI·env 테스트 FAIL
M-12 뮤테이션 M4 --max-cols 기본값 None 복귀 🟢 test_env_max_cols_applies_without_flag FAIL
M-13 뮤테이션 M5 헤드리스 max_columns 체크 제거 🟢 test_headless_max_columns_growth_guard FAIL
M-14 뮤테이션 M6 홀수 분기에도 상한 검사(과잉 교정) 🔴 19 passed — 가드가 검출 못 함 (§3.2)
M-15 뮤테이션 M7 GUI max_columns 분기 삭제 🟢 3건 FAIL
M-16 MAM_MIN_PANE_COLS=0 거동 env vs 플래그 대조 🔴 env 0overflow / 플래그 --min-cols 0down (§3.1)
M-17 M6 판별 조건 분석 헤드리스 n=3/5/7 × max=2 정상 코드 전부 down. 과잉 교정 시 n=5,7overflow — 테스트의 n=3 은 임계 미달

2. 구현 확인 상세

2.1 I-2 — 시간 단언이 실제로 계약이 되었다

import time 추가, SKS_* 3종을 pin 이 아니라 제거(계획 요구대로), subprocess.run 만 감싼 측정, 원인 가설을 담은 실패 메시지까지 사양대로 구현되었습니다.

두 방향의 뮤테이션에서 모두 FAIL 합니다.

뮤테이션 결과
조기 return 2 제거 test_bug4_headless_unobservable_fast_path FAIL (13.7 s 소요)
SKS_EMPTY_GIVEUP:-3:-20 FAIL (13.5 s)

두 번째가 특히 값어치 있습니다 — 코드 구조는 그대로 두고 상수만 바꿔도 잡힙니다. 정상 경로는 1.18 s 로 상한 5.0 s 대비 4배 여유가 유지됩니다.

2.2 I-3a — 죽은 표면이 실제로 사라졌다

PaneInfo.focused 필드, focused_id 반환, layout.get("focused_pane_id") 조회가 모두 제거되고 extract_panes_and_focusextract_panes 로 개명, 호출부 1곳과 tests/test_layout.py 의 미사용 import 2개가 함께 정리되었습니다. Tuple import 도 제거되어 잔재가 없습니다(M-7).

제거 이유를 dataclass 자리에 주석으로 남긴 것도 적절합니다 — 다음 사람이 "왜 focused 가 없지"를 되묻지 않게 합니다.

2.3 I-3b / C-1 — 배선과 교정이 같은 커밋에 함께 들어갔다

계획이 분리 불가로 못박은 부분입니다. MAM_MAX_PANE_COLS opt-in 경로를 살리는 변경과, 그 경로의 헤드리스 정합성을 보장하는 C-1 교정이 한 커밋에 있습니다. 결과적으로 이 변경은 결함을 활성화하지 않습니다.

GUI 와 헤드리스가 같은 시점에 같은 사유로 overflow 합니다(M-8).

GUI       -> overflow  overflow=True  reason=max_columns_reached
HEADLESS  -> overflow  overflow=True  reason=max_columns_reached

lib.sh 는 한 글자도 바뀌지 않았습니다 — bash 3.2 빈 배열 함정을 피하려던 계획의 의도가 그대로 지켜졌습니다.

C-1 주석도 계획이 요구한 두 근거(추론 vs 측정 / 성장 가드)를 모두 담고 있습니다.

2.4 문서

.mam.env.example 3종이 기존 템플릿 규약(#default: + 주석 처리된 대입)을 따르고, MAM_MAX_PANE_COLS 설명에 "Applies to both measured (GUI) and headless 0x0 layouts" 를 명기해 C-1 의 결과를 운영자에게 전달합니다. IMPROVEMENTS.md 는 B-20 항목에 후속 정리를 1–2줄로 추가하고 C-1 을 별도 문장으로 기록했습니다(계획 Q-3 의 처방대로).


3. 잔여 지적 (비차단)

🟡 J-1 (P3) — or 60 관용구가 MAM_MIN_PANE_COLS=0 을 삼킨다

parser.add_argument("--min-cols", type=int, default=_env_int("MAM_MIN_COLS", "MAM_MIN_PANE_COLS") or 60)
parser.add_argument("--min-rows", type=int, default=_env_int("MAM_MIN_ROWS", "MAM_MIN_PANE_ROWS") or 20)

_env_int0 을 반환하면 falsy 이므로 or 60 이 발동해 60 으로 덮어씁니다. 같은 값을 플래그로 주면 0 이 그대로 쓰입니다.

MAM_MIN_PANE_COLS=0 MAM_MIN_PANE_ROWS=0  ->  {"direction": "overflow", "reason": "single_pane_overflow"}
--min-cols 0 --min-rows 0                ->  {"direction": "down",     "reason": "single_pane_split_down"}

_env_int('MAM_MIN_PANE_COLS')0 을 정확히 반환하며, or 60 단계에서만 60 이 됩니다(M-16). 즉 동일한 설정을 표현하는 두 경로가 갈라집니다.

0 은 "폭 하한 없음" 을 뜻하는 자연스러운 표현이고, 이번 커밋 이전의 int(os.environ.get(..., 60)) 은 이를 올바르게 처리했습니다. 계획은 --min-cols/--min-rows 의 헬퍼 통일을 권고(비필수) 로만 적었으므로 이 코드는 선택적 확장이었고, 확장 과정에서 falsy-zero 함정이 들어왔습니다.

처방_env_int 에 기본값 인자를 주어 or 를 없앱니다.

def _env_int(*names: str, default: Optional[int] = None) -> Optional[int]:
    for n in names:
        raw = os.environ.get(n, "").strip()
        if raw:
            try:
                return int(raw)
            except ValueError:
                return default
    return default

parser.add_argument("--min-cols", type=int, default=_env_int("MAM_MIN_COLS", "MAM_MIN_PANE_COLS", default=60))
parser.add_argument("--min-rows", type=int, default=_env_int("MAM_MIN_ROWS", "MAM_MIN_PANE_ROWS", default=20))
parser.add_argument("--max-cols", type=int, default=_env_int("MAM_MAX_COLS", "MAM_MAX_PANE_COLS"))

--max-cols 는 영향이 없습니다 — or 를 쓰지 않았고, 0if max_columns and … 에서 falsy 가 되어 "상한 없음" 으로 읽히는 것은 의도에 부합합니다.

권고 가드: MAM_MIN_PANE_COLS=0--min-cols 0 이 같은 결정을 내는지 단언하는 테스트 1건.

🟡 J-2 (P3, 계획 측 오류) — 성장 가드 테스트의 d3 케이스가 임계에 못 미친다

계획 §5 는 뮤테이션 M6(홀수 분기에도 상한 검사 추가 = 과잉 교정)이 test_headless_max_columns_growth_guardd3 단언에서 FAIL 할 것으로 적었습니다. 실제로는 19/19 통과합니다(M-14).

원인은 테스트 데이터에 있습니다. d3headless(3), max_columns=2 인데 n // 2 = 1 이라 1 >= 2 가 거짓이므로, 과잉 교정을 넣어도 그 분기에 도달하지 않습니다.

n (max_columns=2) n // 2 정상 코드 과잉 교정 시
3 1 down down판별 불가
5 2 down overflow
7 3 down overflow

"기존 열을 채우는 것은 막지 않는다" 는 계약 — C-1 처방이 GUI 와 대칭임을 보장하는 바로 그 성질 — 이 현재 아무 단언에도 걸려 있지 않습니다.

이 오류의 출처는 구현이 아니라 계획입니다. Creator 는 계획이 지정한 테스트를 그대로 구현했고, 임계값을 넘지 않는 데이터를 고른 것은 제 쪽입니다.

처방 — 한 줄 추가.

    # Filling an existing column is not blocked even at/above the cap
    # (n=5 -> n//2=2 >= max_columns=2, so this case actually reaches the check)
    d5 = compute_2xk_layout(headless(5), max_columns=2)
    assert d5.direction == "down" and not d5.is_overflow

수용 기준: 홀수 분기에 상한 검사를 넣는 뮤테이션에서 FAIL 해야 합니다.

🟢 참고 (조치 불요)

test_bug4_headless_unobservable_fast_path 의 목에서 paste-buffer 분기의 return 0 이 삭제되었습니다. 바로 아래 return 0 으로 떨어지므로 동작은 같습니다. 계획에 없던 변경이지만 무해합니다.


4. 규약 준수 확인

항목 확인
역할 분리 (MULTI_AGENT_RULES.md §1) Planner 계획 → Creator 구현 → Reviewer 검증 절차 준수
반박 절차 (§3.1) [REBUT:] 제기 없음
민감정보 미포함 (§2) diff 에 자격증명·절대 시스템 경로 하드코딩 없음
회귀 가드 실효성 뮤테이션 7종 FAIL / 1종 미검출(J-2, 계획 측 오류) ⚠️
계획 DoD 1·3·4·5·6·7 333 passed / 컴파일 / 배포 31 / 죽은 표면 0 / GUI·헤드리스 대칭 / 의도한 5파일만 변경
계획 DoD 2 (뮤테이션 8종) 7/8 — M6 은 계획 명세 자체가 판별 불가였음 ⚠️

5. 이월 항목

# 파일 조치
J-1 .agents/skills/lib_py/layout.py _env_intdefault= 인자 도입, or 60 / or 20 제거. MAM_MIN_PANE_COLS=0--min-cols 0 동치 가드 1건
J-2 tests/test_layout.py test_headless_max_columns_growth_guardheadless(5) 케이스 추가. 수용 기준: 홀수 분기 상한 검사 뮤테이션에서 FAIL
I-3 잔여 layout.py (기존 이월) PaneInfo 관련 정리는 완료. 추가 잔여 없음

6. 결론

계획 5e4ef463 의 여섯 항목(I-2, I-3a/b/c, C-1, 문서)이 모두 구현되었고, 사양에서 벗어난 곳이 없습니다. 특히 C-1 교정과 --max-cols 배선이 같은 커밋에 들어가 결함 활성화를 피했고, GUI 와 헤드리스가 4페인·max_columns=2 에서 동일하게 max_columns_reached 를 내는 것을 실측했습니다. 뮤테이션 7종이 각각 지정 테스트를 FAIL 시키며, 그중 SKS_EMPTY_GIVEUP 상수만 바꾸는 뮤테이션까지 잡히는 것은 I-2 가드가 구조가 아니라 동작을 붙들고 있음을 보여줍니다. 전체 333/333 통과, 배포 신선도 31/31 유지, 시스템 python 3.9.6 호환 확인.

남은 두 항목은 모두 P3 입니다. J-1(MAM_MIN_PANE_COLS=0 이 60 으로 덮임)은 문서화되지 않은 경계값에서만 나타나는 좁은 회귀이고, J-2(성장 가드의 판별 불가 케이스)는 제 계획의 뮤테이션 명세 오류로서 구현 책임이 아닙니다. 어느 쪽도 현재 동작을 해치지 않고 결함을 가리지도 않으므로 차단하지 않으며, 각각 한 줄 수정으로 다음 커밋에 정리하면 충분합니다.

[VERDICT: PASS]